4 use File::Basename qw(&basename &dirname);
6 # List explicitly here the variables you want Configure to
7 # generate. Metaconfig only looks for shell variables, so you
8 # have to mention them as if they were shell variables, not
9 # %Config entries. Thus you write
11 # to ensure Configure will look for $Config{startperl}.
13 # This forces PL files to create target in same directory as PL file.
14 # This is so that make depend always knows where to find PL derivatives.
16 ($file = basename($0)) =~ s/\.PL$//;
17 $file =~ s/\.pl$// if ($Config{'osname'} eq 'OS2'); # "case-forgiving"
18 $file =~ s/\.pl$/.com/ if ($Config{'osname'} eq 'VMS'); # "case-forgiving"
20 my $dprof_pm = '../ext/Devel/DProf/DProf.pm';
22 open( PM, "<$dprof_pm" ) || die "Can't open $dprof_pm: $!";
24 if( /^\$Devel::DProf::VERSION\s*=\s*'([\d._]+)'/ ){
31 die "Did not find VERSION in $dprof_pm";
33 open OUT,">$file" or die "Can't create $file: $!";
35 print "Extracting $file (with variable substitutions)\n";
37 # In this section, perl variables will be expanded during extraction.
38 # You can use $Config{...} to use Configure variables.
40 print OUT <<"!GROK!THIS!";
42 eval 'exec perl -S \$0 "\$@"'
47 my \$VERSION = '$VERSION';
51 # In the following, perl variables are not expanded during extraction.
53 print OUT <<'!NO!SUBS!';
56 dprofpp - display perl profile data
60 dprofpp [B<-a>|B<-z>|B<-l>|B<-v>|B<-U>] [B<-s>|B<-r>|B<-u>] [B<-q>] [B<-F>] [B<-I|-E>] [B<-O cnt>] [B<-A>] [B<-R>] [B<-S>] [B<-g subroutine>] [profile]
62 dprofpp B<-T> [B<-F>] [B<-g subroutine>] [profile]
64 dprofpp B<-t> [B<-F>] [B<-g subroutine>] [profile]
66 dprofpp B<-p script> [B<-Q>] [other opts]
68 dprofpp B<-V> [profile]
72 The I<dprofpp> command interprets profile data produced by a profiler, such
73 as the Devel::DProf profiler. Dprofpp will read the file F<tmon.out> and
74 will display the 15 subroutines which are using the most time. By default
75 the times for each subroutine are given exclusive of the times of their
78 To profile a Perl script run the perl interpreter with the B<-d> switch. So
79 to profile script F<test.pl> with Devel::DProf the following command should
82 $ perl5 -d:DProf test.pl
84 Then run dprofpp to analyze the profile. The output of dprofpp depends
85 on the flags to the program and the version of Perl you're using.
88 Total Elapsed Time = 1.67 Seconds
89 User Time = 0.61 Seconds
91 %Time Seconds #Calls sec/call Name
92 52.4 0.320 2 0.1600 main::foo
93 45.9 0.280 200 0.0014 main::bar
94 0.00 0.000 1 0.0000 DynaLoader::import
95 0.00 0.000 1 0.0000 main::baz
97 The dprofpp tool can also run the profiler before analyzing the profile
98 data. The above two commands can be executed with one dprofpp command.
100 $ dprofpp -u -p test.pl
102 Consult L<Devel::DProf/"PROFILE FORMAT"> for a description of the raw profile.
112 Percentage of time spent in this routine.
116 Number of calls to this routine.
120 Average number of seconds per call to this routine.
128 Time (in seconds) spent in this routine and routines called from it.
132 Time (in seconds) spent in this routine (not including those called
137 Average time (in seconds) spent in each call of this routine
138 (including those called from it).
148 Sort alphabetically by subroutine names.
152 Count timing for autoloaded subroutine as timing for C<*::AUTOLOAD>.
153 Otherwise the time to autoload it is counted as time of the subroutine
154 itself (there is no way to separate autoload time from run time).
156 This is going to be irrelevant with newer Perls. They will inform
157 C<Devel::DProf> I<when> the C<AUTOLOAD> switches to actual subroutine,
158 so a separate statistics for C<AUTOLOAD> will be collected no matter
159 whether this option is set.
163 Count anonymous subroutines defined in the same package separately.
167 (default) Display all subroutine times exclusive of child subroutine times.
171 Force the generation of fake exit timestamps if dprofpp reports that the
172 profile is garbled. This is only useful if dprofpp determines that the
173 profile is garbled due to missing exit timestamps. You're on your own if
174 you do this. Consult the BUGS section.
178 Display all subroutine times inclusive of child subroutine times.
182 Sort by number of calls to the subroutines. This may help identify
183 candidates for inlining.
187 Show only I<cnt> subroutines. The default is 15.
191 Tells dprofpp that it should profile the given script and then interpret its
192 profile data. See B<-Q>.
196 Used with B<-p> to tell dprofpp to quit after profiling the script, without
197 interpreting the data.
201 Do not display column headers.
205 Display elapsed real times rather than user+system times.
209 Display system times rather than user+system times.
213 Display subroutine call tree to stdout. Subroutine statistics are
218 Display subroutine call tree to stdout. Subroutine statistics are not
219 displayed. When a function is called multiple consecutive times at the same
220 calling level then it is displayed once with a repeat count.
224 Display I<merged> subroutine call tree to stdout. Statistics is
225 displayed for each branch of the tree.
227 When a function is called multiple (I<not necessarily consecutive>)
228 times in the same branch then all these calls go into one branch of
229 the next level. A repeat count is output together with combined
230 inclusive, exclusive and kids time.
232 Branches are sorted w.r.t. inclusive time.
236 Do not sort. Display in the order found in the raw profile.
240 Display user times rather than user+system times.
244 Print dprofpp's version number and exit. If a raw profile is found then its
245 XS_VERSION variable will be displayed, too.
249 Sort by average time spent in subroutines during each call. This may help
250 identify candidates for inlining.
254 (default) Sort by amount of user+system time used. The first few lines
255 should show you which subroutines are using the most time.
257 =item B<-g> C<subroutine>
259 Ignore subroutines except C<subroutine> and whatever is called from it.
265 The environment variable B<DPROFPP_OPTS> can be set to a string containing
266 options for dprofpp. You might use this if you prefer B<-I> over B<-E> or
267 if you want B<-F> on all the time.
269 This was added fairly lazily, so there are some undesirable side effects.
270 Options on the commandline should override options in DPROFPP_OPTS--but
271 don't count on that in this version.
275 Applications which call _exit() or exec() from within a subroutine
276 will leave an incomplete profile. See the B<-F> option.
278 Any bugs in Devel::DProf, or any profiler generating the profile data, could
279 be visible here. See L<Devel::DProf/BUGS>.
281 Mail bug reports and feature requests to the perl5-porters mailing list at
282 F<E<lt>perl5-porters@perl.orgE<gt>>. Bug reports should include the
283 output of the B<-V> option.
287 dprofpp - profile processor
288 tmon.out - raw profile
292 L<perl>, L<Devel::DProf>, times(2)
296 use Getopt::Std 'getopts';
297 use Config '%Config';
300 my $options = 'O:g:lzaAvuTtqrRsUFEIp:QVS';
302 $Monfile = 'tmon.out';
303 if( exists $ENV{DPROFPP_OPTS} ){
305 @ARGV = split( ' ', $ENV{DPROFPP_OPTS} );
308 # there was a filename.
316 # there was a filename, it overrides any earlier name.
320 # -O cnt Specifies maximum number of subroutines to display.
321 # -a Sort by alphabetic name of subroutines.
322 # -z Sort by user+system time spent in subroutines. (default)
323 # -l Sort by number of calls to subroutines.
324 # -v Sort by average amount of time spent in subroutines.
326 # -t Show call tree, compressed.
327 # -q Do not print column headers.
328 # -u Use user time rather than user+system time.
329 # -s Use system time rather than user+system time.
330 # -r Use real elapsed time rather than user+system time.
331 # -U Do not sort subroutines.
332 # -E Sub times are reported exclusive of child times. (default)
333 # -I Sub times are reported inclusive of child times.
334 # -V Print dprofpp's version.
335 # -p script Specifies name of script to be profiled.
336 # -Q Used with -p to indicate the dprofpp should quit after
337 # profiling the script, without interpreting the data.
338 # -A count autoloaded to *AUTOLOAD
339 # -R count anonyms separately even if from the same package
340 # -g subr count only those who are SUBR or called from SUBR
341 # -S Create statistics for all the depths
343 if( defined $opt_V ){
345 print "$0 version: $VERSION\n";
346 open( $fh, "<$Monfile" ) && do {
347 local $XS_VERSION = 'early';
350 print "XS_VERSION: $XS_VERSION\n";
356 $sort = 'by_ctime' if defined $opt_I;
357 $sort = 'by_calls' if defined $opt_l;
358 $sort = 'by_alpha' if defined $opt_a;
359 $sort = 'by_avgcpu' if defined $opt_v;
360 $incl_excl = 'Exclusive';
361 $incl_excl = 'Inclusive' if defined $opt_I;
362 $whichtime = 'User+System';
363 $whichtime = 'System' if defined $opt_s;
364 $whichtime = 'Real' if defined $opt_r;
365 $whichtime = 'User' if defined $opt_u;
367 if( defined $opt_p ){
369 my $startperl = $Config{'startperl'};
371 $startperl =~ s/^#!//; # remove shebang
372 run_profiler( $opt_p, $prof, $startperl );
373 $Monfile = 'tmon.out'; # because that's where it is
374 exit(0) if defined $opt_Q;
376 elsif( defined $opt_Q ){
377 die "-Q is meaningful only when used with -p\n";
382 my $monout = $Monfile;
385 local $times = {}; # times in hz
386 local $ctimes = {}; # Cumulative times in hz
388 local $persecs = {}; # times in seconds
390 local $runtime; # runtime in seconds
393 local $rrun_utime = 0; # user time in hz
394 local $rrun_stime = 0; # system time in hz
395 local $rrun_rtime = 0; # elapsed run time in hz
396 local $rrun_ustime = 0; # user+system time in hz
398 local $deep_times = {count => 0 , kids => {}, incl_time => 0};
399 local $time_precision = 2;
402 open( $fh, "<$monout" ) || die "Unable to open $monout\n";
406 $rrun_ustime = $rrun_utime + $rrun_stime;
413 parsestack( $fh, $names, $calls, $times, $ctimes, $idkeys );
415 settime( \$runtime, $hz ) unless $opt_g;
417 exit(0) if $opt_T || $opt_t;
420 percalc( $calls, ($opt_I ? $ctimes : $times), $persecs, $idkeys );
423 @a = sort $sort @$idkeys;
429 display( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt, $a,
434 # Sets $runtime to user, system, real, or user+system time. The
435 # result is given in seconds.
438 my( $runtime, $hz ) = @_;
443 $$runtime = ($rrun_rtime - $overhead - $over_rtime * $total_marks/$over_tests/2)/$hz;
446 $$runtime = ($rrun_stime - $overhead - $over_stime * $total_marks/$over_tests/2)/$hz;
449 $$runtime = ($rrun_utime - $overhead - $over_utime * $total_marks/$over_tests/2)/$hz;
452 $$runtime = ($rrun_ustime - $overhead - ($over_utime + $over_stime) * $total_marks/$over_tests/2)/$hz;
454 $$runtime = 0 unless $$runtime > 0;
457 sub exclusives_in_tree {
458 my( $deep_times ) = @_;
461 # When summing, take into account non-rounded-up kids time.
462 for $kid (keys %{$deep_times->{kids}}) {
463 $kids_time += $deep_times->{kids}{$kid}{incl_time};
465 $kids_time = 0 unless $kids_time >= 0;
466 $deep_times->{excl_time} = $deep_times->{incl_time} - $kids_time;
467 $deep_times->{excl_time} = 0 unless $deep_times->{excl_time} >= 0;
468 for $kid (keys %{$deep_times->{kids}}) {
469 exclusives_in_tree($deep_times->{kids}{$kid});
471 $deep_times->{incl_time} = 0 unless $deep_times->{incl_time} >= 0;
472 $deep_times->{kids_time} = $kids_time;
475 sub kids_by_incl { $kids{$b}{incl_time} <=> $kids{$a}{excl_time}
479 my( $deep_times, $name, $level ) = @_;
480 exclusives_in_tree($deep_times);
483 local *kids = $deep_times->{kids}; # %kids
487 $time = sprintf '%.*fs = (%.*f + %.*f)',
488 $time_precision, $deep_times->{incl_time}/$hz,
489 $time_precision, $deep_times->{excl_time}/$hz,
490 $time_precision, $deep_times->{kids_time}/$hz;
492 $time = sprintf '%.*f', $time_precision, $deep_times->{incl_time}/$hz;
494 print ' ' x (2*$level), "$name x $deep_times->{count} \t${time}s\n"
495 if $deep_times->{count};
497 for $kid (sort kids_by_incl keys %kids) {
498 display_tree( $deep_times->{kids}{$kid}, $kid, $level + 1 );
502 # Report the times in seconds.
504 my( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt,
505 $idkeys, $deep_times ) = @_;
506 my( $x, $key, $s, $cs );
507 #format: $ncalls, $name, $secs, $percall, $pcnt
510 display_tree( $deep_times, 'toplevel', -1 )
512 for( $x = 0; $x < @$idkeys; ++$x ){
513 $key = $idkeys->[$x];
514 $ncalls = $calls->{$key};
515 $name = $names->{$key};
516 $s = $times->{$key}/$hz;
517 $secs = sprintf("%.3f", $s );
518 $cs = $ctimes->{$key}/$hz;
519 $csecs = sprintf("%.3f", $cs );
520 $percall = sprintf("%.4f", $s/$ncalls );
521 $cpercall = sprintf("%.4f", $cs/$ncalls );
522 $pcnt = sprintf("%.2f",
523 $runtime? ((($opt_I ? $csecs : $secs) / $runtime) * 100.0): 0 );
525 $pcnt = $secs = $ncalls = $percall = "";
526 write while( length $name );
533 my ($source, $dest) = @_;
536 for $kid (keys %$source) {
537 if (exists $dest->{$kid}) {
538 $dest->{count} += $source->{count};
539 $dest->{incl_time} += $source->{incl_time};
540 move_keys($source->{kids},$dest->{kids});
542 $dest->{$kid} = delete $source->{$kid};
548 my ($curdeep_times, $name, $t) = @_;
549 if ($name ne $curdeep_times->[-1]{name} and $opt_A) {
550 $name = $curdeep_times->[-1]{name};
552 die "Shorted?!" unless @$curdeep_times >= 2;
553 $curdeep_times->[-2]{kids}{$name} = { count => 0 , kids => {},
556 unless exists $curdeep_times->[-2]{kids}{$name};
557 my $entry = $curdeep_times->[-2]{kids}{$name};
558 # Now transfer to the new node (could not do earlier, since name can change)
560 $entry->{incl_time} += $t - $curdeep_times->[-1]{enter_stamp};
562 move_keys($curdeep_times->[-1]->{kids},$entry->{kids});
567 my( $fh, $names, $calls, $times, $ctimes, $idkeys ) = @_;
569 my( $t, $syst, $realt, $usert );
570 my( $x, $z, $c, $id, $pack );
576 # remember last call depth and function name
584 my $in_level = not defined $opt_g; # Level deep in report grouping
585 my $curdeep_times = [$deep_times];
588 if ( $opt_u ) { $over_per_call = $over_utime }
589 elsif( $opt_s ) { $over_per_call = $over_stime }
590 elsif( $opt_r ) { $over_per_call = $over_rtime }
591 else { $over_per_call = $over_utime + $over_stime }
592 $over_per_call /= 2*$over_tests; # distribute over entry and exit
600 ($dir, $id, $pack, $name) = split;
601 if ($opt_R and ($name =~ /::(__ANON_|END)$/)) {
604 $cv_hash{$id} = "$pack\::$name";
607 ($dir, $usert, $syst, $realt, $name) = split;
611 $syst = $stack[-1][0];
614 #warn("Inserted exit for $stack[-1][0].\n")
616 if (defined $realt) { # '+ times nam' '- times nam' or '@ incr'
617 if ( $opt_u ) { $t = $usert }
618 elsif( $opt_s ) { $t = $syst }
619 elsif( $opt_r ) { $t = $realt }
620 else { $t = $usert + $syst }
621 $t += $ot, next if $dir eq '@'; # Increments there
623 # "- id" or "- & name"
624 $name = defined $syst ? $syst : $cv_hash{$usert};
627 next unless $in_level or $name eq $opt_g or $dir eq '*';
628 if ( $dir eq '-' or $dir eq '*' ) {
629 my $ename = $dir eq '*' ? $stack[-1][0] : $name;
630 $overhead += $over_per_call;
631 if ($name eq "Devel::DProf::write") {
632 $dprof_t += $t - $dprof_stamp;
634 } elsif (defined $opt_g and $ename eq $opt_g) {
637 add_to_tree($curdeep_times, $ename,
638 $t - $dprof_t - $overhead) if $opt_S;
639 exitstamp( \@stack, \@tstack,
640 $t - $dprof_t - $overhead,
641 $times, $ctimes, $ename, \$in, $tab,
644 next unless $in_level or $name eq $opt_g;
645 if( $dir eq '+' or $dir eq '*' ){
646 if ($name eq "Devel::DProf::write") {
649 } elsif (defined $opt_g and $name eq $opt_g) {
652 $overhead += $over_per_call;
654 print ' ' x $in, "$name\n";
658 # suppress output on same function if the
659 # same calling level is called.
660 if ($l_in == $in and $l_name eq $name) {
663 $repstr = ' ('.++$repcnt.'x)'
665 print ' ' x $l_in, "$l_name$repstr\n"
674 if( ! defined $names->{$name} ){
675 $names->{$name} = $name;
677 $ctimes->{$name} = 0;
678 push( @$idkeys, $name );
681 push @$curdeep_times, { kids => {},
683 enter_stamp => $t - $dprof_t - $overhead,
685 $x = [ $name, $t - $dprof_t - $overhead ];
688 # my children will put their time here
690 } elsif ($dir ne '-'){
691 die "Bad profile: $_";
695 $repstr = ' ('.++$repcnt.'x)' if $repcnt;
696 print ' ' x $l_in, "$l_name$repstr\n";
701 warn "Garbled profile is missing some exit time stamps:\n";
702 foreach $x (@stack) {
705 die "Try rerunning dprofpp with -F.\n";
706 # I don't want -F to be default behavior--yet
710 warn( "Faking " . scalar( @stack ) . " exit timestamp(s).\n");
711 foreach $x ( reverse @stack ){
713 exitstamp( \@stack, \@tstack,
714 $t - $dprof_t - $overhead, $times,
715 $ctimes, $name, \$in, $tab,
717 add_to_tree($curdeep_times, $name,
718 $t - $dprof_t - $overhead)
723 if (defined $opt_g) {
724 $runtime = $ctimes->{$opt_g}/$hz;
725 $runtime = 0 unless $runtime > 0;
730 my($stack, $tstack, $t, $times, $ctimes, $name, $in, $tab, $deep) = @_;
735 die "Garbled profile, missing an enter time stamp";
737 if( $x->[0] ne $name ){
738 if ($x->[0] =~ /::AUTOLOAD$/) {
743 warn( "Garbled profile, faking exit timestamp:\n\t$name => $x->[0].\n");
746 foreach $z (@stack, $x) {
749 die "Garbled profile, unexpected exit time stamp";
752 if( $opt_T || $opt_t ){
756 $c = pop( @$tstack );
757 # total time this func has been active
759 $ctimes->{$name} += $z;
760 $times->{$name} += ($z > $c)? $z - $c: 0;
761 # pass my time to my parent
763 $c = pop( @$tstack );
764 push( @$tstack, $c + $z );
772 if( ! /^#fOrTyTwO$/ ){
773 die "Not a perl profile";
780 $over_tests = 1 unless $over_tests;
781 $time_precision = length int ($hz - 1); # log ;-)
785 # Report avg time-per-function in seconds
787 my( $calls, $times, $persecs, $idkeys ) = @_;
788 my( $x, $t, $n, $key );
790 for( $x = 0; $x < @$idkeys; ++$x ){
791 $key = $idkeys->[$x];
793 $t = $times->{$key} / $hz;
794 $persecs->{$key} = $t ? $t / $n : 0;
799 # Runs the given script with the given profiler and the given perl.
802 my $profiler = shift;
803 my $startperl = shift;
805 system $startperl, "-d:$profiler", $script;
807 die "Failed: $startperl -d:$profiler $script: $!";
812 sub by_time { $times->{$b} <=> $times->{$a} }
813 sub by_ctime { $ctimes->{$b} <=> $ctimes->{$a} }
814 sub by_calls { $calls->{$b} <=> $calls->{$a} }
815 sub by_alpha { $names->{$a} cmp $names->{$b} }
816 sub by_avgcpu { $persecs->{$b} <=> $persecs->{$a} }
820 Total Elapsed Time = @>>>>>>> Seconds
821 (($rrun_rtime - $overhead - $over_rtime * $total_marks/$over_tests/2) / $hz)
822 @>>>>>>>>>> Time = @>>>>>>> Seconds
826 %Time ExclSec CumulS #Calls sec/call Csec/c Name
830 ^>>> ^>>>> ^>>>>> ^>>>>> ^>>>>> ^>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
831 $pcnt, $secs, $csecs, $ncalls, $percall, $cpercall, $name
836 close OUT or die "Can't close $file: $!";
837 chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
838 exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';