74

Is there a way I can access (for printout) a list of sub + module to arbitrary depth of sub-calls preceding a current position in a Perl script?

I need to make changes to some Perl modules (.pm's). The workflow is initiated from a web-page thru a cgi-script, passing input through several modules/objects ending in the module where I need to use the data. Somewhere along the line the data got changed and I need to find out where.

user2291758
  • 715
  • 8
  • 19
slashmais
  • 7,069
  • 9
  • 54
  • 80
  • While this does not answer your question it might help you solve your problem :-) Here is an interesting article describing one way how to figure out who changes your variables from [Mark Dominus](http://blog.plover.com/oops/who-farted.html) – Pat Oct 23 '08 at 09:01

8 Answers8

71

You can use Devel::StackTrace.

use Devel::StackTrace;
my $trace = Devel::StackTrace->new;
print $trace->as_string; # like carp

It behaves like Carp's trace, but you can get more control over the frames.

The one problem is that references are stringified and if a referenced value changes, you won't see it. However, you could whip up some stuff with PadWalker to print out the full data (it would be huge, though).

Ovid
  • 11,580
  • 9
  • 46
  • 76
  • 3
    A very useful alternative: `perl -d:Confess script.pl` from [Devel::Confess](https://metacpan.org/pod/Devel::Confess). – Pablo Bianchi May 01 '18 at 08:02
25

This code works without any additional modules. Just include it where needed.

my $i = 1;
print STDERR "Stack Trace:\n";
while ( (my @call_details = (caller($i++))) ){
    print STDERR $call_details[1].":".$call_details[2]." in function ".$call_details[3]."\n";
}
Thariama
  • 50,002
  • 13
  • 138
  • 166
20

Carp::longmess will do what you want, and it's standard.

use Carp qw<longmess>;
use Data::Dumper;
sub A { &B; }
sub B { &C; }
sub C { &D; }
sub D { &E; }

sub E { 
    # Uncomment below if you want to see the place in E
    # local $Carp::CarpLevel = -1; 
    my $mess = longmess();
    print Dumper( $mess );
}

A();
__END__
$VAR1 = ' at - line 14
    main::D called at - line 12
    main::C called at - line 10
    main::B called at - line 8
    main::A() called at - line 23
';

I came up with this sub (Now with optional blessin' action!)

my $stack_frame_re = qr{
    ^                # Beginning of line
    \s*              # Any number of spaces
    ( [\w:]+ )       # Package + sub
    (?: [(] ( .*? ) [)] )? # Anything between two parens
    \s+              # At least one space
    called [ ] at    # "called" followed by a single space
    \s+ ( \S+ ) \s+  # Spaces surrounding at least one non-space character
    line [ ] (\d+)   # line designation
}x;

sub get_stack {
    my @lines = split /\s*\n\s*/, longmess;
    shift @lines;
    my @frames
        = map { 
              my ( $sub_name, $arg_str, $file, $line ) = /$stack_frame_re/;
              my $ref =  { sub_name => $sub_name
                         , args     => [ map { s/^'//; s/'$//; $_ } 
                                         split /\s*,\s*/, $arg_str 
                                       ]
                         , file     => $file
                         , line     => $line 
                         };
              bless $ref, $_[0] if @_;
              $ref
          } 
          @lines
       ;
    return wantarray ? @frames : \@frames;
}
Hobo
  • 7,536
  • 5
  • 40
  • 50
Axeman
  • 29,660
  • 2
  • 47
  • 102
  • 1
    longmess is no longer a documented or automatically exported feature of Carp. However: `my $mess = carp();` will provide similar but not identical behaviour. – Ross Attrill Jun 12 '13 at 23:37
18

caller can do that, though you may want even more information than that.

Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
16

There's also Carp::confess and Carp::cluck.

jkramer
  • 15,440
  • 5
  • 47
  • 48
4

In case you can't use (or would like to avoid) non-core modules, here's a simple subroutine I came up with:

#!/usr/bin/perl
use strict;
use warnings;

sub printstack {
    my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash);
    my $i = 1;
    my @r;
    while (@r = caller($i)) {
        ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = @r;
        print "$filename:$line $subroutine\n";
        $i++;
    }
}

sub i {
    printstack();
}

sub h {
    i;
}

sub g {
    h;
}

g;

It produces output like as follows:

/root/_/1.pl:21 main::i
/root/_/1.pl:25 main::h
/root/_/1.pl:28 main::g

Or a oneliner:

for (my $i = 0; my @r = caller($i); $i++) { print "$r[1]:$r[2] $r[3]\n"; }

You can find documentation on caller here.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
3

One that is more pretty: Devel::PrettyTrace

use Devel::PrettyTrace;
bt;
user2291758
  • 715
  • 8
  • 19
2

Moving my comment to an answer:

  1. Install Devel::Confess the right way

    cpanm Devel::Confess
    
  2. Run with

    perl -d:Confess myscript.pl
    

On errors, this will show the whole call stack list.

Pablo Bianchi
  • 1,824
  • 1
  • 26
  • 30