c++exceptionstack-tracecallstackunhandled-exception

c++ stack trace from unhandled exception?


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?


Solution

  • Edited Answer:

    You can use std::set_terminate

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