How to move up call stack exactly at point of error?

后端 未结 3 1598
不知归路
不知归路 2020-12-12 03:22

I\'m aware of this idiom:

eval {
    ...
};

$DB::single = 1 if $@;

...but, as far as I can tell, if the debugger stops after the eva

3条回答
  •  孤街浪徒
    2020-12-12 03:29

    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.

提交回复
热议问题