Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

How do I get a post mortem stack trace?

by rinceWind (Monsignor)
on Nov 11, 2002 at 16:09 UTC ( [id://211954]=perlquestion: print w/replies, xml ) Need Help??

rinceWind has asked for the wisdom of the Perl Monks concerning the following question:

I have downloaded a script that I hope will be useful to me, as it pertains to do something useful. But, it dies on me.

I want to submit a bug report to the author, and possibly a patch to fix the problem. But, I need to know more than just the line number, where it's failing. I've tried the Perl 5 debugger, and I suppose I could <tedious> single step my way into the previous line </tedious>, then use 'T' to get a traceback. The trouble is, that one the script has died, the stack is no longer there.

I have also looked at perl -MCarp=verbose as per the documentation on Carp, but this will only catch Carp generated exceptions, not normal die exceptions.

Is there an existing snippet, or a command line that will do the trick? Have I missed anything obvious?

Any help would be appreciated. My platform is Win32 by the way.

Replies are listed 'Best First'.
Re: How do I get a post mortem stack trace?
by broquaint (Abbot) on Nov 11, 2002 at 16:26 UTC
    If you're happy to add to the source then you could add a __DIE__ handler e.g
    $SIG{__DIE__} = sub { warn "ack: ", join(', ', grep defined, caller(0)), $/; }; die("I died at line: ", __LINE__); __output__ ack: main, -, 5, main::__ANON__, 1, 0, 0, I died at line: 5 at - line 5.
    So you should get nice carp type details without the Carp.
    HTH

    _________
    broquaint

      Or to take this a step further, place the following at the beginning of the script....
      use Carp; $SIG{__DIE__} = \&Carp::confess;
      This turns any die into a confess, giving you a full stack trace. (Of course it does add two to the line numbers reported -- you could fudge this by adding these two statements to a pre-existing line).
      Enjoy...

      --JAS
Re: How do I get a post mortem stack trace?
by samtregar (Abbot) on Nov 11, 2002 at 18:39 UTC
    You're on the right track thinking about using the debuugger but you don't need to single-step to the line where the error occurs. Just set a breakpoint on the line before with the "b" command and then "c" to continue to the next breakpoint. For example, to set a breakpoint on line 4 of trans.pl and go to it, I would do:

    $ perl -d trans.pl Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(trans.pl:1): open(DICT, "/usr/dict/words"); DB<1> b 4 DB<2> c main::(trans.pl:4): my $text = join('',<DICT>); DB<2>

    From there you can get a stack trace, examine variables, etc.

    Happy hunting,
    -sam

Re: How do I get a post mortem stack trace?
by particle (Vicar) on Nov 11, 2002 at 16:45 UTC

    maybe Devel::TraceSubs would help? something like...

    use Devel::TraceSubs 0.02; my $dbg = Devel::TraceSubs->new(); $dbg->trace( 'my::package::here::' ); ## maybe it's 'main::' in your +case?

    it should show a pretty good stack trace. please let me know if it helps, or you have problems using it.

    ~Particle *accelerates*

Re: How do I get a post mortem stack trace?
by BrowserUk (Patriarch) on Nov 11, 2002 at 18:04 UTC

    If you know the line where the code fails, you can insert   $DB::single=2; on the line before, run under the debugger and hit 'c' to got straight to that line and grab your stacktrace that way. Quick and much less tedious than the single stepping method.


    Nah! You're thinking of Simon Templar, originally played (on UKTV) by Roger Moore and later by Ian Ogilvy
      Along the lines of my above (erroneuos) reply, If you know the line where the error occurs, just put a
      confess()
      statement at the line immediately before the script fails.
Re: How do I get a post mortem stack trace?
by shemp (Deacon) on Nov 11, 2002 at 16:20 UTC
    you could do something like putting an eval() statement around your entire script, which is easily accomplished by the following:
    # beginning of program eval { main(); }; if ( $@ ) { confess(); } sub main { ... }
    Here, just make the sub main() be what would normally be the program block outside of any function call, i.e. heres how to turn hello world into what i'm saying
    #!/usr/bin/perl -w use strict; use Carp; eval { main(); }; if ( $@ ) { confess(); } exit; sub main { print "hello world\n"; }
    Trivial example, this should never die, if it does, you have bigger problems.
      No cigar.
      #!/usr/bin/perl -w use strict; use Carp; eval { main(); }; if ( $@ ) { confess($@); } exit; sub foo { my $aargh = shift; die $aargh; } sub main { foo "hello world\n"; }
      I don't get a stack trace. Once it has finished the eval, the stack is no longer there.
Re: How do I get a post mortem stack trace?
by Popcorn Dave (Abbot) on Nov 11, 2002 at 23:30 UTC
    I think you are actually on the right track to do the debugger routine. As others have said, if you know where it's failing, start tracing from there.

    I've been in similar circumstances and I use the GUI Debugger and had great success with it. You can expand or contract data structures which is nice and since you can see your code, you can decide where you want to set your breakpoints. Plus it runs great on win32 boxes too.

    Hope that helps!

    There is no emoticon for what I'm feeling now.

Re: How do I get a post mortem stack trace?
by rinceWind (Monsignor) on Nov 12, 2002 at 10:35 UTC
    Thanks everyone for your suggestions here.

    Of course (Doh!) once I know the line where the program is failing, I can set a break in the previous executable line, and if it's the first line in a sub, set a break on the sub.

    This has given me enough information for a bug report for the original problem I had, but I could imagine there might be some problems if the code is inside a coderef and/or an eval (yes, I do do tortuous things with coderefs sometimes as fever knows :-).

    However, I still think that the debugger could benefit from a better handler to catch $SIG{__DIE__}, enabling you to look at lexical variables in scope at the point where it dies, and the good ol' stack trace. Where or to whom do I post such a suggestion?

    Also, I could use (and I am thinking of writing when I get a round tuit) a generic script called confessor.pl, which takes a script and argv list, sets up the die handler which will confess, then requires in the script. I have had a brief stab at this, but there are issues with BEGIN code, and the scripts are not designed to be 'require'd so they do not return true.

    Update: Maybe a form of use Carp qw(verbose); should catch $SIG{__DIE__}. Possibly use Carp qw(verbose,catchdie);

    Update 2: Just tried the suggestion use diagnostics; or rather perl -Mdiagnostics foobar.pl from anonymonk below. It does the business! Anonymonk++ whoever you are! Sign up to the monastery with an ID, your input is most welcome.

      Recently a very interesting post/patch was made to p5p that looks like it could meet your debugging requirements (and make Carp::Assert redundant). If it gets in it will be in version 5.10 however. :-(

      --- demerphq
      my friends call me, usually because I'm late....

      and the scripts are not designed to be 'require'd so they do not return true.

      Maybe you could use the do 'stat.pl'; syntax instead?


      Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
      Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
      Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
      Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

An easy-to-use signal handler
by Jeppe (Monk) on Nov 12, 2002 at 16:20 UTC
    $SIG{__DIE__} = sub { my ( $path, $line, $subr ); my $i = 0; while ( $subr = (caller($i++))[3] ) { return if $subr eq '(eval)'; } warn "died at: ", join(' line ', (caller)[1,2]), "\n"; $i = 1; while ( ($path, $line) = (caller($i++))[1,2] ) { warn "caller: $path line $line\n"; } };
    I am not sure if this runs on Win32.
Re: How do I get a post mortem stack trace?
by Anonymous Monk on Nov 12, 2002 at 18:04 UTC
    Have you tried use diagnostics;? Gives me plenty of debug and more.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://211954]
Approved by insensate
Front-paged by ChemBoy
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-04-19 07:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found