Pergunta

Existe uma maneira de acessar (para imprimir) uma lista de módulo sub + para profundidade arbitrária de subcalões que precedem uma posição atual em um script Perl?

Preciso fazer alterações em alguns módulos Perl (.pm's). O fluxo de trabalho é iniciado a partir de uma página da web através de um ccri-script, passando a entrada através de vários módulos/objetos que terminam no módulo onde eu preciso usar os dados. Em algum lugar ao longo da linha, os dados foram alterados e eu preciso descobrir onde.

Foi útil?

Solução

Você pode usar Devele :: Stacktrace.

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

Ele se comporta como o traço da Carp, mas você pode obter mais controle sobre os quadros.

O único problema é que as referências são rigíveis e, se um valor referenciado for alterado, você não o verá. No entanto, você pode preparar algumas coisas com Padwalker Para imprimir os dados completos (seria enorme).

Outras dicas

chamador pode fazer isso, embora você possa querer ainda mais informações do que isso.

Carp::longmess fará o que você quiser e é padrão.

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

Eu inventei este sub (agora com ação opcional abençoada!)

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

Há também Carp::confess e Carp::cluck.

Este código funciona sem módulos adicionais. Basta incluí -lo quando necessário.

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

Um que é mais bonito: Devele :: bestytrace

use Devel::PrettyTrace;
bt;

Caso você não possa usar (ou gostaria de evitar) módulos não essenciais, aqui está uma sub-rotina simples que eu criei:

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

Produz saída como a seguinte:

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

Ou um OneLiner:

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

Você pode encontrar documentação sobre o chamador aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top