The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Exception::Backtrace - Get C and Perl backtraces

SYNOPSIS

    use Exception::Backtrace;

    # sets/overrides SIG{__DIE__} interceptor
    Exception::Backtrace::install();

    eval { die("what-ever"); }
    my $ex = @$;

    # Returns C and Perl backtraces string like:
    my $bt1 = Exception::Backtrace::get_backtrace_string($ex);
    # C backtrace:
    # 0x7f27b06e7cc8 in panda::Backtrace::Backtrace at XS-libpanda/src/panda/exception.cc:55
    # 0x7f27b06e7dd4 in panda::exception::exception at XS-libpanda/src/panda/exception.cc:87
    # 0x7f27b01068c5 in fn_1<short unsigned int> at Exception-Backtrace/t/MyTest.xs:9
    # 0x7f27b0106482 in fn_2<std::__cxx11::basic_string<char> > at Exception-Backtrace/t/MyTest.xs:13
    # 0x7f27b0105cad in <lambda()>::<lambda()>::operator() at Exception-Backtrace/t/MyTest.xs:225
    # 0x7f27b0105d54 in <lambda()>::operator() at Exception-Backtrace/t/MyTest_xsgen.cc:235
    # 0x7f27b01064b1 in xs::throw_guard<XS_MyTest_throw_backtrace(PerlInterpreter*, CV*)::<lambda()> > at ../../var/lib/x86_64-linux-thread-multi/XS/Framework.x/i/xs/catch.h:23
    # 0x7f27b0105dbb in XS_MyTest_throw_backtrace at Exception-Backtrace/t/MyTest_xsgen.cc:229
    # 0x55a24133efd4 in Perl_pp_entersub at /home/b/perl5/perlbrew/build/perl-5.32.0/perl-5.32.0/pp_hot.c:5277
    # 0x55a2412f5a6a in Perl_runops_debug at /home/b/perl5/perlbrew/build/perl-5.32.0/perl-5.32.0/dump.c:2571
    # 0x55a24124f303 in S_run_body at /home/b/perl5/perlbrew/build/perl-5.32.0/perl-5.32.0/perl.c:2761
    # 0x55a24124f303 in perl_run at /home/b/perl5/perlbrew/build/perl-5.32.0/perl-5.32.0/perl.c:2761
    # 0x55a24120f3ee in main at /home/b/perl5/perlbrew/build/perl-5.32.0/perl-5.32.0/perlmain.c:73
    # 0x7f27b0a5edeb in __libc_start_main at /builddir/glibc-2.30/csu/../csu/libc-start.c:130
    # 0x55a24120f42a from /home/b/perl5/perlbrew/perls/5.32.0m/bin/perl
    # Perl backtrace:
    # main::(eval) at t/06-c-exceptions.t:16
    # main::__ANON__ at /home/b/perl5/perlbrew/perls/5.32.0m/lib/5.32.0/Test/Builder.pm:334
    # Test::Builder::(eval) at /home/b/perl5/perlbrew/perls/5.32.0m/lib/5.32.0/Test/Builder.pm:334
    # Test::Builder::subtest at /home/b/perl5/perlbrew/perls/5.32.0m/lib/5.32.0/Test/More.pm:809
    # Test::More::subtest at t/06-c-exceptions.t:27
    # Returns just Perl backtrace
    my $bt2 = Exception::Backtrace::get_backtrace_string_pp($ex);

    # Programmatic API

    local Exception::Backtrace::decorator = sub { JSON::XS::encode_json(shift) };
    my $bt3 = Exception::Backtrace::get_backtrace($@);
    say $bt3->perl_trace->to_string($decorator); # custom arguments stringification
    # something like main::fn["k","v",[1,2,3,3.14158]] will be dumped for function
    # fn(k => 'v', [1,2,3,3.14158]) call

    # get the current trace without exception
    my $bt = Exception::Backtrace::create_backtrace;

    # perl trace
    my $perl_trace = $bt->perl_trace;
    say $perl_trace->to_string; # stringifies just perl backtrace
    my $frames = $perl_trace->get_frames;
    for my $frame (@$frames) {
        say $frame->to_string;  # stringifies just single frame
        say
            $frame->library,    # aka perl package
            $frame->file,
            $frame->line_no,
            $frame->name        # function name
            ;
        # stringified arguments inspection
        say $frame->args;
    }

    # C/C++ trace
    my $c_trace = $bt->c_trace;
    say $c_trace->to_string;
    my $frames = $c_trace->get_frames;
    for my $frame (@$frames) {
        say $frame->to_string;  # stringifies just single frame
        say
            $frame->library,    # path to .so-file
            $frame->file,
            $frame->line_no,
            $frame->name,       # function name
            $frame->address,
            $frame->offset
            ;
    }

DESCRIPTION

Complex applications can be developed as mixture of layers of C++ and Perl code. When something abnormal is happen (i.e. exception is thrown) it is desirable to figure out what was wrong and on what layer.

If an application is written in pure Perl then the module probably useless, and Devel::StackTrace should be considered to use.

In the application it is possible to throw either C++ or Perl exception. If Perl exception is thrown then it is handled via SIG{__DIE__} where Perl and C++ backtraces are attached to it. As there was no C++ exception, the C++ backtrace is gathered from the point of SIG{__DIE__} handler.

If C++ exception is thrown then it's backtrace should be already available via panda::Backtrace object. The C++ to Perl exception conversion is performed by XS::Framework; the C++ backtrace is preserved and Perl backtrace is constructed and attached to the exception.

The package can be seen as the thin bridge for backtraces between XS::libpanda (the collection of general purpose C++ classes a-la STL) and XS::Framework (which makes it easy for C++ classes adoption into Perl).

If C++ backtrace consists from two parts: the backtraces gathering (done by panda::Backtrace) and converting them into symbolic information (done by Exception::Backtrace). As the second part is quite slow and heavy, it is performed only on demand.

FUNCTIONS

install([$decorator = undef])

Sets up global SIG{__DIE__} to be wrapped by Exception::Backtrace. The $decorator function is that one, which takes an array of original arguments of a perl frame, and must return a string, which is attached to the exception object.

If $decorator is undef then the default $decorator will be installed.

To let a $decorator be applied it should be set before creating an exception.

get_backtrace_string($exception)

Returns a joined string of perl and C stack frames coming from $exception (usually it is $@).

get_backtrace_string_pp($exception)

Returns a string of perl frames coming from $exception (usually it is $@).

create_backtrace()

Returns an inspectable Exception::Backtrace::DualTrace.

LIMITATIONS

The module was tested on *nix, Darwin and Windows.

To gather C/C++ backtrace the sources must be compiled with debug info(aka -g flag for cc). It is possible to have a mixture of .so compiled with and without debug info; in the last case the symbol information will be just partially available via XS::libpanda, i.e. just symbol name and .so if available.

It is recommended to have Perl itself to be compiled with debug info, i.e. perl -V / Compiler / optimize should contain -g; this will allow to automatically add debug info for all XS-extensions.

Strawberry Perl users: out of the box debug info is being stripped from the binaries via -s -O2 gcc flags. They should be changed to -O0 -g to have DWARF debug info.

NB: debug info has zero runtime overhead on *nix. The debug_* sections in elf files are not loaded by Operating System at all. So, it occupies only disk space, which is quite cheap nowadays.

C++ symbolic information resolution (i.e. gathering source file, file line etc.), happens on demand, i.e. when Backtrace object is created. During the resolution the .so files are loaded from disk. That means, if the shared libraries on disk are different from shared libraries loaded in the process, the reconstructed backtraces will be wrong.

To have full trace for C++ exceptions, they must be inherited from panda::Backtrace.

The thrown Perl exception should be either object or scalar (i.e. not reference to scalar) to be able to attach backtraces to it.

PORTABILITIY ISSUES

It seems that underlying backtrace call from XS::libpanda does not always return correct/full backtrace. That means, that C/C++ backtrace does not works reliable. If you know workaround, please submit a patch.

It is known, that systems with musl (e.g. Alipne Linux) instead of glibc do not export backtrace capabilities, so backtraces will not be available for that systems. There is a theoretical workaround to use libunwind; that might be implemented in future.

PERL FRAME ARGUMENTS

It is different time when exception happens, and when stack frame is gathered: it might happen, when an argument was available during frame call, but later (deeper) it was deleted. So, when Exception::Backtrace gathers such an argument, just undef will be recorded.

See "to_string" in Exception::Backtrace::PerlFrame;

SEE ALSO

XS::Framework

XS::libpanda

Devel::StackTrace

http://dwarfstd.org/

libunwind

AUTHOR

Ivan Baidakou <i.baydakov@crazypanda.ru>, Crazy Panda, CP Decision LTD

LICENSE

You may distribute this code under the same terms as Perl itself.