← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:26:38 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Monitor.pm
StatementsExecuted 38 statements in 3.05ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
77262µs62µsMonitor::::__ANON__[:119]Monitor::__ANON__[:119]
11123µs31µsMonitor::::BEGIN@40Monitor::BEGIN@40
11121µs34µsMonitor::::BEGIN@103Monitor::BEGIN@103
11121µs21µsMonitor::::BEGIN@113Monitor::BEGIN@113
11118µs49µsMonitor::::BEGIN@280Monitor::BEGIN@280
11117µs50µsMonitor::::BEGIN@97Monitor::BEGIN@97
11117µs49µsMonitor::::BEGIN@231Monitor::BEGIN@231
11116µs36µsMonitor::::BEGIN@41Monitor::BEGIN@41
11116µs47µsMonitor::::BEGIN@212Monitor::BEGIN@212
11116µs47µsMonitor::::BEGIN@98Monitor::BEGIN@98
11116µs22µsMonitor::::BEGIN@104Monitor::BEGIN@104
11115µs48µsMonitor::::BEGIN@281Monitor::BEGIN@281
11115µs43µsMonitor::::BEGIN@232Monitor::BEGIN@232
1119µs9µsMonitor::::ENDMonitor::END
0000s0sMonitor::::__ANON__[:120]Monitor::__ANON__[:120]
0000s0sMonitor::::__ANON__[:255]Monitor::__ANON__[:255]
0000s0sMonitor::::__ANON__[:322]Monitor::__ANON__[:322]
0000s0sMonitor::::_get_stat_infoMonitor::_get_stat_info
0000s0sMonitor::::_markMonitor::_mark
0000s0sMonitor::::_monitorMethodMonitor::_monitorMethod
0000s0sMonitor::::getRunTimeSoFarMonitor::getRunTimeSoFar
0000s0sMonitor::::monitorMACROMonitor::monitorMACRO
0000s0sMonitor::::startMonitoringMonitor::startMonitoring
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.
34TODO: rewrite to use Foswiki::Loggers
35
36=cut
37
38package Monitor;
39
40244µs238µs
# spent 31µs (23+8) within Monitor::BEGIN@40 which was called: # once (23µs+8µs) by Foswiki::BEGIN@48 at line 40
use strict;
# spent 31µs making 1 call to Monitor::BEGIN@40 # spent 8µs making 1 call to strict::import
412489µs256µs
# spent 36µs (16+20) within Monitor::BEGIN@41 which was called: # once (16µs+20µs) by Foswiki::BEGIN@48 at line 41
use warnings;
# spent 36µs making 1 call to Monitor::BEGIN@41 # spent 20µs making 1 call to warnings::import
42
4312µsour @times;
4411µsour @methodStats;
4511µsour $show_percent;
46
47sub _get_stat_info {
48
49 # open and read the main stat file
50 my $_INFO;
51 if ( !open( $_INFO, '<', "/proc/$_[0]/stat" ) ) {
52
53 # Failed
54 return { vsize => 0, rss => 0 };
55 }
56 my @info = split( /\s+/, <$_INFO> );
57 close($_INFO);
58
59 # these are all the props (skip some)
60 # pid(0) comm(1) state ppid pgrp session tty
61 # tpgid(7) flags minflt cminflt majflt cmajflt
62 # utime(13) stime cutime cstime counter
63 # priority(18) timeout itrealvalue starttime vsize rss
64 # rlim(24) startcode endcode startstack kstkesp kstkeip
65 # signal(30) blocked sigignore sigcatch wchan
66
67 # get the important ones
68 return {
69 vsize => $info[22],
70 rss => $info[23] * 4
71 };
72}
73
74sub _mark {
75 my $event = shift;
76 push( @times, [ $event, new Benchmark(), _get_stat_info($$) ] );
77}
78
79sub tidytime {
80 my ( $a, $b ) = @_;
81 my $s = timestr( timediff( $a, $b ) );
82 $s =~ /([\d.]+) wallclock secs.*([\d.]+) CPU/;
83 my ( $w, $c ) = ( $1, $2 );
84 if ( defined $show_percent ) {
85 $w = $w * 100.0 / $show_percent;
86 return "$w%";
87 }
88 return "wall $w CPU $c";
89}
90
91sub startMonitoring {
92 require Benchmark;
93 import Benchmark ':hireswallclock';
94 die $@ if $@;
95
96 {
97246µs283µs
# spent 50µs (17+33) within Monitor::BEGIN@97 which was called: # once (17µs+33µs) by Foswiki::BEGIN@48 at line 97
no warnings 'redefine';
# spent 50µs making 1 call to Monitor::BEGIN@97 # spent 33µs making 1 call to warnings::unimport
98275µs279µs
# spent 47µs (16+31) within Monitor::BEGIN@98 which was called: # once (16µs+31µs) by Foswiki::BEGIN@48 at line 98
no strict "refs";
# spent 47µs making 1 call to Monitor::BEGIN@98 # spent 31µs making 1 call to strict::unimport
99
100 *MARK = \&_mark;
101 *MonitorMethod = \&_monitorMethod;
102
103244µs246µs
# spent 34µs (21+12) within Monitor::BEGIN@103 which was called: # once (21µs+12µs) by Foswiki::BEGIN@48 at line 103
use warnings;
# spent 34µs making 1 call to Monitor::BEGIN@103 # spent 12µs making 1 call to warnings::import
1042186µs228µs
# spent 22µs (16+6) within Monitor::BEGIN@104 which was called: # once (16µs+6µs) by Foswiki::BEGIN@48 at line 104
use strict;
# spent 22µs making 1 call to Monitor::BEGIN@104 # spent 6µs making 1 call to strict::import
105
106 #reset the loged time
107 @times = ();
108 @methodStats = ();
109 }
110 MARK('START');
111}
112
113
# spent 21µs within Monitor::BEGIN@113 which was called: # once (21µs+0s) by Foswiki::BEGIN@48 at line 122
BEGIN {
114212µs my $caller = caller;
11529µs if ( $ENV{FOSWIKI_MONITOR} ) {
116 startMonitoring();
117 }
118 else {
119772µs
# spent 62µs within Monitor::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Monitor.pm:119] which was called 7 times, avg 9µs/call: # once (12µs+0s) by Foswiki::new at line 1898 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm # once (11µs+0s) by Foswiki::UI::View::view at line 356 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm # once (10µs+0s) by Foswiki::UI::View::view at line 397 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm # once (10µs+0s) by Foswiki::UI::View::view at line 393 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm # once (10µs+0s) by Foswiki::UI::View::view at line 371 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm # once (5µs+0s) by Foswiki::UI::View::view at line 403 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm # once (4µs+0s) by Foswiki::new at line 1633 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm
*MARK = sub { };
120 *MonitorMethod = sub { };
121 }
1221921µs121µs}
# spent 21µs making 1 call to Monitor::BEGIN@113
123
124#a bit of a hack to allow us to display the time it took to render
125sub getRunTimeSoFar {
126 my $ibm = timestr( timediff( $times[$#times]->[1], $times[0]->[1] ) );
127 return $ibm;
128}
129
130
# spent 9µs within Monitor::END which was called: # once (9µs+0s) by main::RUNTIME at line 0 of view
sub END {
131112µs return unless ( $ENV{FOSWIKI_MONITOR} );
132 MARK('END');
133 my $lastbm;
134 my $firstbm;
135 my %mash;
136
137 if ( scalar(@times) > 1 ) {
138 my $ibm = timestr( timediff( $times[$#times]->[1], $times[0]->[1] ) );
139 if ( $ibm =~ /([\d.]+) wallclock/ ) {
140 $show_percent = $1;
141 }
142 print STDERR "\n\n| Event | Delta | Abs | Mem |";
143 foreach my $bm (@times) {
144 $firstbm = $bm unless $firstbm;
145 if ($lastbm) {
146 my $s = tidytime( $bm->[1], $lastbm->[1] );
147 my $t = tidytime( $bm->[1], $firstbm->[1] );
148 $s = "\n| $bm->[0] | $s | $t | $bm->[2]->{vsize} |";
149 print STDERR $s;
150 }
151 $lastbm = $bm;
152 }
153 print STDERR "\nTotal time: $ibm";
154 }
155
156 my %methods;
157 foreach my $call (@methodStats) {
158 $methods{ $call->{method} } = {
159 count => 0,
160 min => 99999999,
161 max => 0,
162 mem_min => 99999999,
163 mem_max => 0
164 }
165 unless defined( $methods{ $call->{method} } );
166 $methods{ $call->{method} }{count} += 1;
167 my $diff = timediff( $call->{out}, $call->{in} );
168
169 $methods{ $call->{method} }{min} = ${$diff}[0]
170 if ( $methods{ $call->{method} }{min} > ${$diff}[0] );
171 $methods{ $call->{method} }{max} = ${$diff}[0]
172 if ( $methods{ $call->{method} }{max} < ${$diff}[0] );
173 if ( defined( $methods{ $call->{method} }{total} ) ) {
174 $methods{ $call->{method} }{total} =
175 Benchmark::timesum( $methods{ $call->{method} }{total}, $diff );
176 }
177 else {
178 $methods{ $call->{method} }{total} = $diff;
179 }
180 my $memdiff = $call->{out_stat}{rss} - $call->{in_stat}{rss};
181 $methods{ $call->{method} }{mem_min} = $memdiff
182 if ( $methods{ $call->{method} }{mem_min} > $memdiff );
183 $methods{ $call->{method} }{mem_max} = $memdiff
184 if ( $methods{ $call->{method} }{mem_max} < $memdiff );
185 }
186 print STDERR
187"\n\n| Count | Time (Min/Max) | Memory(Min/Max) | Total | Method |";
188 foreach my $method ( sort keys %methods ) {
189 print STDERR "\n| "
190 . sprintf( '%6u', $methods{$method}{count} ) . ' | '
191 . sprintf( '%6.3f / %6.3f',
192 $methods{$method}{min},
193 $methods{$method}{max} )
194 . ' | '
195 . sprintf( '%6u / %6u',
196 $methods{$method}{mem_min},
197 $methods{$method}{mem_max} )
198 . ' | '
199 . timestr( $methods{$method}{total} )
200 . " | $method |";
201 }
202 print STDERR "\n";
203}
204
205#BEWARE - though this is extremely useful to show whats fast / slow in a Class, its also a potentially
206#deadly hack
207#method wrapper - http://chainsawblues.vox.com/library/posts/page/1/
208sub _monitorMethod {
209 my ( $package, $method ) = @_;
210
211 if ( !defined($method) ) {
2122266µs277µs
# spent 47µs (16+30) within Monitor::BEGIN@212 which was called: # once (16µs+30µs) by Foswiki::BEGIN@48 at line 212
no strict "refs";
# spent 47µs making 1 call to Monitor::BEGIN@212 # spent 30µs making 1 call to strict::unimport
213 foreach my $symname ( sort keys %{"${package}::"} ) {
214 next if ( $symname =~ /^ASSERT/ );
215 next if ( $symname =~ /^DEBUG/ );
216 next if ( $symname =~ /^UNTAINTED/ );
217 next if ( $symname =~ /^except/ );
218 next if ( $symname =~ /^otherwise/ );
219 next if ( $symname =~ /^finally/ );
220 next if ( $symname =~ /^try/ );
221 next if ( $symname =~ /^with/ );
222 _monitorMethod( $package, $symname );
223 }
224 }
225 else {
226 my $old = ($package)->can($method); # look up along MRO
227 return if ( !defined($old) );
228
229 #print STDERR "monitoring $package :: $method)";
230 {
231246µs280µs
# spent 49µs (17+32) within Monitor::BEGIN@231 which was called: # once (17µs+32µs) by Foswiki::BEGIN@48 at line 231
no warnings 'redefine';
# spent 49µs making 1 call to Monitor::BEGIN@231 # spent 32µs making 1 call to warnings::unimport
2322366µs272µs
# spent 43µs (15+29) within Monitor::BEGIN@232 which was called: # once (15µs+29µs) by Foswiki::BEGIN@48 at line 232
no strict "refs";
# spent 43µs making 1 call to Monitor::BEGIN@232 # spent 29µs making 1 call to strict::unimport
233 *{"${package}::$method"} = sub {
234
235 #Monitor::MARK("begin $package $method");
236 my $in_stat = _get_stat_info($$);
237 my $in_bench = new Benchmark();
238 my $self = shift;
239 my @result = $self->$old(@_);
240 my $out_bench = new Benchmark();
241
242 #Monitor::MARK("end $package $method => ".($result||'undef'));
243 my $out_stat = _get_stat_info($$);
244 push(
245 @methodStats,
246 {
247 method => "${package}::$method",
248 in => $in_bench,
249 in_stat => $in_stat,
250 out => $out_bench,
251 out_stat => $out_stat
252 }
253 );
254 return wantarray ? @result : $result[0];
255 }
256 }
257 }
258}
259
260#BEWARE - as above
261#provide more detailed information about a specific MACRO handler
262#this Presumes that the macro function is defined as 'sub Foswiki::MACRO' and can be loaded from 'Foswiki::Macros::MACRO'
263#
264# logs, session GET and POST params, MACRO and MACRO params and timing stats
265#
266# the $logFunction is an optional reference to a writeLog($name, hash_ref_of_values_to_log) (see DebugLogPlugin for an example)
267sub monitorMACRO {
268 my $package = 'Foswiki';
269 my $method = shift;
270 my $logLevel = shift;
271 my $logFunction = shift;
272
273 eval "require Foswiki::Macros::$method";
274 return if ($@);
275 my $old = ($package)->can($method); # look up along MRO
276 return if ( !defined($old) );
277
278 #print STDERR "monitoring $package :: $method)";
279 {
280251µs280µs
# spent 49µs (18+31) within Monitor::BEGIN@280 which was called: # once (18µs+31µs) by Foswiki::BEGIN@48 at line 280
no warnings 'redefine';
# spent 49µs making 1 call to Monitor::BEGIN@280 # spent 31µs making 1 call to warnings::unimport
2812405µs281µs
# spent 48µs (15+33) within Monitor::BEGIN@281 which was called: # once (15µs+33µs) by Foswiki::BEGIN@48 at line 281
no strict "refs";
# spent 48µs making 1 call to Monitor::BEGIN@281 # spent 33µs making 1 call to strict::unimport
282 *{"${package}::$method"} = sub {
283 my ( $session, $params, $topicObject ) = @_;
284
285 #Monitor::MARK("begin $package $method");
286 my $in_stat = _get_stat_info($$);
287 my $in_bench = new Benchmark();
288 my @result = $session->$old( $params, $topicObject, @_ );
289 my $out_bench = new Benchmark();
290
291 #Monitor::MARK("end $package $method => ".($result||'undef'));
292 my $out_stat = _get_stat_info($$);
293
294 my $stat_hash = {
295 method => "${package}::$method",
296 in => $in_bench,
297 in_stat => $in_stat,
298 out => $out_bench,
299 out_stat => $out_stat
300 };
301 push( @methodStats, $stat_hash );
302
303 if ( defined($logFunction) )
304 { #this is effectivly the same as $logLevel>0
305 #lets not make the %stat_hash huge, as its kept in memory
306 my %hashToLog = %$stat_hash;
307 $hashToLog{params} = $params;
308
309#if we're logging this detail of information, we're less worried about performance.
310#numbers _will be off_ if there are nested MACRO's being logged
311 $hashToLog{macroTime} =
312 timestr( timediff( $stat_hash->{out}, $stat_hash->{in} ) );
313 $hashToLog{macroMemory} =
314 $stat_hash->{out_stat}{rss} - $stat_hash->{in_stat}{rss};
315
316 if ( $logLevel > 1 ) {
317 $hashToLog{result} = wantarray ? @result : $result[0];
318 }
319 &$logFunction( $method, \%hashToLog );
320 }
321 return wantarray ? @result : $result[0];
322 }
323 }
324}
325
32616µs1;
327__END__