Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
534 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 "$_ => ${$vars->{$_}}
";
            } elsif (not /^$vars$/) {
                print "$_ => $vars->{$_}
";
            }
        }
        ++$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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...