2

I'm aware of this idiom:

eval {
    ...
};

$DB::single = 1 if $@;

...but, as far as I can tell, if the debugger stops after the eval it's already too late to examine the frames in the stack as they were exactly at the instant the error occurred.

Is there a way to stop the debugger exactly at the moment the error ocurred, and examine the frames in the call stack?

ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
kjo
  • 33,683
  • 52
  • 148
  • 265

3 Answers3

6

Note   This was written for the original question, before it was changed. It retrieves all lexical variables for each frame in the call stack at the point where die is thrown, without the debugger.


For debugging, Carp::Always is helpful.

Also, for errors you seem to be after, you can override die to get Carp's backtrace

eval { 
    local $SIG{__DIE__} = \&Carp::confess;
    # ... code ...
};
if ($@) { print $@ }

This has limitations and complexities to be aware of, see eval and %SIG in perlvar, and die, but given the lack of detail on what triggers the error it should be worth trying.


Since __DIE__ hook runs when die is triggered, in it we can examine the call stack 'live'.

The code below uses caller to walk the stack and for basic info, and PadWalker to get lexical variables for each frame. I put some variables in subs so to more easily follow the output.

use warnings;
use strict;
use PadWalker qw(peek_my);

my $ondie = sub {
    my $sf = 0;
    while ( my @call = caller($sf) ) {        # go through stack frames
        say "At $sf frame, |@call[0..3]|";
        my $vars = peek_my($sf);              # lexicals for this frame
        for (keys %$vars) {
            if (ref($vars->{$_}) eq 'SCALAR') {
                print "\t$_ => ${$vars->{$_}}\n";
            } elsif (not /^\$vars$/) {
                print "\t$_ => $vars->{$_}\n";
            }
        }
        ++$sf;
    }
};

eval { 
    local $SIG{__DIE__} = $ondie;
    top_level(25); 
}; 
if ($@) { print "eval: $@" }

sub top_level {
    my ($top_x,  $sub_name) = (12.1, (caller(0))[3]);
    next_level($_[0]);
};    
sub next_level { 
    my ($next_x, $sub_name) = (7, (caller(0))[3]);
    $_[0] / 0;
};

The output

At 0 frame, |main debug_stack.pl 51 main::__ANON__|
        $sf => 0
        @call => ARRAY(0x15464c8)
At 1 frame, |main debug_stack.pl 59 main::next_level|
        $next_x => 7
        $ondie => REF(0x1587938)
        $sub_name => main::next_level
At 2 frame, |main debug_stack.pl 38 main::top_level|
        $top_x => 12.1
        $ondie => REF(0x1587938)
        $sub_name => main::top_level
At 3 frame, |main debug_stack.pl 36 (eval)|
        $ondie => REF(0x1587938)
eval: Illegal division by zero at debug_stack.pl line 51.

The PadWalker's peek_my returns a hashref, where each value is a reference. I dereference only scalars, for demonstration, and also exclude $vars, where PadWalker's findings are stored, from prints. To leave out the handler itself start with my $sf = 1.

zdim
  • 64,580
  • 5
  • 52
  • 81
5

if there was a way to stop exactly at the point of failure

$SIG{__DIE__} is called where the exception is thrown, so you could add

local $SIG{__DIE__} = sub { $DB::single = 1; die(@_); };

$ cat a.pl
sub g {
   die "!";
}

sub f {
   g();
}

local $SIG{__DIE__} = sub { $DB::single = 1; die(@_); };
f();

$ perl -d a.pl

Loading DB routines from perl5db.pl version 1.49_04
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(a.pl:9): local $SIG{__DIE__} = sub { $DB::single = 1; die(@_); };
  DB<1> r
main::CODE(0x1067280)(a.pl:9):  local $SIG{__DIE__} = sub { $DB::single = 1; die(@_); };
  DB<1> T
@ = DB::DB called from file 'a.pl' line 9
$ = main::__ANON__[a.pl:9]('! at a.pl line 2.^J') called from file 'a.pl' line 2
. = main::g() called from file 'a.pl' line 6
. = main::f() called from file 'a.pl' line 10
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

You can view a stack backtrace when the program is paused at any breakpoint using the T command

Alternatively, you may set the dieLevel option with o dieLevel=1 to cause an automatic backtrace on any exception

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • I see. I thought that with the perl debugger one could go up/down the call stack, like one can with the python debugger (with the `up`/`down` directives), and so be able to inspect lexical variables at each level. If you understand you correctly, all one can do is view the backtrace. Please correct me if I'm wrong. – kjo Feb 12 '17 at 02:14
  • @kjo: I'm afraid that's correct. If you looks at the BUGS section of [`perldoc perldebug`](http://perldoc.perl.org/perldebug.html#BUGS) it says ***You cannot get stack frame information or in any fashion debug functions that were not compiled by Perl, such as those from C or C++ extensions.*** I wouldn't call that a bug, but it's pretty clear that what you want is unavailable at present. – Borodin Feb 12 '17 at 02:20
  • @kjo: That's why I described how to generate an automatic backtrace in the case of an exception. – Borodin Feb 12 '17 at 03:04
  • @kjo: Why don't you try it to see what I'm talking about? You get a list of the files and line numbers at each point in the call stack. – Borodin Feb 12 '17 at 03:19
  • 2
    @kjo: That's very ungracious and entitled of you. You said you wanted to *"go up the call stack and examine where the error occurred"* which my answer and **zdim**'s satisfied. You managed to convince two of us that is was exactly what you were asking for. – Borodin Feb 12 '17 at 13:17
  • My sincere apologies. I do feel terrible for having come across the way I did. I will be more careful. – kjo Feb 12 '17 at 18:45
  • @kjo Yes, please do be more careful. I wrote a little debugger in order to answer your question. It is now sticking out like a sore thumb. (When I added the code I didn't check the question, not expecting that it would change so much. In the end, I find all answers here to be good and useful so I don't feel too bad about it.) Your edit could've been an _addition_, leaving the original question in place, not a rewrite. – zdim Feb 14 '17 at 09:56