The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

calltree - Who called whom

SYNOPSIS

    calltree    --exclude=<pkg1>...<pkgN> --include=<pkg3>...<pkgQ>
                --noempty
                [ --reportfunc file ]
                [ -Iinc/path1 -Iinc/path2 -I...] [ -Mmodule1 -Mmodule2 -M...]
                [ -e <CODE> | script.pl ]

    calltree    --chkfunc=<fnc1>...<funcN>
                [ -Iinc/path1 -Iinc/path2 -M...] [ -Mmodule1 -Mmodule2 -M...]
                [ -e <CODE> | script.pl ]

OPTIONS

  • --exclude LIST

    A comma-separated list of regular expressions. Functions matching any of the expressions are excluded from the report.

  • --include LIST

    A comma-separated list of regular expressions. Functions matching any of the expressions are always included from the report (even when they match one in --exclude.

  • --chkfunc LIST

    A comma-separated list of regular expressions. All functions matching one of the expression are checked whether they are called at all. This function cannot be used together with --exclude or --include.

  • --noempty

    Exclude functions from the generated report that do not call any other functions.

  • --reportfunc FILE

    Load FILE and use the function 'Devel::Calltree::print_report' defined therein to create the report instead of the built-in report functions.

  • -Ipath

    The same as perl's -I. This is used to add directories to @INC.

  • -Mmodule

    The same as perl's -M. This runs the code after useing the module module. Can be used to print the calltree of a module:

        calltree -MMy::Module
  • -e 'CODE'

    The same as perl's -e. This is used to pass the code that shall be inspected.

DESCRIPTION

calltree inspects the OP-tree of a program or module after compilation is done and creates a report showing which method and function has been called by whom.

The default output is pretty straightforward. When not using the --chkfunc switch, it looks like this:

    Package::func  (/path/to/Package.pm)
      method   'some_method'                                      (68)
      function 'Pkg::function'                                    (70)
      
    Package::nest::otherfunc  (/path/to/Package/nest.pm)
      method   'foobar'                                           (10)
    
    ...

    __MAIN__ (-)
      function 'Package::func::func'                              (3)

It begins with the fully qualified function followed by the path to the file in which this function resides. After that a list of function and method calls follows. The line where this call happens is prepended.

The last in the list is always __MAIN__ which is the report for what happens in package main. This doesn't necessarily exist (e.g. when you only inspect a module).

In --chkfunc mode, output looks like this:

    calltree --chkfunc=foo,bar -e 'sub foo {1} print foo()'
    These patterns did not match any called function:
      bar

    These functions were called:
      function main::foo        from __MAIN__           at line 1

PROVIDING YOUR OWN REPORT FUNCTIONS

With the help of the --reportfunc switch, you can tell calltree to use a different function for outputting the report. The argument to this switch must be a file that contains the function Devel::Calltree::print_report. The file itself must additionally return a true value.

A skeleton of this file therefore must look like this:

    sub Devel::Calltree::print_report {
        ...
    }
    
    "MJD kindly reminded calltree's author of putting in this feature";

Devel::Calltree::print_report

This function will receive exactly one argument, namely a hash-reference which is additionally blessed in some fancy way. The keys of this hash are the various functions that calltree was able to find in your code. The value is an array-reference holding Devel::Calltree::Func objects where each object represents exactly one call to a function or method that was done from within the given function.

The hash-reference passed to Devel::Calltree::print_report overloads the @{} operator. That means that in order to iterate over all functions in your program, you can simply write:

    sub Devel::Calltree::print_report {
        my $calls = shift;
        for my $func (@$calls) {
            print $func, "\n";
            ...
        }
    }

This is often more convenient than writing

    for my $func (keys %$calls) {

because @$calls will return the list of functions sorted by package and then by function names.

Now that you are iterating over all exiting functions, you want to look at what is called from each function. To get the list of these method/function calls:

    for my $func (@$calls) {
        my @called = funcs($calls->{ $func });
        ...
    }

where now @called contains the list of Devel::Calltree::Func objects. The list returned by funcs() has the same order in which the function/method calls were done. That means they are sorted by line-number.

Additionally, there's a file() function that returns the filename where the current function resides in.

    for my $func (@$calls) {
        print "$func lives in ", file($calls->{ $func });
        ...
    }

Here's a list of methods you can use on each Devel::Calltree::Func object:

  • name

    Returns the name of the function/method. If it is a function, the name is fully package-qualified. Otherwise it is just the name without the package (there is no easy way to package-qualify a method call at INIT-time).

  • line

    Returns the linenumber where this function/method was called.

  • file

    Returns the name of the file in which this function was called.

  • is_method

    Returns a true value when this function is in fact a method.

Here is an example how a complete report function should look like. This function is in fact producing the same output as the built-in report function:

    sub print_report {
        my $calls = shift;
        for my $caller (@$calls) {
            my $file = file($calls->{ $caller });
            if (funcs($calls->{$caller}) || !$OPT{-filter_empty} ) {
                print "\n$caller  ($file): \n";
                for my $targ (funcs($calls->{$caller})) {
                    my $n = $targ->name;
                    my $l = $targ->line;
                    if ($targ->is_method) {
                        print "  method   '$n'", ' ' x (60 - 14 - length($n)), " ($l)\n"; 
                        next;
                    }
                    print "  function '$n'", ' ' x (60 - 14 - length($n)), " ($l)\n";
                }
            }
        }
    }

%OPT holds the arguments which calltree passed on to the underlying module:

  • -exclude

    The list of regular expressions passed to calltree through the --exclude switch.

  • -include

    The list of regular expressions passed to calltree through the --include switch.

  • -filter_empty

    Is true when --noempty was passed to calltree.

  • -iscalled

    The list of regular expressions passed to calltree through the --chkfunc switch.

EXAMPLES

See the calltree of a script:

    calltree script.pl

Or one of a script given on the command-line:

    calltree -e '...'

See the calltree of the module URI:

    calltree -MURI

The same, but skip empty functions (those that do not make calls to others):

    calltree --noempty -MURI

The same, but ignore functions not in the URI:: namespace:

    calltree --noempty --exclude=. --include=URI:: -MURI

Thus, use --exclude=. to exclude everything and then include only functions from the URI:: namespace with --include=URI::.

And finally check whether a particular function is called at all:

    calltree --chkfunc=Carp::croak -MURI

ENVIRONMENT VARIABLES

calltree uses PERLLIB and PERL5LIB in a similar fashion as perl does.

TODO

  • Add a count for function calls

  • Better control over --chkfunc (packages can't currently be excluded)

  • Fix some fatal runtime errors that appear to happen sometimes on defined but uncalled functions

AUTHOR

Original idea and code by Mark Jason Dominus <mjd@plover.com>.

Current maintainer Tassilo von Parseval <tassilo.parseval@post.rwth-aachen.de>.

COPYRIGHT AND LICENSE

Original code copyright (C) 2003 by Mark Jason Dominus

Revisions copyright (C) 2004 by Tassilo von Parseval

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 383:

You forgot a '=back' before '=head1'