How to get a stack trace for C++ using gcc with line number information?

We use stack traces in proprietary assert like macro to catch developer mistakes - when error is caught, stack trace is printed.

I find gcc's pair backtrace() / backtrace_symbols() methods insufficient:

  • Names are mangled
  • No line information
  • 1st problem can be resolved by abi::__cxa_demangle.

    However 2nd problem s more tough. I found replacement for backtrace_symbols(). This is better than gcc's backtrace_symbols(), since it can retrieve line numbers (if compiled with -g) and you don't need to compile with -rdynamic.

    Hoverer the code is GNU licenced, so IMHO I can't use it in commercial code.

    Any proposal?

    PS

    gdb is capable to print out arguments passed to functions. Probably it's already too much to ask for :)

    PS 2

    Similar question (thanks nobar)


    Not too long ago I answered a similar question. You should take a look at the source code available on method #4, which also prints line numbers and filenames.


    So you want a stand-alone function that prints a stack trace with all of the features that gdb stack traces have and that doesn't terminate your application. The answer is to automate the launch of gdb in a non-interactive mode to perform just the tasks that you want.

    This is done by executing gdb in a child process, using fork(), and scripting it to display a stack-trace while your application waits for it to complete. This can be performed without the use of a core-dump and without aborting the application. I learned how to do this from looking at this question: How it's better to invoke gdb from program to print it's stacktrace?

    The example posted with that question didn't work for me exactly as written, so here's my "fixed" version (I ran this on Ubuntu 9.04).

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    void print_trace() {
        char pid_buf[30];
        sprintf(pid_buf, "%d", getpid());
        char name_buf[512];
        name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
        int child_pid = fork();
        if (!child_pid) {           
            dup2(2,1); // redirect output to stderr
            fprintf(stdout,"stack trace for %s pid=%sn",name_buf,pid_buf);
            execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
            abort(); /* If gdb failed to start */
        } else {
            waitpid(child_pid,NULL,0);
        }
    }
    

    As shown in the referenced question, gdb provides additional options that you could use. For example, using "bt full" instead of "bt" produces an even more detailed report (local variables are included in the output). The manpages for gdb are kind of light, but complete documentation is available here.

    Since this is based on gdb, the output includes demangled names , line-numbers , function arguments , and optionally even local variables . Also, gdb is thread-aware, so you should be able to extract some thread-specific metadata.

    Here's an example of the kind of stack traces that I see with this method.

    0x00007f97e1fc2925 in waitpid () from /lib/libc.so.6
    [Current thread is 0 (process 15573)]
    #0  0x00007f97e1fc2925 in waitpid () from /lib/libc.so.6
    #1  0x0000000000400bd5 in print_trace () at ./demo3b.cpp:496
    2  0x0000000000400c09 in recursive (i=2) at ./demo3b.cpp:636
    3  0x0000000000400c1a in recursive (i=1) at ./demo3b.cpp:646
    4  0x0000000000400c1a in recursive (i=0) at ./demo3b.cpp:646
    5  0x0000000000400c46 in main (argc=1, argv=0x7fffe3b2b5b8) at ./demo3b.cpp:70
    

    Note: I found this to be incompatible with the use of valgrind (probably due to Valgrind's use of a virtual machine). It also doesn't work when you are running the program inside of a gdb session (can't apply a second instance of "ptrace" to a process).


    There is a robust discussion of essentially the same question at: How to generate a stacktrace when my gcc C++ app crashes. Many suggestions are provided, including lots of discussion about how to generate stack traces at run-time.

    My personal favorite answer from that thread was to enable core dumps which allows you to view the complete application state at the time of the crash (including function arguments, line numbers, and unmangled names). An additional benefit of this approach is that it not only works for asserts, but also for segmentation faults and unhandled exceptions.

    Different Linux shells use different commands to enable core dumps, but you can do it from within your application code with something like this...

    #include <sys/resource.h>
    ...
    struct rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY };
    assert( setrlimit( RLIMIT_CORE, &core_limit ) == 0 ); // enable core dumps for debug builds
    

    After a crash, run your favorite debugger to examine the program state.

    $ kdbg executable core
    

    Here's some sample output...

    替代文字

    It is also possible to extract the stack trace from a core dump at the command line.

    $ ( CMDFILE=$(mktemp); echo "bt" >${CMDFILE}; gdb 2>/dev/null --batch -x ${CMDFILE} temp.exe core )
    Core was generated by `./temp.exe'.
    Program terminated with signal 6, Aborted.
    [New process 22857]
    #0  0x00007f4189be5fb5 in raise () from /lib/libc.so.6
    #0  0x00007f4189be5fb5 in raise () from /lib/libc.so.6
    #1  0x00007f4189be7bc3 in abort () from /lib/libc.so.6
    #2  0x00007f4189bdef09 in __assert_fail () from /lib/libc.so.6
    #3  0x00000000004007e8 in recursive (i=5) at ./demo1.cpp:18
    #4  0x00000000004007f3 in recursive (i=4) at ./demo1.cpp:19
    #5  0x00000000004007f3 in recursive (i=3) at ./demo1.cpp:19
    #6  0x00000000004007f3 in recursive (i=2) at ./demo1.cpp:19
    #7  0x00000000004007f3 in recursive (i=1) at ./demo1.cpp:19
    #8  0x00000000004007f3 in recursive (i=0) at ./demo1.cpp:19
    #9  0x0000000000400849 in main (argc=1, argv=0x7fff2483bd98) at ./demo1.cpp:26
    
    链接地址: http://www.djcxy.com/p/86020.html

    上一篇: 如何在gdb中打印长字符串的完整值?

    下一篇: 如何使用gcc和行号信息获取C ++的堆栈跟踪?