=head1 NAME

Exception::Backtrace - Get C and Perl backtraces

=cut

=head1 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:
    # 0x7f77fb6a0b38 in panda::Backtrace::Backtrace at src/panda/exception.cc:52
    # 0x7f77fb0bfbea in xs::attach_backtraces at src/xs/backtrace.cc:264
    # 0x7f77fb0bfd55 in xs::safe_wrap_exception at src/xs/backtrace.cc:276
    # 0x7f77fb0b293d in <lambda()>::operator() at Backtrace_xsgen.cc:262
    # 0x7f77fb0b5450 in xs::throw_guard<XS_Exception__Backtrace_safe_wrap_exception(CV*)::<lambda()> > at Backtrace_xsgen.cc:22
    # 0x7f77fb0b2d0f in XS_Exception__Backtrace_safe_wrap_exception at Backtrace_xsgen.cc:255
    # 0x55d395d1b003 in Perl_pp_entersub at pp_hot.c:5237
    # 0x55d395cd7912 in Perl_runops_debug at dump.c:2537
    # 0x55d395c376b7 in Perl_call_sv at perl.c:3026
    # 0x55d395ce0ed8 in S_invoke_exception_hook at util.c:1578
    # 0x55d395ce2c4a in Perl_croak_sv at util.c:1677
    # 0x55d395ce2c8e in Perl_die_sv at util.c:1602
    # 0x55d395da052e in Perl_pp_die at pp_sys.c:509
    # 0x55d395cd7912 in Perl_runops_debug at dump.c:2537
    # 0x55d395c4223f in perl_run at perl.c:2711
    # 0x55d395c0a282 in main at perlmain.c:72
    # 0x7f77fba20deb in __libc_start_main at ../sysdeps/x86/libc-start.c:129
    # 0x55d395c0a2ca from /home/b/perl5/perlbrew/perls/perl-debug-5.30.2/bin/perl
    # Perl backtrace:
    # Exception::Backtrace::__ANON__ at t/05-perl-exceptions.t:118
    # main::(eval) at t/05-perl-exceptions.t:118
    # main::__ANON__ at /home/b/perl5/perlbrew/perls/perl-debug-5.30.2/lib/site_perl/5.30.2/Test/Builder.pm:334
    # Test::Builder::(eval) at /home/b/perl5/perlbrew/perls/perl-debug-5.30.2/lib/site_perl/5.30.2/Test/Builder.pm:334
    # Test::Builder::subtest at /home/b/perl5/perlbrew/perls/perl-debug-5.30.2/lib/site_perl/5.30.2/Test/More.pm:809
    # Test::More::subtest at t/05-perl-exceptions.t:128
    # Returns just Perl backtrace
    my $bt2 = Exception::Backtrace::get_backtrace_string_pp($ex);

    # Programmatic API

    my $bt3 = Exception::Backtrace::get_backtrace($@);
    say $bt3->to_string;

    # 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
            ;
    }

    # 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
            ;
    }

=head1 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
L<Devel::StackTrace> should be considered to use.

In the application it is possible to throw either C++ or Perl exception. If
C<Perl> exception is thrown then it is handled via C<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 C<SIG{__DIE__}> handler.

If C<C++> exception is thrown then it's backtrace should be already available
via C<panda::Backtrace> object. The C++ to Perl exception conversion is performed
by L<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 L<XS::libpanda>
(the collection of general purpose C++ classes a-la STL) and L<XS::Framework>
(which makes it easy for C++ classes adoption into Perl).

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

=head1 LIMITATIONS

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

To gather C/C++ backtrace the sources must be compiled with B<debug info>(aka
C<-g> flag for C<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 not
available, there will just address and .so name.

It is recommended to have C<Perl> itself to be compiled with debug info, i.e.
C<perl -V> / C<Compiler> / C<optimize> should contain C<-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 C<-s -O2> gcc flags. They should be changed to C<-O0 -g> to
have DWARF debug info.

NB: debug info has B<zero runtime overhead> on *nix. The C<debug_*> sections
in elf files are not loaded by Operating System at all. So, it takes 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 C<Backtrace> object is created. During the resolution
the C<.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 C<panda::Backtrace>.

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

=head1 PORTABILITIY ISSUES

It seems that underlying C<backtrace> call from L<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.

=head1 SEE ALSO

L<XS::Framework>

L<XS::libpanda>

L<Devel::StackTrace>

L<http://dwarfstd.org/>

=head1 AUTHOR

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

=head1 LICENSE

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

=cut
