The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Devel::CallStack - record the calling stacks

SYNOPSIS

    perl -d:CallStack ...

DESCRIPTION

The Devel::CallStack is meant for code developers wondering why their code is running so slow. One possible reason is simply too many subroutine or method calls since they are not cheap in Perl.

The Devel::CallStack records the calling stacks, how many times each calling stack is being called. By default the results are written to a file called callstack.out.

NOTE: recording the callstacks is a very heavy operation which slows down the execution of your code easily ten-fold or more: do not attempt any other code timing or profiling at the same time. The gathered information is useful in conjunction with other profiling tools such as Devel::DProf.

MOTIVATION

I got frustrated by Devel::DProf results that looked not unlike this:

  Total Elapsed Time = 1.892063 Seconds
    User+System Time = 1.742063 Seconds
  Exclusive Times
  %Time ExclSec CumulS #Calls sec/call Csec/c  Name
   13.8   0.241  0.426   2170   0.0001 0.0002  Foo::_id
   10.3   0.181  0.181   1747   0.0001 0.0001  Foo::Map::has
   9.18   0.160  0.434      3   0.0532 0.1448  main::BEGIN
   8.21   0.143  0.143   5205   0.0000 0.0000  Foo::Map::_has
   7.46   0.130  0.611      1   0.1299 0.6112  Foo::Map::new
   ...

I obviously needed to try cutting down the number of Foo::_id calls (not to mention the number of Foo::Map::_has and Foo::Map::_has calls), but the problem was that Foo::_id was being called from multiple places, there were more than one possible "hot path" that I needed to locate and "cool down".

EXAMPLE

For this file, code.pl:

    sub foo { bar(@_) }
    sub bar { zog(@_) if $_[0] % 7 }
    sub zog { }
    for (my $i = 0; $i < 1e3; $i++) {
        $i % 5 ? foo($i) : bar($i);
    }

running perl -d:CallStack code.pl will result in:

    main::bar 1 200
    main::bar,main::zog 2 171
    main::foo 1 800
    main::foo,main::bar 2 800
    main::foo,main::bar,main::zog 3 686

Meaning that the callstack main::bar was called 200 times, which makes sense since every fifth call out of 1000 should have been made to bar(). On the other hand, the callstack main::bar,main::zog was reached 171 times, which is the number of integers between 0 and 999 (inclusive) that are evenly divisible both by five and seven. The numbers in the second column are the callstack depths (the number of commas plus one).

PARAMETERS

Parameters are given in the command line after the -d:Callstack and a =:

    perl -d:CallStack=...

The available parameters are as follows:

Out

The results are written by default to a file called callstack.out. This can be changed either with

    perl -d:CallStack=out=filename

or

    perl -d:CallStack=out=stdout
    perl -d:CallStack=out=stderr

which will output to a file called filename or the standard output or the standard error, respectively.

Depth

By default the calling stacks are walked all the way back to the beginning. This may be very expensive if the calling stacks are deep. To limit the number of frames walked back, supply the depth parameter:

   perl -d:CallStack=depth=N

or just

   perl -d:CallStack=N

Using callstack depth two for for our example:

    main::bar 1 200
    main::bar,main::zog 2 857
    main::foo 1 800
    main::foo,main::bar 2 800

Using the depth of one (or zero) gives the number of times each subroutine was called:

    main::bar 1 1000
    main::foo 1 800
    main::zog 1 857

Reverse

By default the callstacks go from left to right, that is, the callers are on the left and the callees are on the right, the time flows from left to right. With the reverse parameter you can flip the order, which may fit your brain better. For our example:

    main::bar 1 200
    main::bar,main::foo 2 800
    main::foo 1 800
    main::zog,main::bar 2 171
    main::zog,main::bar,main::foo 3 686

Full

By default only the names of the called subroutines (methods) are recorded. To record also the filename and (calling) linenumber in the file, use the full parameter:

   perl -d:CallStack=full

The filename and the linenumber are prepended to the subname, all concatenated with single colons, for our example:

    code.pl:1:main::foo 1 800
    code.pl:2:main::bar 1 200
    code.pl:5:main::bar,code.pl:3:main::zog 2 171
    code.pl:5:main::foo,code.pl:1:main::bar,code.pl:3:main::zog 3 686
    code.pl:5:main::foo,code.pl:2:main::bar 2 800

Append

Normally the output file is overwritten. To append instead:

   perl -d:CallStack=append

In

Normally the statistics are started from scratch for each run. To read in initial statistics from a file:

   perl -d:CallStack=in=filename

The =filename part is optional, the default filename is callstack.out. The input data needs to be in the same format (depth, full, reverse) as the current settings.

Combining parameters

To use several parameters at the same time, combine the parameters by using a comma:

   perl -d:CallStack=3,out=my.out,full

If you combine the append and in parameters, you get cumulative statistics.

UTILITY FUNCTIONS

To get a copy of the statistics accumulated so far, call

        my %C = Devel::CallStack::get();

The keys of the hash are the callstacks as comma-concatenated strings, and the values are the number of calls.

To set the statistics, call

        Devel::CallStack::set(%C).

To clear the statistics, simply call Devel::CallStack::set() with no argument.

To write out the statistics accumulated so far, call

        Devel::CallStack::write()

This overwrites the existing output file (either callstack.out or whatever you used for the out parameter or the standard output or error streams) unless the append parameter is used. You need to do any needed file renaming yourself. write() is used by Devel::CallStack itself to output the statistics at the end of a run, by calling it from its END block.

To read in the statistics accumulated from a file, call

        Devel::CallStack::read("filename")

This merges in the data instead of replacing. If you want to replace the data, call set() yourself. read() is used by the in parameter. The input data needs to be in the same format (depth, full, reverse) as the current settings.

POSSIBILITIES

Now you've run your code with Devel::CallStack. Now what?

If you see that a method or a subroutine is called several thousand times while the upper layers are called only a few times:

  • First of all try your code with different input and with different amount of input: how does the number of calls vary? Linear, logarithmic, squared, cubed, random? Have you picked the right algorithm? You are not reimplementing something from the Perl core that might have either a better algorithm or simply a faster implementation? (For example sort().)

  • You may manually inline the method or subroutine code to its callers. The downsides include harder maintenance (remember to document/comment the inlining both to the callers and to the original code), the upsides include faster execution. Maybe you can somehow automate the inlining, for example via Perl source filters?

  • You may manually or (preferably) automatically cache the computation, whenever reasonable and possible. Use for example the Memoize module. The downsides include more memory usage, upsides include faster execution.

If you see some deep code paths having only a few callers (or maybe even just a single one):

  • Maybe you have several layers of subroutines calling each other always along the same paths - you could possibly collapse/inline several levels of these subroutines into fewer ones, or even just a single one. If you still need to have some of the intermediate functions separately, you may consider maintaining separate functions for those, but remember to document/comment the fact profusely.

KNOWN PROBLEMS

Devel::CallStack unfortunately works only in 5.6.1 or later Perls, there is something different with the -d:Xyz option of older Perls that breaks Devel::CallStack, one would get something like this:

   syntax error at code.pl line 0, near "use Devel::CallStack="

ACKNOWLEDGEMENTS

Sébastien Aperghis-Tramoni for bravely testing the code in Jaguar.

SEE ALSO

"caller" in perlfunc; Devel::CallerItem, Devel::DumpStack, Devel::StackTrace, for alternative views of the call stack; Devel::DProf and Devel::SmallProf for time-based profiling; Devel::Cover for coverage.

AUTHOR AND COPYRIGHT

Jarkko Hietaniemi <jhi@iki.fi> 2004-2005

LICENSE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.