前一节Linux backtrace()系列函数 ,已经知道可以通过backtrace,backtrace_symbols得到函数的调用栈信息。不过,在C++中,得到的是一堆难以识别的符号,如何解码得到准确的函数名信息?
如,前面得到的函数调用栈信息:
$ ./backtrace 2 backtrace() return 7 address ./backtrace(_Z7myfunc3v+0x1f) [0x400a8c] ./backtrace() [0x400b45] ./backtrace(_Z6myfunci+0x25) [0x400b6c] ./backtrace(_Z6myfunci+0x1e) [0x400b65] ./backtrace(main+0x59) [0x400bc7] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f7170ed1f45] ./backtrace() [0x4009a9]
像(_Z7myfunc3v+0x1f) [0x400a8c]
,晦涩难懂。
我们可以用glibc提供的abi::__cxa_demangle(),对得到的函数符号信息进行解码。
__cxa_demangle详细参见:如何在C++中获得完整的类型名称 | CSDN
下面一段代码来自chensuo muduo,对其做了简单修改,以便打印完整信息。
// from chensuo muduo project // https://github.com/chenshuo/muduo #include <string> #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <cxxabi.h> #include <execinfo.h> string stackTrace(bool demangle) { string stack; const int max_frames = 200; void* frame[max_frames]; int nptrs = ::backtrace(frame, max_frames); // GNU extensions built-in function char** strings = ::backtrace_symbols(frame, nptrs); if (strings) { size_t len = 256; char* demangled = demangle ? static_cast<char*>(::malloc(len)) : nullptr; for (int i = 1; i < nptrs; ++i) { // skipping the 0-th, which is this function if (demangle) { // https://panthema.net/2008/0901-stacktrace-demangled/ // bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909] // demangle "_ZN3Bar4testEv" between "(" and "+" to function name char* left_par = nullptr; char* plus = nullptr; for (char* p = strings[i]; *p; ++p) { if (*p == '(') left_par = p; else if (*p == '+') plus = p; } if (left_par && plus) { *plus = '\0'; int status = 0; char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status); *plus = '+'; if (status == 0) { // demangle success demangled = ret; stack.append(strings[i], left_par + 1); stack.append(demangled); stack.push_back(')'); // add ')' to form "(...)" stack.push_back('\n'); continue; } } } // Fallback to mangled names stack.append(strings[i]); stack.push_back('\n'); } free(demangled); free(strings); } return stack; }
还是用来运行前一节的示例,可以得到
$./test_demo 2 /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc3()) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x40133d] /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int)) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int)) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(main+0xd2) [0x4017bc] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f8fb1213f45] /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x401209]
注意到这里有2个myfunc,而没有myfunc2。检查代码发现myfunc2是static函数,推测可能是static修饰符的影响,导致无法正确解码myfunc2符号信息。
去掉static限制:
static void myfunc2() { myfunc3(); } // 修改为 void myfunc2() // 去掉了static { myfunc3(); }
再次运行之
$./test_demo 2 /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc3()) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc2()) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int)) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int)) /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(main+0xd2) [0x4017ec] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7faf0ff12f45] /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x401239]
可以发现,函数名的顺序,已经之前的推测的调用顺序完全一致。