views:

220

answers:

2

This question has been asked before and there have been windows-specific answers but no satisfactory gcc answer. I can use set_terminate() to set a function that will be called (in place of terminate()) when an unhandled exception is thrown. I know how to use the backtrace library to generate a stack trace from a given point in the program. However, this won't help when my terminate-replacement is called since at that point the stack has been unwound.

Yet if I simply allow the program to abort(), it will produce a core-dump which contains the full stack information from the point at which the exception was thrown. So the information is there -- but is there a programmatic way to get it, for example so it can be logged, rather than having to examine a core file?

+2  A: 

Edited Answer:

You can use std::set_terminate

#include <iostream>
#include <stdexcept>

#include <execinfo.h>

void
handler()
{
    void *trace_elems[20];
    int trace_elem_count(backtrace( trace_elems, 20 ));
    char **stack_syms(backtrace_symbols( trace_elems, trace_elem_count ));
    for ( int i = 0 ; i < trace_elem_count ; ++i )
    {
        std::cout << stack_syms[i] << "\n";
    }
    free( stack_syms );

    exit(1);
}   

int foo()
{
    throw std::runtime_error( "hello" );
}   

void bar()
{
    foo();
}

void baz()
{
    bar();
}

int
main()
{
    std::set_terminate( handler );
    baz();
}

giving this output:

samm@macmini ~> ./a.out 
./a.out [0x10000d20]
/usr/lib/libstdc++.so.6 [0xf9bb8c8]
/usr/lib/libstdc++.so.6 [0xf9bb90c]
/usr/lib/libstdc++.so.6 [0xf9bbaa0]
./a.out [0x10000c18]
./a.out [0x10000c70]
./a.out [0x10000ca0]
./a.out [0x10000cdc]
/lib/libc.so.6 [0xfe4dd80]
/lib/libc.so.6 [0xfe4dfc0]
samjmill@bgqfen4 ~> 

assuming you have debug symbols in your binary, you can then use addr2line to construct a prettier stack trace postmortem

samm@macmini ~> addr2line 0x10000c18
/home/samm/foo.cc:23
samm@macmini ~> 

original answer is below


I've done this in the past using boost::error_info to inject the stack trace using backtrace from execinfo.h into an exception that is thrown.

typedef boost::error_info<struct tag_stack_str,std::string> stack_info;

Then when catching the exceptions, you can do

} catch ( const std::exception& e ) {                                                                                                            
    if ( std::string const *stack boost::get_error_info<stack_error_info>(e) ) {                    
        std::cout << stack << std::endl;
    }
}
Sam Miller
Yes, as I said I know how to get the trace at the current point. Clearly if I stored it into every exception that would be that. But the question assumes that I haven't done that -- perhaps due to laziness, perhaps because I don't have access to the source code.
c-urchin
what do you expect the stack trace to look like after the stack is unwound due to not catching an exception?
Sam Miller
Like the stack trace I would get from applying gdb to the core file if I didn't intercept terminate() -- as clearly stated in my question.
c-urchin
got it, I misread your question. I've updated my answer.
Sam Miller
Ah, very interesting! So I assume you are claiming that if I run those a.out addresses through addr2line I'll get the function calls on the stack! Perfect!
c-urchin
So it's easier than I thought. This is just the usual way to get the stack from inside the program. I assumed it wouldn't show anything after unwinding, and that is true if you catch the exception and then call backtrace(). But when terminate is called, the stack magically reappears.
c-urchin
A: 

Yet if I simply allow the program to abort(), it will produce a core-dump which contains the full stack information from the point at which the exception was thrown. So the information is there -- but is there a programmatic way to get it, for example so it can be logged, rather than having to examine a core file?

I doubt my experience would fit your needs but here it goes anyway.

I was overloading abort(): either by adding my own object file before the libc or using LD_PRELOAD. In my own version of abort() I was starting the debugger telling it to attach to the process (well, I surely know my PID) and dump the stack trace into a file (commands were passed to the debugger via command line). After debugger had finished, terminate the process with e.g. _exit(100).

That was on Linux using GDB. On Solaris I routinely employ similar trick but due to unavailability of a sane debugger I use the pstack tool: system("pstack <PID>").

Dummy00001