Как я могу получить список стека вызовов в Perl?

Есть ли способ получить доступ (для распечатки) к списку вспомогательных модулей с произвольной глубиной вспомогательных вызовов, предшествующих текущей позиции в сценарии Perl?

Мне нужно внести изменения в некоторые модули Perl (.pm). Рабочий процесс запускается с веб-страницы через cgi-скрипт, вводящий данные через несколько модулей / объектов, заканчивающихся модулем, в котором мне нужно использовать данные. Где-то по ходу дела данные изменились, и мне нужно выяснить, где.

Хотя это не отвечает на ваш вопрос, но может помочь вам решить вашу проблему :-) Вот интересная статья, описывающая один из способов выяснить, кто изменяет ваши переменные из Марк Доминус

Pat 23.10.2008 13:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
70
1
35 079
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Вы можете использовать Devel :: StackTrace.

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

Он ведет себя как след Карпа, но вы можете получить больший контроль над кадрами.

Единственная проблема заключается в том, что ссылки преобразованы в строку, и если указанное значение изменится, вы его не увидите. Тем не менее, вы можете использовать PadWalker, чтобы распечатать полные данные (хотя это было бы огромно).

Очень полезная альтернатива: perl -d:Confess script.pl от Devel :: Признаться.

Pablo Bianchi 01.05.2018 11:02

звонящий может это сделать, хотя вам может потребоваться еще больше информации.

Также есть Carp::confess и Carp::cluck.

Carp::longmess сделает то, что вы хотите, и это стандартно.

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

Я придумал эту сабвуфер (теперь с дополнительным действием благословения!)

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

longmess больше не является документированной или автоматически экспортируемой функцией Carp. Однако: my $mess = carp(); будет обеспечивать аналогичное, но не идентичное поведение.

Ross Attrill 13.06.2013 03:37

Более красивый: Devel :: PrettyTrace

use Devel::PrettyTrace;
bt;

Этот код работает без дополнительных модулей. Просто включите его там, где это необходимо.

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

изящная техника (хотя должен сказать, что давно не пробовал работать с Perl :)

slashmais 18.08.2016 10:36

Должен сказать, очень приятно! Спасибо :-)

frr 10.07.2017 15:26

начинать с 0, а не с 1.

Jim Balter 27.07.2019 01:14

Если вы не можете использовать (или хотите избежать) неосновные модули, вот простая подпрограмма, которую я придумал:

#!/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;

Он производит следующий вывод:

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

Или один лайнер:

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

Вы можете найти документацию по вызывающему здесь.

Перемещение мой комментарий в ответ:

  1. Установите Devel :: Признаться на правильно

    cpanm Devel::Confess
    
  2. Бежать с

    perl -d:Confess myscript.pl
    

При ошибках отображается весь список стека вызовов.

Другие вопросы по теме