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

我们在像宏这样的专有assert使用堆栈跟踪来捕捉开发人员的错误 - 当发现错误时,会打印堆栈跟踪。

我发现gcc的对backtrace() / backtrace_symbols()方法不够:

  • 名字被破坏
  • 没有线路信息
  • 第一个问题可以通过abi :: __ cxa_demangle解决。

    然而第二个问题更加困难。 我找到了backtrace_symbols()的替代品。 这比gcc的backtrace_symbols()更好,因为它可以检索行号(如果使用-g编译),并且不需要使用-rdynamic编译。

    Hoverer的代码是GNU许可的,所以恕我直言,我不能在商业代码中使用它。

    任何提议?

    PS

    gdb能够打印出传递给函数的参数。 可能它已经太多要求:)

    PS 2

    类似的问题(谢谢nobar)


    不久之前,我回答了类似的问题。 您应该查看方法#4上的源代码,该代码还会打印行号和文件名。


    所以你需要一个独立的函数来打印一个堆栈跟踪,其中包含所有gdb堆栈跟踪具有的功能,并且不会终止你的应用程序。 答案是以非交互模式自动启动gdb以执行您所需的任务。

    这是通过在子进程中执行gdb,使用fork()以及在您的应用程序等待完成时脚本化它来显示堆栈跟踪来完成的。 这可以在不使用核心转储的情况下执行,也不需要中止应用程序。 我从这个问题中学会了如何做到这一点:如何从程序调用gdb来打印它的堆栈跟踪更好?

    这个问题发布的例子并没有像我写的那样正确工作,所以这是我的“固定”版本(我在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);
        }
    }
    

    如引用的问题所示,gdb提供了可以使用的其他选项。 例如,使用“bt full”而不是“bt”会生成更详细的报告(局部变量包含在输出中)。 gdb的manpages很轻便,但完整的文档可以在这里找到。

    由于这是基于gdb的,所以输出包括demangled名字行号函数参数 ,甚至可选的本地变量 。 此外,gdb是线程感知的,所以你应该能够提取一些线程特定的元数据。

    这里有一个我用这种方法看到的堆栈跟踪的例子。

    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
    

    注意:我发现这与使用valgrind不兼容(可能是由于Valgrind使用虚拟机)。 当您在gdb会话中运行程序时,它也不起作用(不能将第二个“ptrace”实例应用于进程)。


    有一个关于本质上相同的问题的强大讨论:如何在我的gcc C ++应用程序崩溃时生成堆栈跟踪。 提供了许多建议,包括关于如何在运行时生成堆栈跟踪的大量讨论。

    我的个人最喜欢的答案是启用核心转储,它允许您在崩溃时查看完整的应用程序状态 (包括函数参数,行号和未加密的名称)。 这种方法的另一个好处是它不仅适用于断言,而且适用于分段错误和未处理的异常。

    不同的Linux shell使用不同的命令来启用核心转储,但是你可以在你的应用程序代码中使用类似这样的方法来完成它...

    #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
    

    崩溃后,运行您最喜欢的调试器来检查程序状态。

    $ kdbg executable core
    

    以下是一些示例输出...

    替代文字

    也可以从命令行的核心转储中提取堆栈跟踪。

    $ ( 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/86019.html

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

    下一篇: Show a caret in a custom textarea without displaying its text