2 eval 'exec perl -S $0 "$@"'
7 my $VERSION = '20050603.00';
12 dprofpp - display perl profile data
16 dprofpp [B<-a>|B<-z>|B<-l>|B<-v>|B<-U>] [B<-d>] [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>] [B<-G> <regexp> [B<-P>]] [B<-f> <regexp>] [profile]
18 dprofpp B<-T> [B<-F>] [B<-g subroutine>] [profile]
20 dprofpp B<-t> [B<-F>] [B<-g subroutine>] [profile]
22 dprofpp B<-G> <regexp> [B<-P>] [profile]
24 dprofpp B<-p script> [B<-Q>] [other opts]
26 dprofpp B<-V> [profile]
30 The I<dprofpp> command interprets profile data produced by a profiler, such
31 as the Devel::DProf profiler. Dprofpp will read the file F<tmon.out> and
32 display the 15 subroutines which are using the most time. By default
33 the times for each subroutine are given exclusive of the times of their
36 To profile a Perl script run the perl interpreter with the B<-d> switch. So
37 to profile script F<test.pl> with Devel::DProf use the following:
39 $ perl5 -d:DProf test.pl
41 Then run dprofpp to analyze the profile. The output of dprofpp depends
42 on the flags to the program and the version of Perl you're using.
45 Total Elapsed Time = 1.67 Seconds
46 User Time = 0.61 Seconds
48 %Time Seconds #Calls sec/call Name
49 52.4 0.320 2 0.1600 main::foo
50 45.9 0.280 200 0.0014 main::bar
51 0.00 0.000 1 0.0000 DynaLoader::import
52 0.00 0.000 1 0.0000 main::baz
54 The dprofpp tool can also run the profiler before analyzing the profile
55 data. The above two commands can be executed with one dprofpp command.
57 $ dprofpp -u -p test.pl
59 Consult L<Devel::DProf/"PROFILE FORMAT"> for a description of the raw profile.
69 Percentage of time spent in this routine.
73 Number of calls to this routine.
77 Average number of seconds per call to this routine.
85 Time (in seconds) spent in this routine and routines called from it.
89 Time (in seconds) spent in this routine (not including those called
94 Average time (in seconds) spent in each call of this routine
95 (including those called from it).
105 Sort alphabetically by subroutine names.
109 Reverse whatever sort is used
113 Count timing for autoloaded subroutine as timing for C<*::AUTOLOAD>.
114 Otherwise the time to autoload it is counted as time of the subroutine
115 itself (there is no way to separate autoload time from run time).
117 This is going to be irrelevant with newer Perls. They will inform
118 C<Devel::DProf> I<when> the C<AUTOLOAD> switches to actual subroutine,
119 so a separate statistics for C<AUTOLOAD> will be collected no matter
120 whether this option is set.
124 Count anonymous subroutines defined in the same package separately.
128 (default) Display all subroutine times exclusive of child subroutine times.
132 Force the generation of fake exit timestamps if dprofpp reports that the
133 profile is garbled. This is only useful if dprofpp determines that the
134 profile is garbled due to missing exit timestamps. You're on your own if
135 you do this. Consult the BUGS section.
139 Display all subroutine times inclusive of child subroutine times.
143 Sort by number of calls to the subroutines. This may help identify
144 candidates for inlining.
148 Show only I<cnt> subroutines. The default is 15.
152 Tells dprofpp that it should profile the given script and then interpret its
153 profile data. See B<-Q>.
157 Used with B<-p> to tell dprofpp to quit after profiling the script, without
158 interpreting the data.
162 Do not display column headers.
166 Display elapsed real times rather than user+system times.
170 Display system times rather than user+system times.
174 Display subroutine call tree to stdout. Subroutine statistics are
179 Display subroutine call tree to stdout. Subroutine statistics are not
180 displayed. When a function is called multiple consecutive times at the same
181 calling level then it is displayed once with a repeat count.
185 Display I<merged> subroutine call tree to stdout. Statistics are
186 displayed for each branch of the tree.
188 When a function is called multiple (I<not necessarily consecutive>)
189 times in the same branch then all these calls go into one branch of
190 the next level. A repeat count is output together with combined
191 inclusive, exclusive and kids time.
193 Branches are sorted with regard to inclusive time.
197 Do not sort. Display in the order found in the raw profile.
201 Display user times rather than user+system times.
205 Print dprofpp's version number and exit. If a raw profile is found then its
206 XS_VERSION variable will be displayed, too.
210 Sort by average time spent in subroutines during each call. This may help
211 identify candidates for inlining.
215 (default) Sort by amount of user+system time used. The first few lines
216 should show you which subroutines are using the most time.
218 =item B<-g> C<subroutine>
220 Ignore subroutines except C<subroutine> and whatever is called from it.
224 Aggregate "Group" all calls matching the pattern together.
225 For example this can be used to group all calls of a set of packages
227 -G "(package1::)|(package2::)|(package3::)"
229 or to group subroutines by name:
235 Used with -G to aggregate "Pull" together all calls that did not match -G.
239 Filter all calls matching the pattern.
243 Display brief help and exit.
247 Display long help and exit.
253 The environment variable B<DPROFPP_OPTS> can be set to a string containing
254 options for dprofpp. You might use this if you prefer B<-I> over B<-E> or
255 if you want B<-F> on all the time.
257 This was added fairly lazily, so there are some undesirable side effects.
258 Options on the commandline should override options in DPROFPP_OPTS--but
259 don't count on that in this version.
263 Applications which call _exit() or exec() from within a subroutine
264 will leave an incomplete profile. See the B<-F> option.
266 Any bugs in Devel::DProf, or any profiler generating the profile data, could
267 be visible here. See L<Devel::DProf/BUGS>.
269 Mail bug reports and feature requests to the perl5-porters mailing list at
270 F<E<lt>perl5-porters@perl.orgE<gt>>. Bug reports should include the
271 output of the B<-V> option.
275 dprofpp - profile processor
276 tmon.out - raw profile
280 L<perl>, L<Devel::DProf>, times(2)
286 dprofpp [options] [profile]
288 -A Count autoloaded to *AUTOLOAD
289 -a Sort by alphabetic name of subroutines.
291 -E Sub times are reported exclusive of child times. (default)
292 -f Filter all calls mathcing the pattern.
293 -G Group all calls matching the pattern together.
294 -g subr Count only those who are SUBR or called from SUBR
295 -H Display long manual page.
296 -h Display this short usage message.
297 -I Sub times are reported inclusive of child times.
298 -l Sort by number of calls to subroutines.
299 -O cnt Specifies maximum number of subroutines to display.
300 -P Used with -G to pull all other calls together.
301 -p script Specifies name of script to be profiled.
302 -Q Used with -p to indicate the dprofpp should quit
303 after profiling the script, without interpreting the data.
304 -q Do not print column headers.
305 -R Count anonyms separately even if from the same package
306 -r Use real elapsed time rather than user+system time.
307 -S Create statistics for all the depths
308 -s Use system time rather than user+system time.
310 -t Show call tree, compressed.
311 -U Do not sort subroutines.
312 -u Use user time rather than user+system time.
313 -V Print dprofpp's version.
314 -v Sort by average amount of time spent in subroutines.
315 -z Sort by user+system time spent in subroutines. (default)
319 use Getopt
::Std
'getopts';
320 use Config
'%Config';
323 my $options = 'O:g:G:Pf:dlzaAvuTtqrRsUFEIp:QVShH';
325 $Monfile = 'tmon.out';
326 if( exists $ENV{DPROFPP_OPTS
} ){
328 @ARGV = split( ' ', $ENV{DPROFPP_OPTS
} );
331 # there was a filename.
337 getopts
( $options ) or die "Try 'dprofpp -h' for help.\n";
339 # there was a filename, it overrides any earlier name.
343 if ( defined $opt_h ) {
347 if ( defined $opt_H ) {
349 Pod
::Usage
::pod2usage
( {-verbose
=> 2, -input
=> $0 } );
353 if( defined $opt_V ){
355 print "$0 version: $VERSION\n";
356 open( $fh, "<$Monfile" ) && do {
357 local $XS_VERSION = 'early';
360 print "XS_VERSION: $XS_VERSION\n";
366 $sort = 'by_ctime' if defined $opt_I;
367 $sort = 'by_calls' if defined $opt_l;
368 $sort = 'by_alpha' if defined $opt_a;
369 $sort = 'by_avgcpu' if defined $opt_v;
374 $incl_excl = 'Exclusive';
375 $incl_excl = 'Inclusive' if defined $opt_I;
376 $whichtime = 'User+System';
377 $whichtime = 'System' if defined $opt_s;
378 $whichtime = 'Real' if defined $opt_r;
379 $whichtime = 'User' if defined $opt_u;
381 if( defined $opt_p ){
383 my $startperl = $Config{'startperl'};
385 $startperl =~ s/^#!//; # remove shebang
386 run_profiler
( $opt_p, $prof, $startperl );
387 $Monfile = 'tmon.out'; # because that's where it is
388 exit(0) if defined $opt_Q;
390 elsif( defined $opt_Q ){
391 die "-Q is meaningful only when used with -p\n";
396 my $monout = $Monfile;
399 local $times = {}; # times in hz
400 local $ctimes = {}; # Cumulative times in hz
402 local $persecs = {}; # times in seconds
404 local $runtime; # runtime in seconds
407 local $rrun_utime = 0; # user time in hz
408 local $rrun_stime = 0; # system time in hz
409 local $rrun_rtime = 0; # elapsed run time in hz
410 local $rrun_ustime = 0; # user+system time in hz
412 local $deep_times = {count
=> 0 , kids
=> {}, incl_time
=> 0};
413 local $time_precision = 2;
416 open( $fh, "<$monout" ) || die "Unable to open $monout\n";
420 $rrun_ustime = $rrun_utime + $rrun_stime;
427 parsestack
( $fh, $names, $calls, $times, $ctimes, $idkeys );
431 for(my $i = 0;$i < @
$idkeys - 2;){
433 if($key =~ /$opt_f/){
434 splice(@
$idkeys, $i, 1);
435 $runtime -= $$times{$key};
443 group
($names, $calls, $times, $ctimes, $idkeys );
446 settime
( \
$runtime, $hz ) unless $opt_g;
448 exit(0) if $opt_T || $opt_t;
451 percalc
( $calls, ($opt_I ?
$ctimes : $times), $persecs, $idkeys );
454 @a = sort $sort @
$idkeys;
460 display
( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt, $a,
465 my ($names, $calls, $times, $ctimes, $idkeys ) = @_;
466 print "Option G Grouping: [$opt_G]\n";
467 # create entries to store grouping
468 $$names{$opt_G} = $opt_G;
471 $$ctimes{$opt_G} = 0;
472 $$idkeys[@
$idkeys] = $opt_G;
473 # Sum calls for the grouping
477 $$names{$other} = $other;
480 $$ctimes{$other} = 0;
481 $$idkeys[@
$idkeys] = $other;
484 for(my $i = 0;$i < @
$idkeys - 2;){
486 if($key =~ /$opt_G/){
487 $$calls{$opt_G} += $$calls{$key};
488 $$times{$opt_G} += $$times{$key};
489 $$ctimes{$opt_G} += $$ctimes{$key};
490 splice(@
$idkeys, $i, 1);
494 $$calls{$other} += $$calls{$key};
495 $$times{$other} += $$times{$key};
496 $$ctimes{$other} += $$ctimes{$key};
497 splice(@
$idkeys, $i, 1);
503 print "Grouping [$opt_G] Calls: [$$calls{$opt_G}]\n".
504 "Grouping [$opt_G] Times: [$$times{$opt_G}]\n".
505 "Grouping [$opt_G] IncTimes: [$$ctimes{$opt_G}]\n";
508 # Sets $runtime to user, system, real, or user+system time. The
509 # result is given in seconds.
512 my( $runtime, $hz ) = @_;
517 $$runtime = ($rrun_rtime - $overhead)/$hz;
520 $$runtime = ($rrun_stime - $overhead)/$hz;
523 $$runtime = ($rrun_utime - $overhead)/$hz;
526 $$runtime = ($rrun_ustime - $overhead)/$hz;
528 $$runtime = 0 unless $$runtime > 0;
531 sub exclusives_in_tree
{
532 my( $deep_times ) = @_;
535 # When summing, take into account non-rounded-up kids time.
536 for $kid (keys %{$deep_times->{kids
}}) {
537 $kids_time += $deep_times->{kids
}{$kid}{incl_time
};
539 $kids_time = 0 unless $kids_time >= 0;
540 $deep_times->{excl_time
} = $deep_times->{incl_time
} - $kids_time;
541 $deep_times->{excl_time
} = 0 unless $deep_times->{excl_time
} >= 0;
542 for $kid (keys %{$deep_times->{kids
}}) {
543 exclusives_in_tree
($deep_times->{kids
}{$kid});
545 $deep_times->{incl_time
} = 0 unless $deep_times->{incl_time
} >= 0;
546 $deep_times->{kids_time
} = $kids_time;
549 sub kids_by_incl
{ $kids{$b}{incl_time
} <=> $kids{$a}{excl_time
}
553 my( $deep_times, $name, $level ) = @_;
554 exclusives_in_tree
($deep_times);
559 if (%{$deep_times->{kids
}}) {
560 $time = sprintf '%.*fs = (%.*f + %.*f)',
561 $time_precision, $deep_times->{incl_time
}/$hz,
562 $time_precision, $deep_times->{excl_time
}/$hz,
563 $time_precision, $deep_times->{kids_time
}/$hz;
565 $time = sprintf '%.*f', $time_precision, $deep_times->{incl_time
}/$hz;
567 print ' ' x
(2*$level), "$name x $deep_times->{count} \t${time}s\n"
568 if $deep_times->{count
};
570 for $kid (sort kids_by_incl
%{$deep_times->{kids
}}) {
571 display_tree
( $deep_times->{kids
}{$kid}, $kid, $level + 1 );
575 # Report the times in seconds.
577 my( $runtime, $hz, $names, $calls, $times, $ctimes, $cnt,
578 $idkeys, $deep_times ) = @_;
579 my( $x, $key, $s, $cs );
580 #format: $ncalls, $name, $secs, $percall, $pcnt
583 display_tree
( $deep_times, 'toplevel', -1 )
585 for( $x = 0; $x < @
$idkeys; ++$x ){
586 $key = $idkeys->[$x];
587 $ncalls = $calls->{$key};
588 $name = $names->{$key};
589 $s = $times->{$key}/$hz;
590 $secs = sprintf("%.3f", $s );
591 $cs = $ctimes->{$key}/$hz;
592 $csecs = sprintf("%.3f", $cs );
593 $percall = sprintf("%.4f", $s/$ncalls );
594 $cpercall = sprintf("%.4f", $cs/$ncalls );
595 $pcnt = sprintf("%.2f",
596 $runtime?
((($opt_I ?
$csecs : $secs) / $runtime) * 100.0): 0 );
598 $pcnt = $secs = $ncalls = $percall = "";
599 write while( length $name );
606 my ($source, $dest) = @_;
608 for my $kid_name (keys %$source) {
609 my $source_kid = delete $source->{$kid_name};
611 if (my $dest_kid = $dest->{$kid_name}) {
612 $dest_kid->{count
} += $source_kid->{count
};
613 $dest_kid->{incl_time
} += $source_kid->{incl_time
};
614 move_keys
($source_kid->{kids
},$dest_kid->{kids
});
616 $dest->{$kid_name} = $source_kid;
622 my ($curdeep_times, $name, $t) = @_;
623 if ($name ne $curdeep_times->[-1]{name
} and $opt_A) {
624 $name = $curdeep_times->[-1]{name
};
626 die "Shorted?!" unless @
$curdeep_times >= 2;
627 my $entry = $curdeep_times->[-2]{kids
}{$name} ||= {
632 # Now transfer to the new node (could not do earlier, since name can change)
634 $entry->{incl_time
} += $t - $curdeep_times->[-1]{enter_stamp
};
636 move_keys
($curdeep_times->[-1]->{kids
},$entry->{kids
});
642 my( $fh, $names, $calls, $times, $ctimes, $idkeys ) = @_;
644 my( $t, $syst, $realt, $usert );
645 my( $x, $z, $c, $id, $pack );
652 # remember last call depth and function name
659 my $in_level = not defined $opt_g; # Level deep in report grouping
660 my $curdeep_times = [$deep_times];
663 if ( $opt_u ) { $over_per_call = $over_utime }
664 elsif( $opt_s ) { $over_per_call = $over_stime }
665 elsif( $opt_r ) { $over_per_call = $over_rtime }
666 else { $over_per_call = $over_utime + $over_stime }
667 $over_per_call /= 2*$over_tests; # distribute over entry and exit
675 ($dir, $id, $pack, $name) = split;
676 if ($opt_R and ($name =~ /(?:::)?(__ANON__|END)$/)) {
679 $cv_hash{$id} = "$pack\::$name";
682 ($dir, $usert, $syst, $realt, $name) = split;
686 $syst = $stack[-1][0] if scalar @stack;
689 #warn("Inserted exit for $stack[-1][0].\n")
691 if (defined $realt) { # '+ times nam' '- times nam' or '@ incr'
692 if ( $opt_u ) { $t = $usert }
693 elsif( $opt_s ) { $t = $syst }
694 elsif( $opt_r ) { $t = $realt }
695 else { $t = $usert + $syst }
696 $t += $ot, next if $dir eq '@'; # Increments there
698 # "- id" or "- & name"
699 $name = defined $syst ?
$syst : $cv_hash{$usert};
702 next unless $in_level or $name eq $opt_g;
703 if ( $dir eq '-' or $dir eq '*' ) {
704 my $ename = $dir eq '*' ?
$stack[-1][0] : $name;
705 $overhead += $over_per_call;
706 if ($name eq "Devel::DProf::write") {
707 $overhead += $t - $dprof_stamp;
709 } elsif (defined $opt_g and $ename eq $opt_g) {
712 add_to_tree
($curdeep_times, $ename,
713 $t - $overhead) if $opt_S;
714 exitstamp
( \
@stack, \
@tstack,
716 $times, $ctimes, $name, \
$in, $tab,
717 $curdeep_times, \
%outer );
719 next unless $in_level or $name eq $opt_g;
720 if( $dir eq '+' or $dir eq '*' ){
721 if ($name eq "Devel::DProf::write") {
724 } elsif (defined $opt_g and $name eq $opt_g) {
727 $overhead += $over_per_call;
729 print ' ' x
$in, "$name\n";
733 # suppress output on same function if the
734 # same calling level is called.
735 if ($l_in == $in and $l_name eq $name) {
738 $repstr = ' ('.++$repcnt.'x)'
740 print ' ' x
$l_in, "$l_name$repstr\n"
749 if( ! defined $names->{$name} ){
750 $names->{$name} = $name;
752 $ctimes->{$name} = 0;
753 push( @
$idkeys, $name );
757 push @
$curdeep_times, { kids
=> {},
759 enter_stamp
=> $t - $overhead,
761 $x = [ $name, $t - $overhead ];
764 # my children will put their time here
766 } elsif ($dir ne '-'){
767 die "Bad profile: $_";
771 $repstr = ' ('.++$repcnt.'x)' if $repcnt;
772 print ' ' x
$l_in, "$l_name$repstr\n";
775 while (my ($key, $count) = each %outer) {
777 warn "$key has $count unstacked calls in outer\n";
782 warn "Garbled profile is missing some exit time stamps:\n";
783 foreach $x (@stack) {
786 die "Try rerunning dprofpp with -F.\n";
787 # I don't want -F to be default behavior--yet
791 warn( "Faking " . scalar( @stack ) . " exit timestamp(s).\n");
792 foreach $x ( reverse @stack ){
794 exitstamp
( \
@stack, \
@tstack,
795 $t - $overhead, $times,
796 $ctimes, $name, \
$in, $tab,
797 $curdeep_times, \
%outer );
798 add_to_tree
($curdeep_times, $name,
804 if (defined $opt_g) {
805 $runtime = $ctimes->{$opt_g}/$hz;
806 $runtime = 0 unless $runtime > 0;
811 my($stack, $tstack, $t, $times, $ctimes, $name, $in, $tab, $deep, $outer) = @_;
816 die "Garbled profile, missing an enter time stamp";
818 if( $x->[0] ne $name and $opt_G and ($name =~ /$opt_G/)){
819 if ($x->[0] =~ /(?:::)?AUTOLOAD$/) {
824 warn( "Garbled profile, faking exit timestamp:\n\t$name => $x->[0].\n");
827 foreach $z (@stack, $x) {
830 die "Garbled profile, unexpected exit time stamp";
833 if( $opt_T || $opt_t ){
837 $c = pop( @
$tstack );
838 # total time this func has been active
840 $ctimes->{$name} += $z
841 unless --$outer->{$name};
842 $times->{$name} += $z - $c;
843 # pass my time to my parent
845 $c = pop( @
$tstack );
846 push( @
$tstack, $c + $z );
854 if( ! /^#fOrTyTwO$/ ){
855 die "Not a perl profile";
862 $over_tests = 1 unless $over_tests;
863 $time_precision = length int ($hz - 1); # log ;-)
867 # Report avg time-per-function in seconds
869 my( $calls, $times, $persecs, $idkeys ) = @_;
870 my( $x, $t, $n, $key );
872 for( $x = 0; $x < @
$idkeys; ++$x ){
873 $key = $idkeys->[$x];
875 $t = $times->{$key} / $hz;
876 $persecs->{$key} = $t ?
$t / $n : 0;
881 # Runs the given script with the given profiler and the given perl.
884 my $profiler = shift;
885 my $startperl = shift;
886 my @script_parts = split /\s+/, $script;
888 system $startperl, "-d:$profiler", @script_parts;
890 my $cmd = join ' ', @script_parts;
891 die "Failed: $startperl -d:$profiler $cmd: $!";
896 sub by_time
{ $times->{$b} <=> $times->{$a} }
897 sub by_ctime
{ $ctimes->{$b} <=> $ctimes->{$a} }
898 sub by_calls
{ $calls->{$b} <=> $calls->{$a} }
899 sub by_alpha
{ $names->{$a} cmp $names->{$b} }
900 sub by_avgcpu
{ $persecs->{$b} <=> $persecs->{$a} }
902 sub rby_time
{ $times->{$a} <=> $times->{$b} }
903 sub rby_ctime
{ $ctimes->{$a} <=> $ctimes->{$b} }
904 sub rby_calls
{ $calls->{$a} <=> $calls->{$b} }
905 sub rby_alpha
{ $names->{$b} cmp $names->{$a} }
906 sub rby_avgcpu
{ $persecs->{$a} <=> $persecs->{$b} }
910 Total Elapsed Time
= @
>>>>>>> Seconds
911 (($rrun_rtime - $overhead) / $hz)
912 @
>>>>>>>>>> Time
= @
>>>>>>> Seconds
916 %Time ExclSec CumulS
#Calls sec/call Csec/c Name
920 my $fmt = ' ^>>> ^>>>> ^>>>>> ^>>>>> ^>>>>> ^>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
921 if (-t STDOUT
and defined $stty and my ($cols) = `$stty -a` =~ /\bcolumns\s+(\d+)/)
923 $fmt .= '<' x
($cols - length $fmt) if $cols > 80;
926 eval "format STAT = \n$fmt" . '
927 $pcnt, $secs, $csecs, $ncalls, $percall, $cpercall, $name