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

Greetings, monks.

I'm doing some database transactions along with some other code, and sometimes things fail. I'd like to get a proper backtrace for them:

use Carp; sub sub1 { sub3(); sub2(); } # want the backtrace down to sub2 or sub3 sub sub2 { die "code failure"; } sub sub3 { die "runtime error" if rand() < 0.25 } sub main_sub { eval { #$dbh->begin_work; # various things, not all db-related sub1(); # more things #$dbh->commit; }; if ($@) { #$dbh->rollback; print STDERR $@; confess "the transaction failed"; }; } main_sub();

All I am getting is the backtrace to main_sub, and the line number of the die. How do I get a non-truncated backtrace?

Replies are listed 'Best First'.
Re: How to print the backtrace in a "catch" block?
by Corion (Patriarch) on Jan 16, 2012 at 15:37 UTC

    Have you looked at Carp::confess? You use the module, so I assume there is something that confess does that you don't want. Can you tell us how it fails for you to do what you want?

      Carp is certainly one way to do it. What you have to be aware of is that Carp uses a concept of "safe calls". Essentially it assumes that when one sub calls another sub in the same package, the author of the package knows what he/she is doing, and doesn't include this call in the stack trace. So the stack trace only shows places where the call stack crosses package boundaries.

      Setting $Carp::Verbose to true switches off that behaviour, so stack traces will include all calls.

      A better solution though, is to notice that Perl doesn't just only support die($string) as a way of throwing an exception. die can be called on any scalar value, including blessed objects. There are a number of modules on CPAN providing useful classes that can be used for throwing exceptions. Throwable::Error is one of the best ones, but has a dependency on Moose. Exception::Class::Base and Error::Base are also good.

      Here's an example using Exception::Class::Base

      use Exception::Class ( 'My::Exception' => { description => 'simple exception'}, ); My::Exception->Trace(1); # enable catching full traces eval { My::Exception->throw('Foo'); }; if ($@) { my $e = $@; $e->show_trace(1); warn $e->as_string; }

      Exception::Class also offers some fairly nice syntactic sugar for exception catching...

      use Exception::Class ( 'My::Exception' => { description => 'simple exception'}, ); My::Exception->Trace(1); # enable catching full traces eval { My::Exception->throw('Foo'); }; if (my $e = Exception::Class->caught('My::Exception')) { $e->show_trace(1); warn $e->as_string; }

      Overall, I'd say the gold standard of exceptions for Perl is Throwable::Error combined with either TryCatch or Try::Tiny. These introduce some pretty heavy dependencies, but if you can afford them, the combination can't be beaten for maintainable, readable exception handling.

        Setting $Carp::Verbose to true switches off that behaviour, so stack traces will include all calls.

        I tried that, but the output is identical for my test script. The eval block somehow truncates my backtrace, and that's not what I want.

        Furthermore, I thank you for your effort, but I am not looking for modules that implement exception classes, as my errors tend to be runtime, i.e. thrown by Perl.

      Well, this is what I'm getting:
      runtime error at - line 6. the transaction failed at - line 23 main::main_sub() called at - line 27
      What I'm hoping for:
      runtime error at - line 6. the transaction failed at - line 23 main::sub3() called at - line 3 main::sub1() called at - line 13 main::main_sub() called at - line 27
      Or perhaps in Ruby code:
      def dying_sub raise RuntimeError, "reason here" end def main begin dying_sub() rescue RuntimeError => e STDERR.puts e.backtrace end end main() # Output: # test.rb:2:in `dying_sub' # test.rb:7:in `main' # test.rb:13

        You will need to replace the die in sub2 and/or sub3 with a call to confess to get at the stack trace. Maybe you can install a $SIG{__DIE__} handler that stashes away the stack trace for later retrieval. I think that Carp::Always does this, but I've never used it.