Tuesday, February 18, 2014

How to obtain C++ application stack trace on Unix

To obtain current execution point stack trace the following code can be used:
#include <execinfo.h>

std::string stacktrace(unsigned int frames_to_skip = 0)
{
    std::string str;

    void* stack_addrs[100];
    int trace_size = backtrace( stack_addrs, 100 );
    char** stack_strings = backtrace_symbols( stack_addrs, trace_size );

    str += "[bt] backtrace:\n";
    // skip frames_to_skip stack frames
    for( int i = frames_to_skip; i < trace_size; ++i )
    {
        char tmp[4096];
        sprintf( tmp, "[bt] #%d %s\n", i-frames_to_skip, stack_strings[i] );
        str += tmp;
    }

    free( stack_strings );

    return str;
}
It can be used for debugging or logging of application abnormal termination. Below is an example of handling and tracing segmentation fault error.

Handle Segmentation Fault error


Application can terminate abnormally with exit code 139. It is mean there is a memory access bug in C++ code.
Unix systems return errorno 128 + signal, when a signal received. Code 139 = 128 + 11. Signal 11 is segmentation violation (SIGSEV).

On POSIX-compatible systems it is possible to register signal handler to catch it and print stacktrace


#include <execinfo.h>
#include <signal.h>

std::string stacktrace(unsigned int frames_to_skip = 0)
{
    std::string str;

    void* stack_addrs[100];
    int trace_size = backtrace( stack_addrs, 100 );
    char** stack_strings = backtrace_symbols( stack_addrs, trace_size );

    str += "[bt] backtrace:\n";
    // skip frames_to_skip stack frames
    for( int i = frames_to_skip; i < trace_size; ++i )
    {
        char tmp[4096];
        sprintf( tmp, "[bt] #%d %s\n", i-frames_to_skip, stack_strings[i] );
        str += tmp;
    }

    free( stack_strings );

    return str;
}

void SignalHandler(int signum)
{
    printf(stacktrace().c_str()); // print stacktrace
    signal(signum, SIG_DFL);      // reinit signal
    exit(139);                    // terminate program with 139 exit code
}


int main(int argc, char *argv[])
{
    signal(SIGSEGV, SignalHandler);
    
    int* p = NULL;
    *p = 1;
}

No comments: