提升堆栈跟踪
这是我到目前为止看到的最方便的选项,因为它:实际上可以打印出行号。
addr2line然而,它只是调用,这很难看,如果你的痕迹太多,可能会很慢。
demangles默认情况下
Boost只是标题,因此最不需要修改构建系统
main.cpp中#include #define BOOST_STACKTRACE_USE_ADDR2LINE#include void my_func_2(void) {
std::cout <
my_func_2();}void my_func_1(int i) {
my_func_2();}int main() {
my_func_1(1); /* line 19 */
my_func_1(2.0); /* line 20 */}
不幸的是,它似乎是一个更新的添加,并且libboost-stacktrace-devUbuntu 16.04中没有包,只有18.04:sudo apt-get install libboost-stacktrace-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o main.out -std=c++11 \ -Wall -Wextra -pedantic-errors main.cpp -ldl
我们必须-ldl在最后添加,否则编译失败。
然后:./main.out
得到:0# my_func_2() at /root/lkmc/main.cpp:7
1# my_func_1(int) at /root/lkmc/main.cpp:16
2# main at /root/lkmc/main.cpp:20
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./main.out
0# my_func_2() at /root/lkmc/main.cpp:7
1# my_func_1(double) at /root/lkmc/main.cpp:12
2# main at /root/lkmc/main.cpp:21
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./main.out
并与-O3:0# my_func_2() at /usr/include/boost/stacktrace/stacktrace.hpp:217
1# my_func_1(double) at /root/lkmc/main.cpp:11
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./main.out
0# my_func_2() at /usr/include/boost/stacktrace/stacktrace.hpp:217
1# main at /root/lkmc/main.cpp:21
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./main.out
请记住,回溯通常会被优化无可挽回地毁掉。尾调用优化是一个值得注意的例子:什么是尾调用优化?
输出并在下面的“glibc backtrace”部分进一步解释,这是类似的。
在Ubuntu 18.04,GCC 7.3.0上测试,提升1.65.1。
glibc回溯
main.c中#include #include /* Paste this on the file you want to debug. */#include #include 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
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;}
编译:gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -rdynamic -std=c99 \ -Wall -Wextra -pedantic-errors main.c
-rdynamic 是关键的必要选项。
跑:./main.out
输出:./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]
因此我们立即看到内联优化发生了,并且一些函数从跟踪中丢失了。
如果我们试图获取地址:addr2line -e main.out 0x4008f9 0x4008fe
我们获得:/home/ciro/main.c:21/home/ciro/main.c:36
完全没了。
如果我们-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]
然后:addr2line -e main.out 0x400a74 0x400a79
得到:/home/cirsan01/test/main.c:34/home/cirsan01/test/main.c:35
所以线条只有一个,TODO为什么?但这可能仍然有用。
结论:回溯只能完美地表现出来-O0。通过优化,原始回溯在编译代码中得到根本修改。
在Ubuntu 16.04,GCC 6.4.0,libc 2.23上测试。
glibc的 backtrace_symbols_fd
这个帮助器比它更方便backtrace_symbols,并产生基本相同的输出:/* Paste this on the file you want to debug. */#include #include #include 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("");}
在Ubuntu 16.04,GCC 6.4.0,libc 2.23上测试。
libunwind
TODO这比glibc回溯有什么优势吗?非常相似的输出,也需要修改构建命令,但不太广泛可用。
main.c中/* This must be on top. */#define _XOPEN_SOURCE 700#include #include /* Paste this on the file you want to debug. */#define UNW_LOCAL_ONLY#include #include 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;}
编译并运行: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
要么#define _XOPEN_SOURCE 700必须在顶部,要么必须使用-std=gnu99:
跑:./main.out
输出:0x4007db: (main+0xb)0x7f4ff50aa830: (__libc_start_main+0xf0)0x400819: (_start+0x29)0x4007e2: (main+0x12)0x7f4ff50aa830: (__libc_start_main+0xf0)0x400819: (_start+0x29)
和:addr2line -e main.out 0x4007db 0x4007e2
得到:/home/ciro/main.c:34/home/ciro/main.c:49
用-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)
和:addr2line -e main.out 0x4009f3 0x4009f8
得到:/home/ciro/main.c:47/home/ciro/main.c:48
在Ubuntu 16.04,GCC 6.4.0,libunwind 1.1上测试。
C ++ demangling