Filename | /var/www/foswiki11/lib/Monitor.pm |
Statements | Executed 26 statements in 1.99ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
7 | 7 | 2 | 29µs | 29µs | __ANON__[:101] | Monitor::
1 | 1 | 1 | 20µs | 42µs | BEGIN@39 | Monitor::
1 | 1 | 1 | 19µs | 38µs | BEGIN@188 | Monitor::
1 | 1 | 1 | 17µs | 17µs | BEGIN@90 | Monitor::
1 | 1 | 1 | 16µs | 42µs | BEGIN@207 | Monitor::
1 | 1 | 1 | 14µs | 22µs | BEGIN@40 | Monitor::
1 | 1 | 1 | 11µs | 27µs | BEGIN@208 | Monitor::
1 | 1 | 1 | 6µs | 6µs | END | Monitor::
0 | 0 | 0 | 0s | 0s | __ANON__[:102] | Monitor::
0 | 0 | 0 | 0s | 0s | __ANON__[:231] | Monitor::
0 | 0 | 0 | 0s | 0s | _get_stat_info | Monitor::
0 | 0 | 0 | 0s | 0s | _mark | Monitor::
0 | 0 | 0 | 0s | 0s | _monitorMethod | Monitor::
0 | 0 | 0 | 0s | 0s | tidytime | Monitor::
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 | |||||
5 | Monitoring package. Instrument the code like this: | ||||
6 | |||||
7 | use Monitor (); | ||||
8 | Monitor::MARK("Description of event"); | ||||
9 | Monitor::MARK("Another event"); | ||||
10 | |||||
11 | or, to monitor all the calls to a module | ||||
12 | |||||
13 | use Monitor (); | ||||
14 | Monitor::MonitorMethod('Foswiki::Users'); | ||||
15 | |||||
16 | or a function | ||||
17 | |||||
18 | use Monitor (); | ||||
19 | Monitor::MonitorMethod('Foswiki::Users', 'getCanonicalUserID'); | ||||
20 | |||||
21 | Then set the environment variable FOSWIKI_MONITOR to a perl true value, and | ||||
22 | run the script from the command line e.g: | ||||
23 | $ cd bin | ||||
24 | $ ./view -topic Myweb/MyTestTopic | ||||
25 | |||||
26 | The results will be printed to STDERR at the end of the run. Two times are | ||||
27 | shown, 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 | ||||
29 | is total memory. | ||||
30 | |||||
31 | NOTE: it uses /proc - so its linux specific... | ||||
32 | |||||
33 | TODO: replace FOSWIKI_MONITOR with LocalSite.cfg setting that can turn on per module instrumentation. | ||||
34 | |||||
35 | =cut | ||||
36 | |||||
37 | package Monitor; | ||||
38 | |||||
39 | 2 | 43µs | 2 | 64µ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 # spent 42µs making 1 call to Monitor::BEGIN@39
# spent 22µs making 1 call to strict::import |
40 | 2 | 569µs | 2 | 31µ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 # spent 22µs making 1 call to Monitor::BEGIN@40
# spent 9µs making 1 call to warnings::import |
41 | |||||
42 | 1 | 500ns | our @times; | ||
43 | 1 | 200ns | our @methodStats; | ||
44 | 1 | 100ns | our $show_percent; | ||
45 | |||||
46 | sub _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 | |||||
73 | sub _mark { | ||||
74 | my $event = shift; | ||||
75 | push( @times, [ $event, new Benchmark(), _get_stat_info($$) ] ); | ||||
76 | } | ||||
77 | |||||
78 | sub 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 | ||||
91 | 1 | 3µs | my $caller = caller; | ||
92 | 1 | 13µ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 { | ||||
101 | 8 | 36µ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 | ||
102 | 1 | 2µs | *MonitorMethod = sub { }; | ||
103 | } | ||||
104 | 1 | 770µs | 1 | 17µ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 | ||||
107 | 1 | 6µ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/ | ||||
184 | sub _monitorMethod { | ||||
185 | my ( $package, $method ) = @_; | ||||
186 | |||||
187 | if ( !defined($method) ) { | ||||
188 | 2 | 229µs | 2 | 58µ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 # 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 | { | ||||
207 | 2 | 46µs | 2 | 67µ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 # spent 42µs making 1 call to Monitor::BEGIN@207
# spent 25µs making 1 call to warnings::unimport |
208 | 2 | 266µs | 2 | 43µ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 # 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 | |||||
236 | 1 | 5µs | 1; | ||
237 | __END__ |