package Devel::SmallProf; # To help the CPAN indexer to identify us $Devel::SmallProf::VERSION = '0.9'; package DB; require 5.000; use Time::HiRes 'time'; use strict; BEGIN { $DB::drop_zeros = 0; $DB::profile = 1; if (-e '.smallprof') { do '.smallprof'; } $DB::prevf = ''; $DB::prevl = 0; my($diff,$cdiff); my($testDB) = sub { my($pkg,$filename,$line) = caller; $DB::profile || return; %DB::packages && !$DB::packages{$pkg} && return; }; # "Null time" compensation code $DB::nulltime = 0; for (1..100) { my($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; &$testDB; ($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; $diff = $DB::done - $DB::start; $DB::nulltime += $diff; } $DB::nulltime /= 100; my($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; } sub DB { my($pkg,$filename,$line) = caller; $DB::profile || return; %DB::packages && !$DB::packages{$pkg} && return; my($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; # Now save the _< array for later reference. If we don't do this here, # evals which do not define subroutines will disappear. no strict 'refs'; $DB::listings{$filename} = \@{"main::_<$filename"} if defined(@{"main::_<$filename"}); use strict 'refs'; my($delta); $delta = $DB::done - $DB::start; $delta = ($delta > $DB::nulltime) ? $delta - $DB::nulltime : 0; $DB::profiles{$filename}->[$line]++; $DB::times{$DB::prevf}->[$DB::prevl] += $delta; $DB::ctimes{$DB::prevf}->[$DB::prevl] += ($DB::cdone - $DB::cstart); ($DB::prevf, $DB::prevl) = ($filename, $line); ($u,$s,$cu,$cs) = times; $DB::cstart = $u+$s+$cu+$cs; $DB::start = time; } END { # Get time on last line executed. my($u,$s,$cu,$cs) = times; $DB::cdone = $u+$s+$cu+$cs; $DB::done = time; my($delta); $delta = $DB::done - $DB::start; $delta = ($delta > $DB::nulltime) ? $delta - $DB::nulltime : 0; $DB::times{$DB::prevf}->[$DB::prevl] += $delta; $DB::ctimes{$DB::prevf}->[$DB::prevl] += ($DB::cdone - $DB::cstart); # Now write out the results. open(OUT,">smallprof.out"); select OUT; my($i,$stat,$time,$ctime,$line,$file,$page); $page = 1; format OUT_TOP= @|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| '================ SmallProf version '.$Devel::SmallProf::VERSION.' ================' @|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| Page @<< "Profile of $file",$page++ ================================================================= count wall tm cpu time line . format OUT= @######## @.###### @.###### @####:^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $stat,$time,$ctime,$i,$line . foreach $file (sort keys %DB::profiles) { $- = 0; if (defined($DB::listings{$file})) { $i = -1; foreach $line (@{$DB::listings{$file}}) { ++$i or next; if (defined($line)) { chomp($line); } else { $line = ''; } $stat = $DB::profiles{$file}->[$i] || 0 or !$DB::drop_zeros or next; $time = defined($DB::times{$file}->[$i]) ? $DB::times{$file}->[$i] : 0; $ctime = defined($DB::ctimes{$file}->[$i]) ? $DB::ctimes{$file}->[$i] : 0; write OUT; } } else { $line = "The code for $file is not in the symbol table."; for ($i=1; $i <= $#{$DB::profiles{$file}}; $i++) { next unless ($stat = $DB::profiles{$file}->[$i] || 0 or !$DB::drop_zeros); $time = defined($DB::times{$file}->[$i]) ? $DB::times{$file}->[$i] : 0; $ctime = defined($DB::ctimes{$file}->[$i]) ? $DB::ctimes{$file}->[$i] : 0; write OUT; } } } close OUT; } sub sub { no strict 'refs'; goto &$DB::sub unless $DB::profile; if (defined($DB::sub{$DB::sub})) { my($m,$s) = ($DB::sub{$DB::sub} =~ /.+(?=:)|[^:-]+/g); $DB::profiles{$m}->[$s]++; $DB::listings{$m} = \@{"main::_<$m"} if defined(@{"main::_<$m"}); } goto &$DB::sub; } 1; __END__ =head1 NAME Devel::SmallProf - per-line Perl profiler =head1 SYNOPSIS perl5 -d:SmallProf test.pl =head1 DESCRIPTION The Devel::SmallProf profiler is focused on the time taken for a program run on a line-by-line basis. It is intended to be as "small" in terms of impact on the speed and memory usage of the profiled program as possible and also in terms of being simple to use. Those statistics are placed in the file F in the following format: