简体   繁体   English

在 C 或 C++ 中打印调用堆栈

[英]print call stack in C or C++

Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called?每次调用某个函数时,有没有办法将调用堆栈转储到 C 或 C++ 中正在运行的进程中? What I have in mind is something like this:我的想法是这样的:

void foo()
{
   print_stack_trace();

   // foo's body

   return
}

Where print_stack_trace works similarly to caller in Perl.其中print_stack_trace的工作方式与 Perl 中的caller类似。

Or something like this:或者是这样的:

int main (void)
{
    // will print out debug info every time foo() is called
    register_stack_trace_function(foo); 

    // etc...
}

where register_stack_trace_function puts some sort of internal breakpoint that will cause a stack trace to be printed whenever foo is called.其中register_stack_trace_function放置了某种内部断点,这将导致在调用foo时打印堆栈跟踪。

Does anything like this exist in some standard C library?某些标准 C 库中是否存在类似的东西?

I am working on Linux, using GCC.我正在使用 GCC 在 Linux 上工作。


Background背景

I have a test run that behaves differently based on some commandline switches that shouldn't affect this behavior.我有一个测试运行,它基于一些不应影响此行为的命令行开关而表现不同。 My code has a pseudo-random number generator that I assume is being called differently based on these switches.我的代码有一个伪随机数生成器,我假设它根据这些开关被不同地调用。 I want to be able to run the test with each set of switches and see if the random number generator is called differently for each one.我希望能够使用每组开关运行测试,看看随机数生成器是否对每个开关都有不同的调用。

For a linux-only solution you can use backtrace(3) that simply returns an array of void * (in fact each of these point to the return address from the corresponding stack frame).对于仅限 linux 的解决方案,您可以使用backtrace(3) ,它只返回一个void *数组(实际上每个都指向相应堆栈帧的返回地址)。 To translate these to something of use, there's backtrace_symbols(3) .要将这些转换为有用的东西,可以使用backtrace_symbols(3)

Pay attention to the notes section in backtrace(3) :注意backtrace(3) 中的注释部分

The symbol names may be unavailable without the use of special linker options.如果不使用特殊的链接器选项,符号名称可能不可用。 For systems using the GNU linker, it is necessary to use the -rdynamic linker option.对于使用 GNU 链接器的系统,必须使用 -rdynamic 链接器选项。 Note that names of "static" functions are not exposed, and won't be available in the backtrace.请注意,“静态”函数的名称不会公开,并且不会在回溯中可用。

Boost stacktrace提升堆栈跟踪

Documented at: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack记录在: https ://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack

This is the most convenient option I've seen so far, because it:这是迄今为止我见过的最方便的选择,因为它:

  • can actually print out the line numbers.实际上可以打印出行号。

    It just makes calls to addr2line however , which adds an ugly external dependency and will considerably slow down your code if you are making lots of traces 然而,它只是调用addr2line ,这会增加一个丑陋的外部依赖关系,如果您要进行大量跟踪,则会大大减慢您的代码速度

  • demangles by default默认解开

  • Boost is header only, so no need to modify your build system most likely Boost 只是标题,因此很可能不需要修改您的构建系统

boost_stacktrace.cpp boost_stacktrace.cpp

#include <iostream>

#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>

void my_func_2(void) {
    std::cout << boost::stacktrace::stacktrace() << std::endl;
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main(int argc, char **argv) {
    long long unsigned int n;
    if (argc > 1) {
        n = strtoul(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    for (long long unsigned int i = 0; i < n; ++i) {
        my_func_1(1);   // line 28
        my_func_1(2.0); // line 29
    }
}

Unfortunately, it seems to be a more recent addition, and the package libboost-stacktrace-dev is not present in Ubuntu 16.04, only 18.04:不幸的是,它似乎是最近添加的,并且包libboost-stacktrace-dev在 Ubuntu 16.04 中不存在,只有 18.04:

sudo apt-get install libboost-stacktrace-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o boost_stacktrace.out -std=c++11 \
  -Wall -Wextra -pedantic-errors boost_stacktrace.cpp -ldl
./boost_stacktrace.out

We have to add -ldl at the end or else compilation fails.我们必须在最后添加-ldl否则编译失败。

Output:输出:

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
 1# my_func_1(int) at /home/ciro/test/boost_stacktrace.cpp:18
 2# main at /home/ciro/test/boost_stacktrace.cpp:29 (discriminator 2)
 3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 4# _start in ./boost_stacktrace.out

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
 1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:13
 2# main at /home/ciro/test/boost_stacktrace.cpp:27 (discriminator 2)
 3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 4# _start in ./boost_stacktrace.out

The output and is further explained on the "glibc backtrace" section below, which is analogous.输出并在下面的“glibc 回溯”部分进一步解释,这是类似的。

Note how my_func_1(int) and my_func_1(float) , which are mangled due to function overload , were nicely demangled for us.请注意my_func_1(int)my_func_1(float) 是如何因函数重载而被破坏的,它们是如何为我们很好地分解的。

Note that the first int calls is off by one line (28 instead of 27 and the second one is off by two lines (27 instead of 29). It was suggested in the comments that this is because the following instruction address is being considered, which makes 27 become 28, and 29 jump off the loop and become 27.请注意,第一个int调用关闭了一行(28 而不是 27,第二个调用关闭了两行(27 而不是 29)。 评论中建议这是因为正在考虑以下指令地址,这使得 27 变为 28,而 29 跳出循环并变为 27。

We then observe that with -O3 , the output is completely mutilated:然后我们观察到使用-O3 ,输出被完全破坏:

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
 1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:12
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in ./boost_stacktrace.out

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
 1# main at /home/ciro/test/boost_stacktrace.cpp:31
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in ./boost_stacktrace.out

Backtraces are in general irreparably mutilated by optimizations.回溯通常被优化不可挽回地破坏。 Tail call optimization is a notable example of that: What is tail call optimization?尾调用优化就是一个值得注意的例子: 什么是尾调用优化?

Benchmark run on -O3 :-O3上运行基准测试:

time  ./boost_stacktrace.out 1000 >/dev/null

Output:输出:

real    0m43.573s
user    0m30.799s
sys     0m13.665s

So as expected, we see that this method is extremely slow likely to to external calls to addr2line , and is only going to be feasible if a limited number of calls are being made.因此,正如预期的那样,我们看到这种方法对于外部调用addr2line可能非常慢,并且只有在进行有限数量的调用时才可行。

Each backtrace print seems to take hundreds of milliseconds, so be warned that if a backtrace happens very often, program performance will suffer significantly.每次回溯打印似乎需要数百毫秒,因此请注意,如果回溯经常发生,程序性能将受到严重影响。

Tested on Ubuntu 19.10, GCC 9.2.1, boost 1.67.0.在 Ubuntu 19.10、GCC 9.2.1、boost 1.67.0 上测试。

glibc backtrace glibc backtrace

Documented at: https://www.gnu.org/software/libc/manual/html_node/Backtraces.html记录在: https ://www.gnu.org/software/libc/manual/html_node/Backtraces.html

main.c主程序

#include <stdio.h>
#include <stdlib.h>

/* Paste this on the file you want to debug. */
#include <stdio.h>
#include <execinfo.h>
void print_trace(void) {
    char **strings;
    size_t i, size;
    enum Constexpr { MAX_SIZE = 1024 };
    void *array[MAX_SIZE];
    size = backtrace(array, MAX_SIZE);
    strings = backtrace_symbols(array, size);
    for (i = 0; i < size; i++)
        printf("%s\n", strings[i]);
    puts("");
    free(strings);
}

void my_func_3(void) {
    print_trace();
}

void my_func_2(void) {
    my_func_3();
}

void my_func_1(void) {
    my_func_3();
}

int main(void) {
    my_func_1(); /* line 33 */
    my_func_2(); /* line 34 */
    return 0;
}

Compile:编译:

gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -rdynamic -std=c99 \
  -Wall -Wextra -pedantic-errors main.c

-rdynamic is the key required option. -rdynamic是关键的必需选项。

Run:跑:

./main.out

Outputs:输出:

./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0x9) [0x4008f9]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]

./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0xe) [0x4008fe]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]

So we immediately see that an inlining optimization happened, and some functions were lost from the trace.所以我们立即看到发生了内联优化,一些函数从跟踪中丢失了。

If we try to get the addresses:如果我们尝试获取地址:

addr2line -e main.out 0x4008f9 0x4008fe

we obtain:我们获得:

/home/ciro/main.c:21
/home/ciro/main.c:36

which is completely off.这是完全关闭的。

If we do the same with -O0 instead, ./main.out gives the correct full trace:如果我们对-O0做同样的事情,. ./main.out会给出正确的完整跟踪:

./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_1+0x9) [0x400a68]
./main.out(main+0x9) [0x400a74]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]

./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_2+0x9) [0x400a5c]
./main.out(main+0xe) [0x400a79]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]

and then:接着:

addr2line -e main.out 0x400a74 0x400a79

gives:给出:

/home/cirsan01/test/main.c:34
/home/cirsan01/test/main.c:35

so the lines are off by just one, TODO why?所以线只差了一条,TODO 为什么? But this might still be usable.但这可能仍然可用。

Conclusion: backtraces can only possibly show perfectly with -O0 .结论:回溯只能用-O0完美显示。 With optimizations, the original backtrace is fundamentally modified in the compiled code.通过优化,原始回溯在编译后的代码中得到了根本性的修改。

I couldn't find a simple way to automatically demangle C++ symbols with this however, here are some hacks:但是,我找不到一种简单的方法来自动对 C++ 符号进行解码,这里有一些技巧:

Tested on Ubuntu 16.04, GCC 6.4.0, libc 2.23.在 Ubuntu 16.04、GCC 6.4.0、libc 2.23 上测试。

glibc backtrace_symbols_fd glibc backtrace_symbols_fd

This helper is a bit more convenient than backtrace_symbols , and produces basically identical output:这个助手比backtrace_symbols方便一点,并且产生基本相同的输出:

/* Paste this on the file you want to debug. */
#include <execinfo.h>
#include <stdio.h>
#include <unistd.h>
void print_trace(void) {
    size_t i, size;
    enum Constexpr { MAX_SIZE = 1024 };
    void *array[MAX_SIZE];
    size = backtrace(array, MAX_SIZE);
    backtrace_symbols_fd(array, size, STDOUT_FILENO);
    puts("");
}

Tested on Ubuntu 16.04, GCC 6.4.0, libc 2.23.在 Ubuntu 16.04、GCC 6.4.0、libc 2.23 上测试。

glibc backtrace with C++ demangling hack 1: -export-dynamic + dladdr glibc backtrace与 C++ 解组技巧 1: -export-dynamic + dladdr

Adapted from: https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3改编自: https ://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3

This is a "hack" because it requires changing the ELF with -export-dynamic .这是一个“hack”,因为它需要使用-export-dynamic更改 ELF。

glibc_ldl.cpp glibc_ldl.cpp

#include <dlfcn.h>     // for dladdr
#include <cxxabi.h>    // for __cxa_demangle

#include <cstdio>
#include <string>
#include <sstream>
#include <iostream>

// This function produces a stack backtrace with demangled function & method names.
std::string backtrace(int skip = 1)
{
    void *callstack[128];
    const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
    char buf[1024];
    int nFrames = backtrace(callstack, nMaxFrames);
    char **symbols = backtrace_symbols(callstack, nFrames);

    std::ostringstream trace_buf;
    for (int i = skip; i < nFrames; i++) {
        Dl_info info;
        if (dladdr(callstack[i], &info)) {
            char *demangled = NULL;
            int status;
            demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
            std::snprintf(
                buf,
                sizeof(buf),
                "%-3d %*p %s + %zd\n",
                i,
                (int)(2 + sizeof(void*) * 2),
                callstack[i],
                status == 0 ? demangled : info.dli_sname,
                (char *)callstack[i] - (char *)info.dli_saddr
            );
            free(demangled);
        } else {
            std::snprintf(buf, sizeof(buf), "%-3d %*p\n",
                i, (int)(2 + sizeof(void*) * 2), callstack[i]);
        }
        trace_buf << buf;
        std::snprintf(buf, sizeof(buf), "%s\n", symbols[i]);
        trace_buf << buf;
    }
    free(symbols);
    if (nFrames == nMaxFrames)
        trace_buf << "[truncated]\n";
    return trace_buf.str();
}

void my_func_2(void) {
    std::cout << backtrace() << std::endl;
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

Compile and run:编译并运行:

g++ -fno-pie -ggdb3 -O0 -no-pie -o glibc_ldl.out -std=c++11 -Wall -Wextra \
  -pedantic-errors -fpic glibc_ldl.cpp -export-dynamic -ldl
./glibc_ldl.out 

output:输出:

1             0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2             0x40139e my_func_1(int) + 16
./glibc_ldl.out(_Z9my_func_1i+0x10) [0x40139e]
3             0x4013b3 main + 18
./glibc_ldl.out(main+0x12) [0x4013b3]
4       0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5             0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]

1             0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2             0x40138b my_func_1(double) + 18
./glibc_ldl.out(_Z9my_func_1d+0x12) [0x40138b]
3             0x4013c8 main + 39
./glibc_ldl.out(main+0x27) [0x4013c8]
4       0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5             0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]

Tested on Ubuntu 18.04.在 Ubuntu 18.04 上测试。

glibc backtrace with C++ demangling hack 2: parse backtrace output glibc backtrace with C++ demangling hack 2: parse backtrace output

Shown at: https://panthema.net/2008/0901-stacktrace-demangled/显示在: https ://panthema.net/2008/0901-stacktrace-demangled/

This is a hack because it requires parsing.这是一个 hack,因为它需要解析。

TODO get it to compile and show it here. TODO 让它编译并在此处显示。

libunwind自由风

TODO does this have any advantage over glibc backtrace? TODO 这比 glibc 回溯有什么优势吗? Very similar output, also requires modifying the build command, but not part of glibc so requires an extra package installation.非常相似的输出,也需要修改构建命令,但不是 glibc 的一部分,因此需要额外的包安装。

Code adapted from: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/代码改编自: https ://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

main.c主程序

/* This must be on top. */
#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>

/* Paste this on the file you want to debug. */
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>
void print_trace() {
    char sym[256];
    unw_context_t context;
    unw_cursor_t cursor;
    unw_getcontext(&context);
    unw_init_local(&cursor, &context);
    while (unw_step(&cursor) > 0) {
        unw_word_t offset, pc;
        unw_get_reg(&cursor, UNW_REG_IP, &pc);
        if (pc == 0) {
            break;
        }
        printf("0x%lx:", pc);
        if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
            printf(" (%s+0x%lx)\n", sym, offset);
        } else {
            printf(" -- error: unable to obtain symbol name for this frame\n");
        }
    }
    puts("");
}

void my_func_3(void) {
    print_trace();
}

void my_func_2(void) {
    my_func_3();
}

void my_func_1(void) {
    my_func_3();
}

int main(void) {
    my_func_1(); /* line 46 */
    my_func_2(); /* line 47 */
    return 0;
}

Compile and run:编译并运行:

sudo apt-get install libunwind-dev
gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -std=c99 \
  -Wall -Wextra -pedantic-errors main.c -lunwind

Either #define _XOPEN_SOURCE 700 must be on top, or we must use -std=gnu99 : #define _XOPEN_SOURCE 700必须在顶部,或者我们必须使用-std=gnu99

Run:跑:

./main.out

Output:输出:

0x4007db: (main+0xb)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)

0x4007e2: (main+0x12)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)

and:和:

addr2line -e main.out 0x4007db 0x4007e2

gives:给出:

/home/ciro/main.c:34
/home/ciro/main.c:49

With -O0 :使用-O0

0x4009cf: (my_func_3+0xe)
0x4009e7: (my_func_1+0x9)
0x4009f3: (main+0x9)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)

0x4009cf: (my_func_3+0xe)
0x4009db: (my_func_2+0x9)
0x4009f8: (main+0xe)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)

and:和:

addr2line -e main.out 0x4009f3 0x4009f8

gives:给出:

/home/ciro/main.c:47
/home/ciro/main.c:48

Tested on Ubuntu 16.04, GCC 6.4.0, libunwind 1.1.在 Ubuntu 16.04、GCC 6.4.0、libunwind 1.1 上测试。

libunwind with C++ name demangling带有 C++ 名称的 libunwind

Code adapted from: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/代码改编自: https ://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

unwind.cpp展开.cpp

#define UNW_LOCAL_ONLY
#include <cxxabi.h>
#include <libunwind.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>

void backtrace() {
  unw_cursor_t cursor;
  unw_context_t context;

  // Initialize cursor to current frame for local unwinding.
  unw_getcontext(&context);
  unw_init_local(&cursor, &context);

  // Unwind frames one by one, going up the frame stack.
  while (unw_step(&cursor) > 0) {
    unw_word_t offset, pc;
    unw_get_reg(&cursor, UNW_REG_IP, &pc);
    if (pc == 0) {
      break;
    }
    std::printf("0x%lx:", pc);

    char sym[256];
    if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
      char* nameptr = sym;
      int status;
      char* demangled = abi::__cxa_demangle(sym, nullptr, nullptr, &status);
      if (status == 0) {
        nameptr = demangled;
      }
      std::printf(" (%s+0x%lx)\n", nameptr, offset);
      std::free(demangled);
    } else {
      std::printf(" -- error: unable to obtain symbol name for this frame\n");
    }
  }
}

void my_func_2(void) {
    backtrace();
    std::cout << std::endl; // line 43
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}  // line 54

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

Compile and run:编译并运行:

sudo apt-get install libunwind-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o unwind.out -std=c++11 \
  -Wall -Wextra -pedantic-errors unwind.cpp -lunwind -pthread
./unwind.out

Output:输出:

0x400c80: (my_func_2()+0x9)
0x400cb7: (my_func_1(int)+0x10)
0x400ccc: (main+0x12)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)

0x400c80: (my_func_2()+0x9)
0x400ca4: (my_func_1(double)+0x12)
0x400ce1: (main+0x27)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)

and then we can find the lines of my_func_2 and my_func_1(int) with:然后我们可以找到my_func_2my_func_1(int)的行:

addr2line -e unwind.out 0x400c80 0x400cb7

which gives:这使:

/home/ciro/test/unwind.cpp:43
/home/ciro/test/unwind.cpp:54

TODO: why are the lines off by one?待办事项:为什么线条会偏离一条?

Tested on Ubuntu 18.04, GCC 7.4.0, libunwind 1.2.1.在 Ubuntu 18.04、GCC 7.4.0、libunwind 1.2.1 上测试。

GDB automation GDB 自动化

We can also do this with GDB without recompiling by using: How to do an specific action when a certain breakpoint is hit in GDB?我们也可以在不重新编译的情况下使用 GDB 执行此操作: 如何在 GDB 中命中某个断点时执行特定操作?

Although if you are going to print the backtrace a lot, this will likely be less fast than the other options, but maybe we can reach native speeds with compile code , but I'm lazy to test it out now: How to call assembly in gdb?虽然如果你要打印很多回溯,这可能会比其他选项快,但也许我们可以通过compile code达到本机速度,但我现在懒得测试它: How to call assembly in数据库?

main.cpp主文件

void my_func_2(void) {}

void my_func_1(double f) {
    my_func_2();
}

void my_func_1(int i) {
    my_func_2();
}

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

main.gdb主数据库

start
break my_func_2
commands
  silent
  backtrace
  printf "\n"
  continue
end
continue

Compile and run:编译并运行:

g++ -ggdb3 -o main.out main.cpp
gdb -nh -batch -x main.gdb main.out

Output:输出:

Temporary breakpoint 1 at 0x1158: file main.cpp, line 12.

Temporary breakpoint 1, main () at main.cpp:12
12          my_func_1(1);
Breakpoint 2 at 0x555555555129: file main.cpp, line 1.
#0  my_func_2 () at main.cpp:1
#1  0x0000555555555151 in my_func_1 (i=1) at main.cpp:8
#2  0x0000555555555162 in main () at main.cpp:12

#0  my_func_2 () at main.cpp:1
#1  0x000055555555513e in my_func_1 (f=2) at main.cpp:4
#2  0x000055555555516f in main () at main.cpp:13

[Inferior 1 (process 14193) exited normally]

TODO I wanted to do this with just -ex from the command line to not have to create main.gdb but I couldn't get the commands to work there. TODO 我想只用命令行中的-ex来执行此操作,而不必创建main.gdb ,但我无法让commands在那里工作。

Tested in Ubuntu 19.04, GDB 8.2.在 Ubuntu 19.04、GDB 8.2 中测试。

Linux kernel Linux内核

How to print the current thread stack trace inside the Linux kernel? 如何在 Linux 内核中打印当前线程堆栈跟踪?

libdwfl libdwfl

This was originally mentioned at: https://stackoverflow.com/a/60713161/895245 and it might be the best method, but I have to benchmark a bit more, but please go upvote that answer.这最初是在: https ://stackoverflow.com/a/60713161/895245 中提到的,它可能是最好的方法,但我必须进行更多基准测试,但请投票赞成这个答案。

TODO: I tried to minimize the code in that answer, which was working, to a single function, but it is segfaulting, let me know if anyone can find why. TODO:我试图将那个有效的答案中的代码最小化为一个函数,但它是段错误的,如果有人能找到原因,请告诉我。

dwfl.cpp dwfl.cpp

#include <cassert>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>

#include <cxxabi.h> // __cxa_demangle
#include <elfutils/libdwfl.h> // Dwfl*
#include <execinfo.h> // backtrace
#include <unistd.h> // getpid

// https://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname
std::string demangle(const char* name) {
    int status = -4;
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };
    return (status==0) ? res.get() : name ;
}

std::string debug_info(Dwfl* dwfl, void* ip) {
    std::string function;
    int line = -1;
    char const* file;
    uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
    Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
    char const* name = dwfl_module_addrname(module, ip2);
    function = name ? demangle(name) : "<unknown>";
    if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
        Dwarf_Addr addr;
        file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
    }
    std::stringstream ss;
    ss << ip << ' ' << function;
    if (file)
        ss << " at " << file << ':' << line;
    ss << std::endl;
    return ss.str();
}

std::string stacktrace() {
    // Initialize Dwfl.
    Dwfl* dwfl = nullptr;
    {
        Dwfl_Callbacks callbacks = {};
        char* debuginfo_path = nullptr;
        callbacks.find_elf = dwfl_linux_proc_find_elf;
        callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
        callbacks.debuginfo_path = &debuginfo_path;
        dwfl = dwfl_begin(&callbacks);
        assert(dwfl);
        int r;
        r = dwfl_linux_proc_report(dwfl, getpid());
        assert(!r);
        r = dwfl_report_end(dwfl, nullptr, nullptr);
        assert(!r);
        static_cast<void>(r);
    }

    // Loop over stack frames.
    std::stringstream ss;
    {
        void* stack[512];
        int stack_size = ::backtrace(stack, sizeof stack / sizeof *stack);
        for (int i = 0; i < stack_size; ++i) {
            ss << i << ": ";

            // Works.
            ss << debug_info(dwfl, stack[i]);

#if 0
            // TODO intended to do the same as above, but segfaults,
            // so possibly UB In above function that does not blow up by chance?
            void *ip = stack[i];
            std::string function;
            int line = -1;
            char const* file;
            uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
            Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
            char const* name = dwfl_module_addrname(module, ip2);
            function = name ? demangle(name) : "<unknown>";
            // TODO if I comment out this line it does not blow up anymore.
            if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
              Dwarf_Addr addr;
              file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
            }
            ss << ip << ' ' << function;
            if (file)
                ss << " at " << file << ':' << line;
            ss << std::endl;
#endif
        }
    }
    dwfl_end(dwfl);
    return ss.str();
}

void my_func_2() {
    std::cout << stacktrace() << std::endl;
    std::cout.flush();
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main(int argc, char **argv) {
    long long unsigned int n;
    if (argc > 1) {
        n = strtoul(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    for (long long unsigned int i = 0; i < n; ++i) {
        my_func_1(1);   // line 122
        my_func_1(2.0); // line 123
    }
}

Compile and run:编译并运行:

sudo apt install libdw-dev libunwind-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw -lunwind
./dwfl.out

We also need libunwind as that makes results more correct.我们还需要 libunwind,因为这会使结果更正确。 If you do without it, it runs, but you will see that some of the lines are a bit wrong.如果没有它,它会运行,但你会发现有些行有点错误。

Output:输出:

0: 0x402b72 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402cda my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d76 my_func_1(int) at /home/ciro/test/dwfl.cpp:111
3: 0x402dd1 main at /home/ciro/test/dwfl.cpp:122
4: 0x7ff227ea0d8f __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58
5: 0x7ff227ea0e3f __libc_start_main@@GLIBC_2.34 at ../csu/libc-start.c:392
6: 0x402534 _start at ../csu/libc-start.c:-1

0: 0x402b72 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402cda my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d5f my_func_1(double) at /home/ciro/test/dwfl.cpp:106
3: 0x402de2 main at /home/ciro/test/dwfl.cpp:123
4: 0x7ff227ea0d8f __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58
5: 0x7ff227ea0e3f __libc_start_main@@GLIBC_2.34 at ../csu/libc-start.c:392
6: 0x402534 _start at ../csu/libc-start.c:-1

Benchmark run:基准运行:

g++ -fno-pie -ggdb3 -O3 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw
time ./dwfl.out 1000 >/dev/null

Output:输出:

real    0m3.751s
user    0m2.822s
sys     0m0.928s

So we see that this method is 10x faster than Boost's stacktrace, and might therefore be applicable to more use cases.所以我们看到这种方法比 Boost 的堆栈跟踪快 10 倍,因此可能适用于更多用例。

Tested in Ubuntu 22.04 amd64, libdw-dev 0.186, libunwind 1.3.2.在 Ubuntu 22.04 amd64、libdw-dev 0.186、libunwind 1.3.2 中测试。

libbacktrace libbacktrace

https://github.com/ianlancetaylor/libbacktrace https://github.com/ianlancetaylor/libbacktrace

Considering the harcore library author, it is worth trying this out, maybe it is The One.考虑到 harcore 库作者,值得尝试一下,也许是 The One。 TODO check it out.待办事项检查一下。

AC library that may be linked into a C/C++ program to produce symbolic backtraces可以链接到 C/C++ 程序以产生符号回溯的 AC 库

As of October 2020, libbacktrace supports ELF, PE/COFF, Mach-O, and XCOFF executables with DWARF debugging information.截至 2020 年 10 月,libbacktrace 支持带有 DWARF 调试信息的 ELF、PE/COFF、Mach-O 和 XCOFF 可执行文件。 In other words, it supports GNU/Linux, *BSD, macOS, Windows, and AIX.换句话说,它支持 GNU/Linux、*BSD、macOS、Windows 和 AIX。 The library is written to make it straightforward to add support for other object file and debugging formats.编写该库是为了方便地添加对其他目标文件和调试格式的支持。

The library relies on the C++ unwind API defined at https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html This API is provided by GCC and clang.该库依赖于https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html中定义的 C++ 展开 API。此 API 由 GCC 和 clang 提供。

See also也可以看看

Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called?每次调用某个函数时,有没有办法将调用堆栈转储到 C 或 C++ 中正在运行的进程中?

You can use a macro function instead of return statement in the specific function.您可以在特定函数中使用宏函数代替 return 语句。

For example, instead of using return,例如,不使用 return,

int foo(...)
{
    if (error happened)
        return -1;

    ... do something ...

    return 0
}

You can use a macro function.您可以使用宏功能。

#include "c-callstack.h"

int foo(...)
{
    if (error happened)
        NL_RETURN(-1);

    ... do something ...

    NL_RETURN(0);
}

Whenever an error happens in a function, you will see Java-style call stack as shown below.每当函数中发生错误时,您将看到 Java 风格的调用堆栈,如下所示。

Error(code:-1) at : so_topless_ranking_server (sample.c:23)
Error(code:-1) at : nanolat_database (sample.c:31)
Error(code:-1) at : nanolat_message_queue (sample.c:39)
Error(code:-1) at : main (sample.c:47)

Full source code is available here.完整的源代码可在此处获得。

c-callstack at https://github.com/Nanolat c-callstack 在 https://github.com/Nanolat

Linux specific, TLDR:特定于 Linux 的 TLDR:

  1. backtrace in glibc produces accurate stacktraces only when -lunwind is linked (undocumented platform-specific feature). glibc中的backtrace仅在链接-lunwind时产生准确的堆栈跟踪(未记录的平台特定功能)。
  2. To output function name , source file and line number use #include <elfutils/libdwfl.h> (this library is documented only in its header file).要输出函数名称源文件行号,请​​使用#include <elfutils/libdwfl.h> (此库仅记录在其头文件中)。 backtrace_symbols and backtrace_symbolsd_fd are least informative. backtrace_symbolsbacktrace_symbolsd_fd信息量最少。

On modern Linux your can get the stacktrace addresses using function backtrace .在现代 Linux 上,您可以使用函数backtrace获取堆栈跟踪地址。 The undocumented way to make backtrace produce more accurate addresses on popular platforms is to link with -lunwind ( libunwind-dev on Ubuntu 18.04) (see the example output below).使backtrace在流行平台上产生更准确地址的未记录方法是链接-lunwind (Ubuntu 18.04 上libunwind-dev )(参见下面的示例输出)。 backtrace uses function _Unwind_Backtrace and by default the latter comes from libgcc_s.so.1 and that implementation is most portable. backtrace使用函数_Unwind_Backtrace并且默认情况下后者来自libgcc_s.so.1并且该实现是最可移植的。 When -lunwind is linked it provides a more accurate version of _Unwind_Backtrace but this library is less portable (see supported architectures in libunwind/src ).-lunwind被链接时,它提供了更准确的_Unwind_Backtrace版本,但这个库的可移植性较差(请参阅libunwind/src中支持的架构)。

Unfortunately, the companion backtrace_symbolsd and backtrace_symbols_fd functions have not been able to resolve the stacktrace addresses to function names with source file name and line number for probably a decade now (see the example output below).不幸的是,伴随的backtrace_symbolsdbacktrace_symbols_fd函数可能十年来一直无法将堆栈跟踪地址解析为具有源文件名和行号的函数名(请参见下面的示例输出)。

However, there is another method to resolve addresses to symbols and it produces the most useful traces with function name , source file and line number .但是,还有另一种方法可以将地址解析为符号,它会产生最有用的跟踪,包括函数名源文件行号 The method is to #include <elfutils/libdwfl.h> and link with -ldw ( libdw-dev on Ubuntu 18.04).方法是#include <elfutils/libdwfl.h>并与-ldw链接(Ubuntu 18.04 上的 libdw libdw-dev )。

Working C++ example ( test.cc ):工作 C++ 示例( test.cc ):

#include <stdexcept>
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <string>

#include <boost/core/demangle.hpp>

#include <execinfo.h>
#include <elfutils/libdwfl.h>

struct DebugInfoSession {
    Dwfl_Callbacks callbacks = {};
    char* debuginfo_path = nullptr;
    Dwfl* dwfl = nullptr;

    DebugInfoSession() {
        callbacks.find_elf = dwfl_linux_proc_find_elf;
        callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
        callbacks.debuginfo_path = &debuginfo_path;

        dwfl = dwfl_begin(&callbacks);
        assert(dwfl);

        int r;
        r = dwfl_linux_proc_report(dwfl, getpid());
        assert(!r);
        r = dwfl_report_end(dwfl, nullptr, nullptr);
        assert(!r);
        static_cast<void>(r);
    }

    ~DebugInfoSession() {
        dwfl_end(dwfl);
    }

    DebugInfoSession(DebugInfoSession const&) = delete;
    DebugInfoSession& operator=(DebugInfoSession const&) = delete;
};

struct DebugInfo {
    void* ip;
    std::string function;
    char const* file;
    int line;

    DebugInfo(DebugInfoSession const& dis, void* ip)
        : ip(ip)
        , file()
        , line(-1)
    {
        // Get function name.
        uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
        Dwfl_Module* module = dwfl_addrmodule(dis.dwfl, ip2);
        char const* name = dwfl_module_addrname(module, ip2);
        function = name ? boost::core::demangle(name) : "<unknown>";

        // Get source filename and line number.
        if(Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
            Dwarf_Addr addr;
            file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
        }
    }
};

std::ostream& operator<<(std::ostream& s, DebugInfo const& di) {
    s << di.ip << ' ' << di.function;
    if(di.file)
        s << " at " << di.file << ':' << di.line;
    return s;
}

void terminate_with_stacktrace() {
    void* stack[512];
    int stack_size = ::backtrace(stack, sizeof stack / sizeof *stack);

    // Print the exception info, if any.
    if(auto ex = std::current_exception()) {
        try {
            std::rethrow_exception(ex);
        }
        catch(std::exception& e) {
            std::cerr << "Fatal exception " << boost::core::demangle(typeid(e).name()) << ": " << e.what() << ".\n";
        }
        catch(...) {
            std::cerr << "Fatal unknown exception.\n";
        }
    }

    DebugInfoSession dis;
    std::cerr << "Stacktrace of " << stack_size << " frames:\n";
    for(int i = 0; i < stack_size; ++i) {
        std::cerr << i << ": " << DebugInfo(dis, stack[i]) << '\n';
    }
    std::cerr.flush();

    std::_Exit(EXIT_FAILURE);
}

int main() {
    std::set_terminate(terminate_with_stacktrace);
    throw std::runtime_error("test exception");
}

Compiled on Ubuntu 18.04.4 LTS with gcc-8.3:使用 gcc-8.3 在 Ubuntu 18.04.4 LTS 上编译:

g++ -o test.o -c -m{arch,tune}=native -std=gnu++17 -W{all,extra,error} -g -Og -fstack-protector-all test.cc
g++ -o test -g test.o -ldw -lunwind

Outputs:输出:

Fatal exception std::runtime_error: test exception.
Stacktrace of 7 frames:
0: 0x55f3837c1a8c terminate_with_stacktrace() at /home/max/src/test/test.cc:76
1: 0x7fbc1c845ae5 <unknown>
2: 0x7fbc1c845b20 std::terminate()
3: 0x7fbc1c845d53 __cxa_throw
4: 0x55f3837c1a43 main at /home/max/src/test/test.cc:103
5: 0x7fbc1c3e3b96 __libc_start_main at ../csu/libc-start.c:310
6: 0x55f3837c17e9 _start

When no -lunwind is linked, it produces a less accurate stacktrace:当没有链接-lunwind时,它会产生不太准确的堆栈跟踪:

0: 0x5591dd9d1a4d terminate_with_stacktrace() at /home/max/src/test/test.cc:76
1: 0x7f3c18ad6ae6 <unknown>
2: 0x7f3c18ad6b21 <unknown>
3: 0x7f3c18ad6d54 <unknown>
4: 0x5591dd9d1a04 main at /home/max/src/test/test.cc:103
5: 0x7f3c1845cb97 __libc_start_main at ../csu/libc-start.c:344
6: 0x5591dd9d17aa _start

For comparison, backtrace_symbols_fd output for the same stacktrace is least informative:为了比较,相同堆栈跟踪的backtrace_symbols_fd输出信息最少:

/home/max/src/test/debug/gcc/test(+0x192f)[0x5601c5a2092f]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92ae5)[0x7f95184f5ae5]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZSt9terminatev+0x10)[0x7f95184f5b20]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(__cxa_throw+0x43)[0x7f95184f5d53]
/home/max/src/test/debug/gcc/test(+0x1ae7)[0x5601c5a20ae7]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe6)[0x7f9518093b96]
/home/max/src/test/debug/gcc/test(+0x1849)[0x5601c5a20849]

In a production version (as well as C language version) you may like to make this code extra robust by replacing boost::core::demangle , std::string and std::cout with their underlying calls.在生产版本(以及 C 语言版本)中,您可能希望通过将boost::core::demanglestd::stringstd::cout替换为其底层调用来使此代码更加健壮。

You can also override __cxa_throw to capture the stacktrace when an exception is thrown and print it when the exception is caught.您还可以覆盖__cxa_throw以在引发异常时捕获堆栈跟踪,并在捕获异常时打印它。 By the time it enters catch block the stack has been unwound, so it is too late to call backtrace , and this is why the stack must be captured on throw which is implemented by function __cxa_throw .当它进入catch块时,堆栈已经展开,所以调用backtrace为时已晚,这就是为什么必须在throw时捕获堆栈,这由函数__cxa_throw实现。 Note that in a multi-threaded program __cxa_throw can be called simultaneously by multiple threads, so that if it captures the stacktrace into a global array that must be thread_local .请注意,在多线程程序__cxa_throw可以由多个线程同时调用,因此如果它将堆栈跟踪捕获到必须是thread_local的全局数组中。

You can also make the stack trace printing function async-signal safe , so that you can invoke it directly from your SIGSEGV , SIGBUS signal handlers (which should use their own stacks for robustness).您还可以使堆栈跟踪打印功能async-signal safe ,以便您可以直接从您的SIGSEGVSIGBUS信号处理程序调用它(它们应该使用它们自己的堆栈以实现健壮性)。 Obtaining function name , source file and line number using libdwfl from a signal handler may fail because it is not async-signal safe or if the address space of the process has been substantially corrupted, but in practice it succeeds 99% of the time (I haven't seen it fail).使用libdwfl从信号处理程序获取函数名称源文件行号可能会失败,因为它不是异步信号安全的,或者如果进程的地址空间已被严重损坏,但实际上它成功率高达 99%(I没有看到它失败)。


To summarize, a complete production-ready library for automatic stacktrace output should:总而言之,用于自动堆栈跟踪输出的完整生产就绪库应该:

  1. Capture the stacktrace on throw into thread-specific storage.throw到特定于线程的存储时捕获堆栈跟踪。
  2. Automatically print the stacktrace on unhandled exceptions.在未处理的异常上自动打印堆栈跟踪。
  3. Print the stacktrace in async-signal-safe manner.以异步信号安全的方式打印堆栈跟踪。
  4. Provide a robust signal handler function which uses its own stack that prints the stacktrace in a async-signal-safe manner.提供一个强大的信号处理函数,它使用自己的堆栈,以异步信号安全的方式打印堆栈跟踪。 The user can install this function as a signal handler for SIGSEGV , SIGBUS , SIGFPE , etc..用户可以将此函数安装为SIGSEGVSIGBUSSIGFPE等的信号处理程序。
  5. The signal handler may as well print the values of all CPU registers at the point of the fault from ucontext_t signal function argument (may be excluding vector registers), a-la Linux kernel oops log messages.信号处理程序也可以从ucontext_t信号函数参数(可能不包括向量寄存器)打印故障点处所有 CPU 寄存器的值,a-la Linux 内核 oops 日志消息。

Another answer to an old thread.旧线程的另一个答案。

When I need to do this, I usually just use system() and pstack当我需要这样做时,我通常只使用system()pstack

So something like this:所以是这样的:

#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <sstream>
#include <cstdlib>

void f()
{
    pid_t myPid = getpid();
    std::string pstackCommand = "pstack ";
    std::stringstream ss;
    ss << myPid;
    pstackCommand += ss.str();
    system(pstackCommand.c_str());
}

void g()
{
   f();
}


void h()
{
   g();
}

int main()
{
   h();
}

This outputs这输出

#0  0x00002aaaab62d61e in waitpid () from /lib64/libc.so.6
#1  0x00002aaaab5bf609 in do_system () from /lib64/libc.so.6
#2  0x0000000000400c3c in f() ()
#3  0x0000000000400cc5 in g() ()
#4  0x0000000000400cd1 in h() ()
#5  0x0000000000400cdd in main ()

This should work on Linux, FreeBSD and Solaris.这应该适用于 Linux、FreeBSD 和 Solaris。 I don't think that macOS has pstack or a simple equivalent, but this thread seems to have an alternative .我不认为 macOS 有 pstack 或简单的等价物,但这个线程似乎有一个替代品.

If you are using C , then you will need to use C string functions.如果您使用的是C ,那么您将需要使用C字符串函数。

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void f()
{
    pid_t myPid = getpid();
    /*
      length of command 7 for 'pstack ', 7 for the PID, 1 for nul
    */
    char pstackCommand[7+7+1];
    sprintf(pstackCommand, "pstack %d", (int)myPid);
    system(pstackCommand);
}

I've used 7 for the max number of digits in the PID, based on this post .根据这篇文章,我使用 7 作为 PID 中的最大位数。

In C++23, there will be <stacktrace> , and then you can do:在 C++23 中,会有<stacktrace> ,然后你可以这样做:

#include <stacktrace>

/* ... */

std::cout << std::stacktrace::current();

Further details:更多详细信息:
https://en.cppreference.com/w/cpp/header/stacktrace https://en.cppreference.com/w/cpp/header/stacktrace
https://en.cppreference.com/w/cpp/utility/basic_stacktrace/operator_ltlt https://en.cppreference.com/w/cpp/utility/basic_stacktrace/operator_ltlt

There is no standardized way to do that.没有标准化的方法可以做到这一点。 For windows the functionality is provided in the DbgHelp library对于 Windows, DbgHelp库中提供了该功能

You can use the Boost libraries to print the current callstack.您可以使用 Boost 库来打印当前调用堆栈。

#include <boost/stacktrace.hpp>

// ... somewhere inside the `bar(int)` function that is called recursively:
std::cout << boost::stacktrace::stacktrace();

Man here: https://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace.html这里的人: https ://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace.html

You can implement the functionality yourself:您可以自己实现该功能:

Use a global (string)stack and at start of each function push the function name and such other values (eg parameters) onto this stack;使用全局(字符串)堆栈,并在每个函数开始时将函数名称和其他值(例如参数)压入此堆栈; at exit of function pop it again.在函数退出时再次弹出它。

Write a function that will printout the stack content when it is called, and use this in the function where you want to see the callstack.编写一个函数,在调用时打印出堆栈内容,并在要查看调用堆栈的函数中使用它。

This may sound like a lot of work but is quite useful.这听起来可能需要做很多工作,但非常有用。

I know this thread is old, but I think it can be useful for other people.我知道这个线程很旧,但我认为它对其他人有用。 If you are using gcc, you can use its instrument features (-finstrument-functions option) to log any function call (entry and exit).如果你使用 gcc,你可以使用它的工具特性(-finstrument-functions 选项)来记录任何函数调用(进入和退出)。 Have a look at this for more information: http://hacktalks.blogspot.fr/2013/08/gcc-instrument-functions.html看看这个以获取更多信息: http ://hacktalks.blogspot.fr/2013/08/gcc-instrument-functions.html

You can thus for instance push and pop every calls into a stack, and when you want to print it, you just look at what you have in your stack.例如,您可以将每个调用推送和弹出到堆栈中,当您想要打印它时,您只需查看堆栈中的内容。

I've tested it, it works perfectly and is very handy我已经测试过了,它工作得很好,非常方便

UPDATE: you can also find information about the -finstrument-functions compile option in the GCC doc concerning the Instrumentation options:https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html更新:您还可以在 GCC 文档中找到有关 Instrumentation 选项的 -finstrument-functions 编译选项的信息:​​https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html

You can use Poppy for this.您可以为此使用罂粟 It is normally used to gather the stack trace during a crash but it can also output it for a running program as well.它通常用于在崩溃期间收集堆栈跟踪,但它也可以将其输出给正在运行的程序。

Now here's the good part: it can output the actual parameter values for each function on the stack, and even local variables, loop counters, etc.现在这里是好的部分:它可以输出堆栈上每个函数的实际参数值,甚至可以输出局部变量、循环计数器等。

Of course the next question is: will this be enough ?当然,下一个问题是:这就够了吗?

The main disadvantage of stack-traces is that why you have the precise function being called you do not have anything else, like the value of its arguments, which is very useful for debugging.堆栈跟踪的主要缺点是为什么要调用精确的函数,而您没有其他任何东西,例如其参数的值,这对于调试非常有用。

If you have access to gcc and gdb, I would suggest using assert to check for a specific condition, and produce a memory dump if it is not met.如果您可以访问 gcc 和 gdb,我建议您使用assert来检查特定条件,如果不满足则生成内存转储。 Of course this means the process will stop, but you'll have a full fledged report instead of a mere stack-trace.当然,这意味着该过程将停止,但您将获得完整的报告,而不仅仅是堆栈跟踪。

If you wish for a less obtrusive way, you can always use logging.如果您希望采用一种不那么突兀的方式,您可以随时使用日志记录。 There are very efficient logging facilities out there, like Pantheios for example.那里有非常高效的日志记录设施,例如Pantheios Which once again could give you a much more accurate image of what is going on.这再次可以让您更准确地了解正在发生的事情。

You can use the GNU profiler.您可以使用 GNU 分析器。 It shows the call-graph as well!它也显示了调用图! the command is gprof and you need to compile your code with some option.该命令是gprof ,您需要使用某些选项编译您的代码。

Is there any way to dump the call stack in a running process in C or C++ every time a certain function is called?每次调用某个函数时,有没有办法将调用堆栈转储到 C 或 C++ 中正在运行的进程中?

No there is not, although platform-dependent solutions might exist.不,没有,尽管可能存在依赖于平台的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM