Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Capturing stack trace in eval

by brycen (Monk)
on Oct 07, 2009 at 19:24 UTC ( #799791=perlquestion: print w/ replies, xml ) Need Help??
brycen has asked for the wisdom of the Perl Monks concerning the following question:

I have some complicated code that is getting an exception deep inside an eval (specifically "Can't use an undefined value as a symbol reference"). This happens about 1 in every 100,000 transactions, from one of several dozen possible code paths. Without a stack trace I'm pretty stuck.

Is there a way to get caller() information through eval, at the eval level? I know (example below) how to print it locally from a method. But if I don't know in advance where the problem will come from, can I get a stack trace later, bubbled up to the eval?

Note that Carp, I think, won't work, since the exception is caused by a perl error, not an explicit die/carp/cluck.

eval { level2(); }; if($@) { print "Error $@"; #want to print stack trace HERE! } sub level2 { print "This is level2\n"; level3(); } sub level3 { print "This is level3\n"; $i = 0; while(($package, $filename, $line, $subroutine)=caller($i++)) +{ print "caller($i)=$package, $filename, $line, $subrout +ine\n"; } $zero = 0; $zero = 100 / $zero; }
Other monks postings on similar topics include http://www.perlmonks.org/?node_id=226358 and http://www.perlmonks.org/?node_id=211954

Comment on Capturing stack trace in eval
Download Code
Re: Capturing stack trace in eval
by moritz (Cardinal) on Oct 07, 2009 at 19:29 UTC
    Something like this?
    use strict; use warnings; use Carp; sub foo { bar() }; sub bar { 1/0; } eval { local $SIG{__DIE__} = \&Carp::confess; foo(); 1; } or print $@; __END__ Illegal division by zero at foo.pl line 7. at foo.pl line 7 main::bar() called at foo.pl line 5 main::foo() called at foo.pl line 13 eval {...} called at foo.pl line 11
    Perl 6 - links to (nearly) everything that is Perl 6.
Re: Capturing stack trace in eval
by Joost (Canon) on Oct 07, 2009 at 19:32 UTC
      edit: don't try to use the $SIG{__DIE__} for this. It won't work.

      Care to elaborate? Surely it won't work if somebody else fiddles with $SIG{__DIE__}, just like overriding CORE::GLOBAL::die won't work if somebody else fiddles with it at same time.

      Perl 6 - links to (nearly) everything that is Perl 6.
        Can't get into the details right now, the code I've worked on isn't here. One issue is that $SIG{__DIE__} won't work correctly with eval {} and eval "", even when testing $^S.

        perlvar sais:

        Due to an implementation glitch, the $SIG{__DIE__} hook is called even inside an eval(). Do not use this to rewrite a pending exception in $@, or as a bizarre substitute for overriding CORE::GLOBAL::die(). This strange action at a dis‐ tance may be fixed in a future release so that $SIG{__DIE__} is only called if your program is about to exit, as was the original intent. Any other use is deprecated.
        The only thing overriding CORE::GLOBAL::die won't catch is compile-time parser errors, but then you won't need a stack trace.

      You can override CORE::GLOBAL::die to throw an exception object

      Yes, but it wouldn't work for Perl errors such as the one the OP wants to capture, "Can't use an undefined value as a symbol reference".

      The only thing overriding CORE::GLOBAL::die won't catch is compile-time parser errors

      It won't catch run-time errors or XS errors either. Perl and XS don't call die, a Perl-space function. They call C function croak (not the one from Carp). Therefore, it only catches user-thrown errors.

        Actually, I might have been mistaken about my SIG{__DIE__} assertion - I mean that using __DIE__ instead of overriding CORE::GLOBAL::die might indeed be the best way of dealing with it.

        As I said I don't have the code with me at the moment - last time I looked at that specific bit of code was a couple of months ago and I switched implementations a few times.

        In any case, the end result turned out that throwing an exception object that encapsulates the stack trace from whatever means you use to intercept the error was the only reasonable way of dealing with the problem when you also have to deal with eval (both versions).

        Update: the way I implemented it, was that the exception object overloaded stringification so that the stack trace is printed if the exception isn't eventually caught.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://799791]
Approved by Joost
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2014-09-20 11:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (158 votes), past polls