2017-02-12 14 views
2

Я в курсе этой идиомы:Как переместить стек вызовов точно в точке ошибки?

eval { 
    ... 
}; 

$DB::single = 1 if [email protected]; 

... но, насколько я могу сказать, если отладчик останавливается после того, как eval это уже слишком поздно, чтобы изучить кадры в стеке, как они были точно в тот момент, когда произошла ошибка.

Есть ли способ остановить отладчик точно в момент возникновения ошибки и проверить кадры в стеке вызовов?

ответ

4

если есть способ точно остановится в точке отказа

$SIG{__DIE__} называется, где исключение, так что вы могли бы добавить

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 
2

Вы можете просматривать трассировки стека, когда программа приостановлена ​​в любой точке останова с помощью команды T

В качестве альтернативы, вы можете установить dieLevel вариант с o dieLevel=1 вызвать автоматическую трассировку любого исключения

+0

Я вижу. Я думал, что с отладчиком perl можно было бы поднимать/опускать стек вызовов, как можно с помощью отладчика python (с директивами 'up' /' down') и, следовательно, иметь возможность проверять лексические переменные на каждом уровне. Если вы правильно поняли, все, что вы можете сделать, это просмотреть обратную трассировку. Пожалуйста, поправьте меня, если я ошибаюсь. – kjo

+0

@kjo: Боюсь, это правильно. Если вы посмотрите раздел BUGS ['perldoc perldebug'] (http://perldoc.perl.org/perldebug.html#BUGS), он говорит: *** Вы не можете получить информацию о кадре стека или в любых способах отладки моды, которые были не скомпилированные Perl, например, из расширений C или C++. *** Я бы не назвал это ошибкой, но довольно ясно, что в настоящее время недоступно то, что вы хотите. – Borodin

+0

@kjo: Вот почему я описал, как создать автоматическую обратную трассировку в случае исключения. – Borodin

6

Примечание   Это было написано по оригинальному вопросу, прежде чем оно было изменено. Он извлекает все лексические переменные для каждого фрейма в стеке вызовов в точке, где выбрано die без отладчика.


Для отладки Carp::Always полезно.

Кроме того, за ошибки, вы, кажется, после того, как вы можете переопределить die получить Carp «s трассировку

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

Это имеет ограничения и сложности, чтобы быть в курсе, см eval и %SIG in perlvar и die, но учитывая отсутствие подробностей о том, что вызывает ошибку, стоит попробовать.


С __DIE__ работает крюк, когда die срабатывает, в нем можно просматривать стек вызовов «вживую».

Код ниже использует caller, чтобы пройти стек и для базовой информации, и PadWalker получить лексические переменные для каждого кадра. Переменные в подсистемах существуют так, чтобы легче отслеживать результат.

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 ([email protected]) { print "eval: [email protected]" } 

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; 
}; 

Выход

 
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. 

В PadWalker в peek_my возвращает hashref, где каждое значение является ссылкой. Я разыскиваю только скаляры, для демонстрации, а также исключаю $vars, где выводы PadWalker хранятся в печати. Чтобы оставить обработчик, начинайте с my $sf = 1.