File Coverage

blib/lib/Devel/Profile.pm
Criterion Covered Total %
statement 11 133 8.2
branch 2 42 4.7
condition 0 15 0.0
subroutine 5 9 55.5
pod 1 5 20.0
total 19 204 9.3


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             # Copyright (c) 2002 by Jeff Weisberg
4             # Author: Jeff Weisberg <jaw+profile @ tcp4me.com>
5             # Date: 2002-Jun-21 22:19 (EDT)
6             # Function: code profiler
7             #
8             # $Id: Profile.pm,v 1.22 2007/03/08 02:25:42 jaw Exp $
9              
10             # Dost thou love life? Then do not squander time
11             # -- Benjamin Franklin
12              
13             # start as:
14             # env PERL5DB='BEGIN{require "src/Profile.pm"}' perl -d program.pl
15             # or: perl -d:Profile program.pl
16             # data gets saved in 'prof.out'
17              
18             # motivation:
19             # Devel::DProf appears to have issues. when it is used
20             # 9 times out of 10 it produces output that is unusable by dprofpp (even with -F)
21             # the statistics are often obviously wrong
22             # it causes crashage
23             # of course, this code isn't really any better....
24              
25             =head1 NAME
26            
27             Devel::Profile - tell me why my perl program runs so slowly
28            
29             =head1 SYNOPSIS
30            
31             perl -d:Profile program.pl
32             less prof.out
33            
34             =head1 DESCRIPTION
35            
36             The Devel::Profile package is a Perl code profiler.
37             This will collect information on the execution time of a Perl script and of the subs in that script.
38             This information can be used to determine which subroutines are using the most time and which
39             subroutines are being called most often.
40            
41             To profile a Perl script, run the perl interpreter with the -d debugging switch.
42             The profiler uses the debugging hooks.
43             So to profile script test.pl the following command should be used:
44            
45             perl -d:Profile test.pl
46            
47             When the script terminates (or periodicly while running, see ENVIRONMENT) the profiler will dump
48             the profile information to a file called F<prof.out>. This file is human-readable, no
49             additional tool is required to read it.
50            
51             Note: Statistics are kept per sub, not per line.
52            
53             =head1 ENVIRONMENT
54            
55             =over 4
56            
57             =item C<PERL_PROFILE_SAVETIME>
58            
59             How often to save profile data while running, in seconds, 0 to save only at exit.
60             The default is every 2 minutes.
61            
62             =item C<PERL_PROFILE_FILENAME>
63            
64             Filename to save profile data to, default is F<prof.out>
65            
66             =item C<PERL_PROFILE_DONT_OTHER>
67            
68             Time spent running code not in 'subs' (such as naked code in main) won\'t
69             get accounted for in the normal manner. By default, we account for this time
70             in the sub '<other>'. With this variable set, we leave it as 'missing' time.
71             This reduces the effective runtime of the program, and the calculated percentages.
72            
73             =back
74            
75             =cut
76                 ;
77             # more POD at end
78              
79             package Devel::Profile;
80             $VERSION = "1.05";
81              
82             package DB;
83             BEGIN {
84 0     0 0 0     sub DB {}
85 1     1   4173     require Time::HiRes; Time::HiRes->import('time');
  1         2338  
86             }
87              
88             my $t0 = time(); # start time
89             my $tsav = $t0; # time of last save
90             my $tacc = 0; # total time accumulated
91             my $tacc0 = 0; # total time accumulated at start (or reset)
92             my $call = 0; # total number of calls
93             my $except = 0; # total number of exceptions handled (est)
94             my $saving = 0; # save in progress
95             my $tprof_save = 0; # time spent saving data
96             my %prof_calls = (); # number of calls per sub
97             my %prof_times = (); # total time per sub
98             my %prof_flags = (); # flags
99             my @prof_stack = (); # call stack, to account for subs that haven't returned
100             my $want_reset = 0; # reset request pending
101             my $prof_pid = $$; # process id
102              
103             my $TSAVE = defined($ENV{PERL_PROFILE_SAVETIME}) ? $ENV{PERL_PROFILE_SAVETIME} : 120;
104             my $NCALOOP = 1000;
105              
106             $SIG{USR2} = \&reset;
107              
108             sub sub {
109              
110 0     0 1 0     my $ti = time(); # wall time at start
111             # save first, keeps timing calculations simpler
112 0 0 0     0     if( !$saving && $TSAVE && ($ti - $tsav) > $TSAVE ){
      0        
113 0         0 save();
114 0         0 $ti = time(); # update to account for save
115                 }
116                 
117 0         0     my $st = $tacc; # accum time at start
118 0         0     my $sx = $sub;
119 0 0       0     if( ref $sx ){
120 0         0 my @c = caller;
121             # was 0, now 1
122             # nb: @c = (pkg, file, line, ...)
123 0         0 $sx = "<anon>:$c[1]:$c[2]";
124                 }
125 0         0     push @prof_stack, [$sx, $ti, $st];
126 0         0     my $ss = @prof_stack;
127                 
128 0         0     my( $wa, $r, @r );
129 0         0     $wa = wantarray;
130 0 0       0     if( $wa ){
    0          
131 0         0 @r = &$sub;
132                 }elsif( defined $wa ){
133 0         0 $r = &$sub;
134                 }else{
135 0         0 &$sub;
136                 }
137              
138 0 0       0     if( $ss < @prof_stack ){
139             # we took an exception - account for aborted subs
140             # print STDERR "exception detected!\n";
141            
142 0         0 while( $ss < @prof_stack ){
143 0         0 my $sk = pop @prof_stack;
144 0         0 my $sn = $sk->[0];
145 0         0 my $t = time() - $sk->[1] - ($tacc - $sk->[2]);
146 0         0 $tacc += $t;
147 0         0 $prof_times{$sn} += $t;
148 0         0 $prof_calls{$sn} ++;
149 0         0 $prof_flags{$sn} |= 2;
150 0         0 $call ++;
151             }
152 0         0 $except++;
153 0         0 $prof_flags{$sx} |= 4;
154                 }
155                 
156 0 0       0     if( pop @prof_stack ){ # do not update if reset
157 0         0 my $t = time() - $ti # total time of called sub
158             - ($tacc - $st); # minus time of subs it called
159 0         0 $tacc += $t;
160 0         0 $prof_times{$sx} += $t; # We take no note of time
161 0         0 $prof_calls{$sx} ++; # But from its loss
162 0         0 $call ++; # -- Edward Young, Night Thoughts
163                 }
164              
165 0 0       0     if( $wa ){
166 0         0 @r;
167                 }else{
168 0         0 $r;
169                 }
170             }
171              
172             sub save {
173 1 50   1 0 6     return if $saving;
174 1 50       4     unless( $call ){
175             # nothing to report
176 1         6 $tsav = time();
177 1         0 return;
178                 }
179 0         0     $saving = 1;
180              
181             # only parent process
182 0 0       0     return unless $$ == $prof_pid;
183                 
184 0         0     my $tnow = time();
185 0         0     my $ttwall = $tnow - $t0;
186 0   0     0     my $f = $ENV{PERL_PROFILE_FILENAME} || 'prof.out';
187 0 0       0     open( F, "> $f" ) || die "open failed, $f $!\n";
188              
189             # calc. an estimate of Tadj (overhead of DB::sub)
190             # Tadj = 3/4 of the fastest sub
191 0         0     my $tadj;
192 0         0     foreach my $s (keys %prof_times){
193 0 0       0 next unless $prof_calls{$s} >= 10;
194 0         0 my $t = $prof_times{$s} / $prof_calls{$s};
195 0 0 0     0 $tadj = $t if !defined($tadj) || $t < $tadj;
196                 }
197 0         0     $tadj *= .75;
198                 
199             # adjust run times
200 0         0     my( %times, %calls, %flags );
201 0         0     %calls = %prof_calls;
202 0         0     %flags = %prof_flags;
203 0         0     foreach (keys %prof_times){
204 0         0 $times{$_} = $prof_times{$_} - $tadj * $prof_calls{$_};
205                 }
206                 
207             # calculate profiling overhead, and hide our droppings
208 0         0     my $calladj = 0;
209 0         0     my $tprof = $tadj * $call + $times{Devel::Profile::__db_calibrate_adj} + $tprof_save;
210 0         0     delete $times{Devel::Profile::__db_calibrate_adj};
211 0         0     $calladj = 0 - $prof_calls{Devel::Profile::__db_calibrate_adj};
212                 
213             # calc time of subs that never finished, by unwinding the saved call stack
214 0         0     my $xend = $tnow;
215 0         0     my $xacc = $tacc;
216 0         0     foreach my $sk (reverse @prof_stack){
217             # since it didn't return, we only adjust by half of Tadj
218 0         0 my $sn = $sk->[0];
219 0         0 my $t = $xend - $sk->[1] - ($xacc - $sk->[2]);
220 0         0 $times{ $sn } += $t - $tadj/2;
221 0         0 $calls{ $sn } ++;
222             # and since we are using different math, and a different estimate of
223             # the profiling overhead, we display a flag alerting the user
224 0         0 $flags{ $sn } |= 2;
225 0         0 $xend = $sk->[1];
226 0         0 $xacc = $sk->[2];
227 0         0 $tprof += $tadj/2;
228 0         0 $calladj ++;
229                 }
230                 
231             # calc time for other: "naked" code, ???
232 0 0       0     unless( $ENV{PERL_PROFILE_DONT_OTHER} ){
233 0         0 my $tnaked = $xend - $t0 - ($tacc - $tacc0);
234 0 0       0 if( $tnaked < 0 ){
235             # dang! mis-estimates threw our numbers off by too much
236             # print STDERR "dang: $tnaked = $xend - $t0 - ($tacc - $tacc0)\n";
237 0         0 $tnaked = 0;
238             }
239 0         0 $times{'<other>'} = $tnaked;
240 0         0 $calls{'<other>'} = 0;
241 0         0 $flags{'<other>'} |= 1;
242                 }
243              
244             # total run time of program
245 0         0     my $tt;
246 0         0     foreach (values %times){$tt += $_}
  0         0  
247              
248             # dreams are very curious and unaccountable things
249             # -- Homer, Odyssey
250             # unaccounted for "missing" time
251 0         0     my $tmissing = $ttwall - $tt - $tprof;
252                 
253 0         0     printf F "time elapsed (wall): %.4f\n", $ttwall;
254 0         0     printf F "time running program: %.4f (%.2f%%)\n", $tt, 100 * $tt / $ttwall;
255 0         0     printf F "time profiling (est.): %.4f (%.2f%%)\n", $tprof, 100 * $tprof / $ttwall;
256 0 0       0     printf F "missing time: %.4f (%.2f%%)\n", $tmissing, 100 * $tmissing / $ttwall
257             if( $tmissing / $ttwall > 0.0001 );
258 0         0     print F "number of calls: ", $call + $calladj, "\n";
259 0 0       0     print F "number of exceptions: $except\n" if $except;
260                 
261 0         0     print F "\n%Time Sec. \#calls sec/call F name\n";
262 0         0     foreach my $s (sort {$times{$b} <=> $times{$a}} keys %times){
  0         0  
263 0         0 my $c = $calls{$s};
264 0         0 my $t = $times{$s};
265 0   0     0 my $tpc = $t / ($c || 1);
266 0         0 my $pct = $t * 100 / $tt;
267 0         0 my $sp = $s;
268              
269 0 0       0 if( substr($sp, 0, 6) eq '<anon>' ){
270             # make prettier
271 0 0       0 if( length($sp) > 35 ){
272 0         0 $sp = '<anon>:...' . substr($sp, -28, 28);
273             }
274             }
275            
276 0         0 printf F "%5.2f %9.4f %7d %9.6f %2s $sp\n",
277             $pct, $t, $c, $tpc, F($flags{$s});
278                 }
279 0         0     close F;
280              
281             # Let every man be master of his time
282             # -- Shakespeare, Macbeth
283             # account for time spent saving data
284 0         0     $tsav = time();
285 0         0     my $telap = $tsav - $tnow;
286 0         0     $tacc += $telap;
287 0         0     $tprof_save += $telap;
288                 
289 0         0     $saving = 0;
290 0 0       0     reset() if $want_reset;
291             }
292              
293             # 1=> *, 2=>?, 4=>x
294             sub F {
295 0   0 0 0 0     ('', '*', '?', '?*', 'x', 'x*', 'x?', 'X?')[shift || 0];
296             }
297              
298             sub reset {
299 0 0   0 0 0     if( $saving ){
300 0         0 $want_reset = 1;
301 0         0 return;
302                 }
303 0         0     save();
304 0         0     $t0 = time();
305 0         0     $tacc0 = $tacc;
306 0         0     $call = 0;
307 0         0     $except = 0;
308 0         0     %prof_calls = ();
309 0         0     %prof_times = ();
310 0         0     %prof_flags = ();
311 0         0     @prof_stack = ();
312 0         0     $want_reset = 0;
313             }
314              
315             END {
316 1     1   161     save();
317             }
318              
319             ################################################################
320             package Devel::Profile;
321 1     1   2074 use strict;
  1         3  
  1         163  
322             sub __db_calibrate_adj {
323 1000     1000   1445     my $x = shift;
324             }
325             for my $i (1..$NCALOOP){
326                 __db_calibrate_adj();
327             }
328              
329             ################################################################
330              
331             # o When execution of the program reaches a subroutine
332             # call, a call to "&DB::sub"(args) is made instead, with
333             # "$DB::sub" holding the name of the called subroutine.
334             # This doesn't happen if the subroutine was compiled in
335             # the "DB" package.)
336              
337             ################################################################
338              
339             =head1 OUTPUT FORMAT
340            
341             example ouput:
342            
343             time elapsed (wall): 86.8212
344             time running program: 65.7657 (75.75%)
345             time profiling (est.): 21.0556 (24.25%)
346             number of calls: 647248
347            
348             %Time Sec. #calls sec/call F name
349             31.74 20.8770 2306 0.009053 Configable::init_from_config
350             20.09 13.2116 144638 0.000091 Configable::init_field_from_config
351             17.49 11.5043 297997 0.000039 Configable::has_attr
352             8.22 5.4028 312 0.017317 MonEl::recycle
353             7.54 4.9570 64239 0.000077 Configable::inherit
354             5.02 3.3042 101289 0.000033 MonEl::unique
355             [...]
356            
357             This is a small summary, followed by one line per sub.
358            
359             =over 4
360            
361             =item time elapsed (wall)
362            
363             This is the total time elapsed.
364            
365             =item time running program
366            
367             This is the amount of time spent running your program.
368            
369             =item time profiling
370            
371             This is the amount of time wasted due to profiler overhead.
372            
373             =item number of calls
374            
375             This is the total number of subroutine calls your program made.
376            
377             =back
378            
379             Followed by one line per subroutine.
380            
381             =over 4
382            
383             =item name
384            
385             The name of the subroutine.
386            
387             =item %Time
388            
389             The percentage of the total program runtime used by this subroutine.
390            
391             =item Sec.
392            
393             The total number of seconds used by this subroutine.
394            
395             =item #calls
396            
397             The number of times this subroutine was called.
398            
399             =item sec/call
400            
401             The average number of seconds this subroutines takes each time it is called.
402            
403             =item F
404            
405             Flags.
406            
407             =over 4
408            
409             =item C<*>
410            
411             pseudo-function to account for otherwise unacounted for time.
412            
413             =item C<?>
414            
415             At least one call of this subroutine did not return (typically because
416             of an C<exit>, or C<die>). The statistics for it may be slightly off.
417            
418             =item C<x>
419            
420             At least one call of this subroutine trapped an exception.
421             The statistics for it may be slightly off.
422            
423             =back
424            
425             =back
426            
427             =head1 LONG RUNNING PROGRAMS
428            
429             This module was written so that the author could profile a large long-running
430             (daemon) program. Since normally, this program never exited, saving profiling
431             data only at program exit was not an interesting option. This module will save
432             profiling data periodically based on $PERL_PROFILE_SAVETIME, or the program
433             being profiled can call C<DB::save()> at any time. This allows you to watch
434             your profiling data while the program is running.
435            
436             The above program also had a very large startup phase (reading config files,
437             building data structures, etc), the author wanted to see profiling data
438             for the startup phase, and for the running phase seperately. The running
439             program can call C<DB::reset()> to save the profiling data and reset the
440             statistics. Once reset, only "stuff" that happens from that point on will be
441             reflected in the profile data file.
442            
443             By default, reset is attached to the signal handler for C<SIGUSR2>.
444             Using a perl built with "safe signal handling" (5.8.0 and higher),
445             you may safely send this signal to control profiling.
446            
447             =head1 BUT I WANT INCLUSIVE TIMES NOT EXCLUSIVE TIMES
448            
449             Please see the spin-off module Devel::DProfLB.
450            
451             =head1 BUGS
452            
453             Some buggy XS based perl modules can behave erroneously when
454             run under the perl debugger. Since Devel::Profile uses the perl
455             debugger interfaces, these modules will also behave erroneously
456             when being profiled.
457            
458             There are no known bugs in this module.
459            
460             =head1 LICENSE
461            
462             This software may be copied and distributed under the terms
463             found in the Perl "Artistic License".
464            
465             A copy of the "Artistic License" may be found in the standard
466             Perl distribution.
467            
468             =head1 SEE ALSO
469            
470             Yellowstone National Park.
471             Devel::DProfLB
472            
473             =head1 AUTHOR
474            
475             Jeff Weisberg - http://www.tcp4me.com/
476            
477             =cut
478                 ;
479              
480             1;
481