http://www.perlmonks.org?node_id=378834

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

Update 2:The latest bleaperl has a patch that allows
perl -Mdiagnostics=t-,-w badly_behaved_script
-w adds stack traces to warnigns and -t turns off the explanations. No need for a new module.

Update:I should have been more clear in my question, I'm not asking "how do I do this?", I already know how to do it. I'm asking "is this readily available already?" because it's pretty amazing that after all these years, there is no standard, easy way to force Perl to give a stacktrace when it dies.

In particular I want to be able to say to someone who's using my code and having problems, "run it again with perl -MDevel::Verbose and send me the output" and they'll be able to get it from CPAN and the code should work just as before, except with much more info when it dies.

Most languages will give you a stack trace when they die or at least you can set a flag so that this happens, Perl lacks this feature and I couldn't find a module that provides it. So for quite a while now I have been using my own module that works like this

use Devel::Verbose; a_sub(); sub a_sub { will_warn(); print STDERR "-------------\n"; will_die(); } sub will_warn { warn("something funny"); } sub will_die { die("something awful"); }
this will give output something like
Trace begun at t.pl line 11
main::will_warn at t.pl line 5
main::a_sub at t.pl line 3
something funny at t.pl line 11.
-------------
Trace begun at t.pl line 14
main::will_die at t.pl line 7
main::a_sub at t.pl line 3
something awful at t.pl line 14.
It's very careful not to interefere with the original dieing and warning behaviour (it's not perfect in this respect but it's close).

Have I reinvented a wheel? Just checking before I post to CPAN. It's based on Devel::StackTrace and Exception::Class.

Carp does something similar but you have to explicitly use it in your code, whereas I can do perl -MDevel::Verbose some_script.pl and I will get full stack traces from all warn()s and die()s, without changing a line of code in some_script.pl.

Replies are listed 'Best First'.
Re: Automatic stack traces for warns and dies
by etcshadow (Priest) on Jul 30, 2004 at 23:22 UTC
    use Carp qw(confess cluck); $SIG{__WARN__} = 'cluck'; $SIG{__DIE__} = 'confess';
    ------------ :Wq Not an editor command: Wq
      That is a simpler implementation but it doesn't preserve the original values in %SIG and will also mess up anything that uses real exceptions (as in Error or Exception::Class) which might do something like
      eval { something_that_might_die()}; if ($@->isa('FileException')) { handle_file_exception } else { die $@; }

      I love the name of that Carp function confess is just so.....on the money.

      cheers

      tachyon

Re: Automatic stack traces for warns and dies
by itub (Priest) on Jul 31, 2004 at 21:55 UTC
    Something that I find useful for this is diagnostics, which comes with Perl. This pragma is famous for giving verbose explanations for all kinds of errors, but perhaps it is not so well known that it also prints the stack trace upon dying. For example,

    # div_by_zero.pl sub a { b(@_) } sub b { c(@_) } sub c { 1/shift }; a(0);

    The output:

    $ perl div_by_zero.pl
    Illegal division by zero at div_by_zero.pl line 4.
    
    $ perl -Mdiagnostics div_by_zero.pl
    Illegal division by zero at div_by_zero.pl line 3 (#1)
        (F) You tried to divide a number by 0.  Either something was wrong in
        your logic, or you need to put a conditional in to guard against
        meaningless input.
        
    Uncaught exception from user code:
            Illegal division by zero at div_by_zero.pl line 3.
            main::c(0) called at div_by_zero.pl line 2
            main::b(0) called at div_by_zero.pl line 1
            main::a(0) called at div_by_zero.pl line 4
    

    Update: Note that stack traces for warnings are not printed.

      Thanks for that, a patch has just been added to bleadperl to allow
      perl -Mdiagnostics=-t,-w some_bad_script
      -t will suppress the explanations and -w will add stack traces to your warns.
Re: Automatic stack traces for warns and dies
by Aristotle (Chancellor) on Jul 31, 2004 at 19:39 UTC
    use Carp qw(confess cluck); BEGIN { *CORE::GLOBAL::warn = \&cluck; *CORE::GLOBAL::die = \&confess; }

    (Note the compile time override; this is important.)

    Makeshifts last the longest.

      I think that will also mess up anything which is doing an eval and then checking $@ afterwards.

      I should have been more clear in my question, I'm not asking "how do I do this?", I already know how to do it. I'm asking "is this readily available already?" because it's pretty amazing that after all these years, there is no standard, easy way to force Perl to give a stacktrace when it dies.

      In particular I want to be able to say to someone who's using my code and having problems, "run it again with perl -MDevel::Verbose and send me the output" and they'll be able to get it from CPAN and the code should work just as before.

        I think that will also mess up anything which is doing an eval and then checking $@ afterwards.
        Does it?
        use Carp qw(confess cluck); BEGIN { *CORE::GLOBAL::warn = \&cluck; *CORE::GLOBAL::die = \&confess; } sub foo { bar() } sub bar { print "in bar\n"; die "badness!" } eval { foo }; print "ack - $@" if $@; __output__ in bar ack - badness! at - line 8 main::bar() called at - line 7 main::foo() called at - line 10 eval {...} called at - line 10
        Same goes for throwing objects
        use Carp qw(confess cluck); BEGIN { *CORE::GLOBAL::warn = \&cluck; *CORE::GLOBAL::die = \&confess; } sub foo { bar() } sub bar { print "in bar\n";die( bless{akey=>"a string"} ) } use DDS; eval { foo }; print "ack: ", Dump($@) if ref $@; __output__ in bar ack: $main1 = bless( { akey => 'a string' }, 'main' );
        So Aristotle's solution should fit the bill.
        HTH

        _________
        broquaint