← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 19:05:14 2015
Reported on Fri Jul 31 19:08:09 2015

Filename/var/www/foswiki11/lib/Monitor.pm
StatementsExecuted 26 statements in 1.99ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
77229µs29µsMonitor::::__ANON__[:101]Monitor::__ANON__[:101]
11120µs42µsMonitor::::BEGIN@39Monitor::BEGIN@39
11119µs38µsMonitor::::BEGIN@188Monitor::BEGIN@188
11117µs17µsMonitor::::BEGIN@90Monitor::BEGIN@90
11116µs42µsMonitor::::BEGIN@207Monitor::BEGIN@207
11114µs22µsMonitor::::BEGIN@40Monitor::BEGIN@40
11111µs27µsMonitor::::BEGIN@208Monitor::BEGIN@208
1116µs6µsMonitor::::ENDMonitor::END
0000s0sMonitor::::__ANON__[:102]Monitor::__ANON__[:102]
0000s0sMonitor::::__ANON__[:231]Monitor::__ANON__[:231]
0000s0sMonitor::::_get_stat_infoMonitor::_get_stat_info
0000s0sMonitor::::_markMonitor::_mark
0000s0sMonitor::::_monitorMethodMonitor::_monitorMethod
0000s0sMonitor::::tidytimeMonitor::tidytime
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5Monitoring package. Instrument the code like this:
6
7use Monitor ();
8Monitor::MARK("Description of event");
9Monitor::MARK("Another event");
10
11or, to monitor all the calls to a module
12
13use Monitor ();
14Monitor::MonitorMethod('Foswiki::Users');
15
16or a function
17
18use Monitor ();
19Monitor::MonitorMethod('Foswiki::Users', 'getCanonicalUserID');
20
21Then set the environment variable FOSWIKI_MONITOR to a perl true value, and
22run the script from the command line e.g:
23$ cd bin
24$ ./view -topic Myweb/MyTestTopic
25
26The results will be printed to STDERR at the end of the run. Two times are
27shown, a time relative to the last MARK and a time relative to the first MARK
28(which is always set the first time this package is used). The final column
29is total memory.
30
31NOTE: it uses /proc - so its linux specific...
32
33TODO: replace FOSWIKI_MONITOR with LocalSite.cfg setting that can turn on per module instrumentation.
34
35=cut
36
37package Monitor;
38
39243µs264µs
# spent 42µs (20+22) within Monitor::BEGIN@39 which was called: # once (20µs+22µs) by Foswiki::BEGIN@48 at line 39
use strict;
# spent 42µs making 1 call to Monitor::BEGIN@39 # spent 22µs making 1 call to strict::import
402569µs231µs
# spent 22µs (14+9) within Monitor::BEGIN@40 which was called: # once (14µs+9µs) by Foswiki::BEGIN@48 at line 40
use warnings;
# spent 22µs making 1 call to Monitor::BEGIN@40 # spent 9µs making 1 call to warnings::import
41
421500nsour @times;
431200nsour @methodStats;
441100nsour $show_percent;
45
46sub _get_stat_info {
47
48 # open and read the main stat file
49 my $_INFO;
50 if ( !open( $_INFO, '<', "/proc/$_[0]/stat" ) ) {
51
52 # Failed
53 return { vsize => 0, rss => 0 };
54 }
55 my @info = split( /\s+/, <$_INFO> );
56 close($_INFO);
57
58 # these are all the props (skip some)
59 # pid(0) comm(1) state ppid pgrp session tty
60 # tpgid(7) flags minflt cminflt majflt cmajflt
61 # utime(13) stime cutime cstime counter
62 # priority(18) timeout itrealvalue starttime vsize rss
63 # rlim(24) startcode endcode startstack kstkesp kstkeip
64 # signal(30) blocked sigignore sigcatch wchan
65
66 # get the important ones
67 return {
68 vsize => $info[22],
69 rss => $info[23] * 4
70 };
71}
72
73sub _mark {
74 my $event = shift;
75 push( @times, [ $event, new Benchmark(), _get_stat_info($$) ] );
76}
77
78sub tidytime {
79 my ( $a, $b ) = @_;
80 my $s = timestr( timediff( $a, $b ) );
81 $s =~ /([\d.]+) wallclock secs.*([\d.]+) CPU/;
82 my ( $w, $c ) = ( $1, $2 );
83 if ( defined $show_percent ) {
84 $w = $w * 100.0 / $show_percent;
85 return "$w%";
86 }
87 return "wall $w CPU $c";
88}
89
90
# spent 17µs within Monitor::BEGIN@90 which was called: # once (17µs+0s) by Foswiki::BEGIN@48 at line 104
BEGIN {
9113µs my $caller = caller;
92113µs if ( $ENV{FOSWIKI_MONITOR} ) {
93 require Benchmark;
94 import Benchmark ':hireswallclock';
95 die $@ if $@;
96 *MARK = \&_mark;
97 *MonitorMethod = \&_monitorMethod;
98 MARK('START');
99 }
100 else {
101836µs
# spent 29µs within Monitor::__ANON__[/var/www/foswiki11/lib/Monitor.pm:101] which was called 7 times, avg 4µs/call: # once (8µs+0s) by Foswiki::UI::View::view at line 359 of /var/www/foswiki11/lib/Foswiki/UI/View.pm # once (6µs+0s) by Foswiki::UI::View::view at line 400 of /var/www/foswiki11/lib/Foswiki/UI/View.pm # once (5µs+0s) by Foswiki::new at line 1978 of /var/www/foswiki11/lib/Foswiki.pm # once (4µs+0s) by Foswiki::UI::View::view at line 396 of /var/www/foswiki11/lib/Foswiki/UI/View.pm # once (2µs+0s) by Foswiki::UI::View::view at line 374 of /var/www/foswiki11/lib/Foswiki/UI/View.pm # once (2µs+0s) by Foswiki::UI::View::view at line 406 of /var/www/foswiki11/lib/Foswiki/UI/View.pm # once (2µs+0s) by Foswiki::new at line 1656 of /var/www/foswiki11/lib/Foswiki.pm
*MARK = sub { };
10212µs *MonitorMethod = sub { };
103 }
1041770µs117µs}
# spent 17µs making 1 call to Monitor::BEGIN@90
105
106
# spent 6µs within Monitor::END which was called: # once (6µs+0s) by main::RUNTIME at line 0 of view
sub END {
10716µs return unless ( $ENV{FOSWIKI_MONITOR} );
108 MARK('END');
109 my $lastbm;
110 my $firstbm;
111 my %mash;
112
113 if ( scalar(@times) > 1 ) {
114 my $ibm = timestr( timediff( $times[$#times]->[1], $times[0]->[1] ) );
115 if ( $ibm =~ /([\d.]+) wallclock/ ) {
116 $show_percent = $1;
117 }
118 print STDERR "\n\n| Event | Delta | Abs | Mem |";
119 foreach my $bm (@times) {
120 $firstbm = $bm unless $firstbm;
121 if ($lastbm) {
122 my $s = tidytime( $bm->[1], $lastbm->[1] );
123 my $t = tidytime( $bm->[1], $firstbm->[1] );
124 $s = "\n| $bm->[0] | $s | $t | $bm->[2]->{vsize} |";
125 print STDERR $s;
126 }
127 $lastbm = $bm;
128 }
129 print STDERR "\nTotal time: $ibm";
130 }
131
132 my %methods;
133 foreach my $call (@methodStats) {
134 $methods{ $call->{method} } = {
135 count => 0,
136 min => 99999999,
137 max => 0,
138 mem_min => 99999999,
139 mem_max => 0
140 }
141 unless defined( $methods{ $call->{method} } );
142 $methods{ $call->{method} }{count} += 1;
143 my $diff = timediff( $call->{out}, $call->{in} );
144
145 $methods{ $call->{method} }{min} = ${$diff}[0]
146 if ( $methods{ $call->{method} }{min} > ${$diff}[0] );
147 $methods{ $call->{method} }{max} = ${$diff}[0]
148 if ( $methods{ $call->{method} }{max} < ${$diff}[0] );
149 if ( defined( $methods{ $call->{method} }{total} ) ) {
150 $methods{ $call->{method} }{total} =
151 Benchmark::timesum( $methods{ $call->{method} }{total}, $diff );
152 }
153 else {
154 $methods{ $call->{method} }{total} = $diff;
155 }
156 my $memdiff = $call->{out_stat}{rss} - $call->{in_stat}{rss};
157 $methods{ $call->{method} }{mem_min} = $memdiff
158 if ( $methods{ $call->{method} }{mem_min} > $memdiff );
159 $methods{ $call->{method} }{mem_max} = $memdiff
160 if ( $methods{ $call->{method} }{mem_max} < $memdiff );
161 }
162 print STDERR
163"\n\n| Count | Time (Min/Max) | Memory(Min/Max) | Total | Method |";
164 foreach my $method ( sort keys %methods ) {
165 print STDERR "\n| "
166 . sprintf( '%6u', $methods{$method}{count} ) . ' | '
167 . sprintf( '%6.3f / %6.3f',
168 $methods{$method}{min},
169 $methods{$method}{max} )
170 . ' | '
171 . sprintf( '%6u / %6u',
172 $methods{$method}{mem_min},
173 $methods{$method}{mem_max} )
174 . ' | '
175 . timestr( $methods{$method}{total} )
176 . " | $method |";
177 }
178 print STDERR "\n";
179}
180
181#BEWARE - though this is extremely useful to show whats fast / slow in a Class, its also a potentially
182#deadly hack
183#method wrapper - http://chainsawblues.vox.com/library/posts/page/1/
184sub _monitorMethod {
185 my ( $package, $method ) = @_;
186
187 if ( !defined($method) ) {
1882229µs258µs
# spent 38µs (19+20) within Monitor::BEGIN@188 which was called: # once (19µs+20µs) by Foswiki::BEGIN@48 at line 188
no strict "refs";
# spent 38µs making 1 call to Monitor::BEGIN@188 # spent 20µs making 1 call to strict::unimport
189 foreach my $symname ( sort keys %{"${package}::"} ) {
190 next if ( $symname =~ /^ASSERT/ );
191 next if ( $symname =~ /^DEBUG/ );
192 next if ( $symname =~ /^UNTAINTED/ );
193 next if ( $symname =~ /^except/ );
194 next if ( $symname =~ /^otherwise/ );
195 next if ( $symname =~ /^finally/ );
196 next if ( $symname =~ /^try/ );
197 next if ( $symname =~ /^with/ );
198 _monitorMethod( $package, $symname );
199 }
200 }
201 else {
202 my $old = ($package)->can($method); # look up along MRO
203 return if ( !defined($old) );
204
205 #print STDERR "monitoring $package :: $method)";
206 {
207246µs267µs
# spent 42µs (16+25) within Monitor::BEGIN@207 which was called: # once (16µs+25µs) by Foswiki::BEGIN@48 at line 207
no warnings 'redefine';
# spent 42µs making 1 call to Monitor::BEGIN@207 # spent 25µs making 1 call to warnings::unimport
2082266µs243µs
# spent 27µs (11+16) within Monitor::BEGIN@208 which was called: # once (11µs+16µs) by Foswiki::BEGIN@48 at line 208
no strict "refs";
# spent 27µs making 1 call to Monitor::BEGIN@208 # spent 16µs making 1 call to strict::unimport
209 *{"${package}::$method"} = sub {
210
211 #Monitor::MARK("begin $package $method");
212 my $in_stat = _get_stat_info($$);
213 my $in_bench = new Benchmark();
214 my $self = shift;
215 my @result = $self->$old(@_);
216 my $out_bench = new Benchmark();
217
218 #Monitor::MARK("end $package $method => ".($result||'undef'));
219 my $out_stat = _get_stat_info($$);
220 push(
221 @methodStats,
222 {
223 method => "${package}::$method",
224 in => $in_bench,
225 in_stat => $in_stat,
226 out => $out_bench,
227 out_stat => $out_stat
228 }
229 );
230 return wantarray ? @result : $result[0];
231 }
232 }
233 }
234}
235
23615µs1;
237__END__