How to automatically generate a stacktrace when my program crashes
我正在使用gcc编译器在Linux上工作。当我的C++程序崩溃时,我希望它能自动生成堆栈跟踪。
我的程序由许多不同的用户运行,它也在Linux、Windows和Macintosh上运行(所有版本都是使用
我希望我的程序能够在崩溃时生成堆栈跟踪,下次用户运行它时,它将询问他们是否可以将堆栈跟踪发送给我,以便我可以跟踪问题。我可以处理发送信息给我,但我不知道如何生成跟踪字符串。有什么想法吗?
对于Linux and I believe Mac Os X,if you're using GCC,or any compiler that use GLIBC,你可以在
这是一个实例,说明安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include <stdio.h> #include <execinfo.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void handler(int sig) { void *array[10]; size_t size; // get void*'s for all entries on the stack size = backtrace(array, 10); // print out all the frames to stderr fprintf(stderr,"Error: signal %d: ", sig); backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } void baz() { int *foo = (int*)-1; // make a bad pointer printf("%d ", *foo); // causes segfault } void bar() { baz(); } void foo() { bar(); } int main(int argc, char **argv) { signal(SIGSEGV, handler); // install our handler foo(); // this will call foo, bar, and baz. baz segfaults. } |
把你的符号信息输出到你的输出中,GLIBC可以用来做一个很好的堆栈:
ZZU1
Executing this gets you this output:
1 2 3 4 5 6 7 8 9 10 | $ ./test Error: signal 11: ./test(handler+0x19)[0x400911] /lib64/tls/libc.so.6[0x3a9b92e380] ./test(baz+0x14)[0x400962] ./test(bar+0xe)[0x400983] ./test(foo+0xe)[0x400993] ./test(main+0x28)[0x4009bd] /lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3a9b91c4bb] ./test[0x40086a] |
这显示了堆栈中每个帧的负载模块、偏移模块和函数。在这里,你可以看到在堆栈顶部的信号手柄和
亚麻
当使用后台函数(Execinfo.h)打印一个堆栈跟踪和优雅地输出时,当你得到一个分割缺陷时,已经被建议,我看不出有任何关于内在性的必要性,以确保后台的反馈点对故障的实际定位(至少对于某些建筑来说是X86&)。
当你进入信号手柄时,在堆栈帧链中的第一个入口包含在信号手柄内的一个回复地址和在LIBC内的一个标记()中。在信号(即故障位置)之前,最后一个函数的堆栈帧被呼叫失败。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifndef __USE_GNU #define __USE_GNU #endif #include <execinfo.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ucontext.h> #include <unistd.h> /* This structure mirrors the one found in /usr/include/asm/ucontext.h */ typedef struct _sig_ucontext { unsigned long uc_flags; struct ucontext *uc_link; stack_t uc_stack; struct sigcontext uc_mcontext; sigset_t uc_sigmask; } sig_ucontext_t; void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) { void * array[50]; void * caller_address; char ** messages; int size, i; sig_ucontext_t * uc; uc = (sig_ucontext_t *)ucontext; /* Get the address at the time the signal was raised */ #if defined(__i386__) // gcc specific caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific #elif defined(__x86_64__) // gcc specific caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific #else #error Unsupported architecture. // TODO: Add support for other arch. #endif fprintf(stderr,"signal %d (%s), address is %p from %p ", sig_num, strsignal(sig_num), info->si_addr, (void *)caller_address); size = backtrace(array, 50); /* overwrite sigaction with caller's address */ array[1] = caller_address; messages = backtrace_symbols(array, size); /* skip first stack frame (points here) */ for (i = 1; i < size && messages != NULL; ++i) { fprintf(stderr,"[bt]: (%d) %s ", i, messages[i]); } free(messages); exit(EXIT_FAILURE); } int crash() { char * p = NULL; *p = 0; return 0; } int foo4() { crash(); return 0; } int foo3() { foo4(); return 0; } int foo2() { foo3(); return 0; } int foo1() { foo2(); return 0; } int main(int argc, char ** argv) { struct sigaction sigact; sigact.sa_sigaction = crit_err_hdlr; sigact.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0) { fprintf(stderr,"error setting signal handler for %d (%s) ", SIGSEGV, strsignal(SIGSEGV)); exit(EXIT_FAILURE); } foo1(); exit(EXIT_SUCCESS); } |
输出
1 2 3 4 5 6 7 8 | signal 11 (Segmentation fault), address is (nil) from 0x8c50 [bt]: (1) ./test(crash+0x24) [0x8c50] [bt]: (2) ./test(foo4+0x10) [0x8c70] [bt]: (3) ./test(foo3+0x10) [0x8c8c] [bt]: (4) ./test(foo2+0x10) [0x8ca8] [bt]: (5) ./test(foo1+0x10) [0x8cc4] [bt]: (6) ./test(main+0x74) [0x8d44] [bt]: (7) /lib/libc.so.6(__libc_start_main+0xa8) [0x40032e44] |
在一个信号手柄的信号中呼叫后台()函数的所有危险,仍然存在,不应被过度探索,但我在这里描述的功能性在解体裂缝中的帮助。
重要的是要指出,我提供的实例是为X86开发/测试Linux。我还成功地用
这是一个链接到文章,我从文章中了解到这一执行的详细情况:http://www.linuxjournal.com/article/6391
它甚至比"man backtrace"更容易,有一个小文档库(GNU特定的库)以libsegfault的形式与glibc一起分发,我认为它是由ulrich drepper编写的,以支持catchsegv程序(参见"man catchsegv")。
这给了我们3种可能性。而不是运行"程序-o hai":
在CatchSegv中运行:
1 | $ catchsegv program -o hai |
运行时与libsegfault链接:
1 | $ LD_PRELOAD=/lib/libSegFault.so program -o hai |
编译时与libsegfault链接:
1 2 | $ gcc -g1 -lSegFault -o program program.cc $ program -o hai |
在所有3种情况下,您将通过更少的优化(gcc-o0或-o1)和调试符号(gcc-g)获得更清晰的回溯。否则,您可能会得到一堆内存地址。
您还可以通过如下方式捕获更多用于堆栈跟踪的信号:
1 2 | $ export SEGFAULT_SIGNALS="all" #"all" signals $ export SEGFAULT_SIGNALS="bus abrt" # SIGBUS and SIGABRT |
输出将如下所示(注意底部的回溯):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | *** Segmentation fault Register dump: EAX: 0000000c EBX: 00000080 ECX: 00000000 EDX: 0000000c ESI: bfdbf080 EDI: 080497e0 EBP: bfdbee38 ESP: bfdbee20 EIP: 0805640f EFLAGS: 00010282 CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Trap: 0000000e Error: 00000004 OldMask: 00000000 ESP/signal: bfdbee20 CR2: 00000024 FPUCW: ffff037f FPUSW: ffff0000 TAG: ffffffff IPOFF: 00000000 CSSEL: 0000 DATAOFF: 00000000 DATASEL: 0000 ST(0) 0000 0000000000000000 ST(1) 0000 0000000000000000 ST(2) 0000 0000000000000000 ST(3) 0000 0000000000000000 ST(4) 0000 0000000000000000 ST(5) 0000 0000000000000000 ST(6) 0000 0000000000000000 ST(7) 0000 0000000000000000 Backtrace: /lib/libSegFault.so[0xb7f9e100] ??:0(??)[0xb7fa3400] /usr/include/c++/4.3/bits/stl_queue.h:226(_ZNSt5queueISsSt5dequeISsSaISsEEE4pushERKSs)[0x805647a] /home/dbingham/src/middle-earth-mud/alpha6/src/engine/player.cpp:73(_ZN6Player5inputESs)[0x805377c] /home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:159(_ZN6Socket4ReadEv)[0x8050698] /home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:413(_ZN12ServerSocket4ReadEv)[0x80507ad] /home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:300(_ZN12ServerSocket4pollEv)[0x8050b44] /home/dbingham/src/middle-earth-mud/alpha6/src/engine/main.cpp:34(main)[0x8049a72] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7d1b775] /build/buildd/glibc-2.9/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8049801] |
如果你想知道血淋淋的细节,最好的来源是不幸的来源:见http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c及其父目录http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
尽管已经提供了一个正确的答案,它描述了如何使用GNU LBC-EDCOX1,0个函数1和I提供了我自己的答案,它描述了如何确保从信号处理程序点到Burrt2的实际位置的回溯,但我没有看到任何从回溯中输出的DeangLink C++符号的提及。
当从C++程序获得回溯时,输出可以通过EDCOX1×1×1来运行,从而使符号解散或直接使用EDCOX1×2×1。
- 1个Linux和OS X注意,
c++filt 和__cxa_demangle 是gcc特有的 - 2 Linux
下面的C++ Linux示例使用与我其他答案相同的信号处理程序,并演示了如何使用EDCOX1(1)来对符号进行解散。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class foo { public: foo() { foo1(); } private: void foo1() { foo2(); } void foo2() { foo3(); } void foo3() { foo4(); } void foo4() { crash(); } void crash() { char * p = NULL; *p = 0; } }; int main(int argc, char ** argv) { // Setup signal handler for SIGSEGV ... foo * f = new foo(); return 0; } |
输出(
1 2 3 4 5 6 7 8 9 10 | signal 11 (Segmentation fault), address is (nil) from 0x8048e07 [bt]: (1) ./test(crash__3foo+0x13) [0x8048e07] [bt]: (2) ./test(foo4__3foo+0x12) [0x8048dee] [bt]: (3) ./test(foo3__3foo+0x12) [0x8048dd6] [bt]: (4) ./test(foo2__3foo+0x12) [0x8048dbe] [bt]: (5) ./test(foo1__3foo+0x12) [0x8048da6] [bt]: (6) ./test(__3foo+0x12) [0x8048d8e] [bt]: (7) ./test(main+0xe0) [0x8048d18] [bt]: (8) ./test(__libc_start_main+0x95) [0x42017589] [bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981] |
非扭曲输出(
1 2 3 4 5 6 7 8 9 10 | signal 11 (Segmentation fault), address is (nil) from 0x8048e07 [bt]: (1) ./test(foo::crash(void)+0x13) [0x8048e07] [bt]: (2) ./test(foo::foo4(void)+0x12) [0x8048dee] [bt]: (3) ./test(foo::foo3(void)+0x12) [0x8048dd6] [bt]: (4) ./test(foo::foo2(void)+0x12) [0x8048dbe] [bt]: (5) ./test(foo::foo1(void)+0x12) [0x8048da6] [bt]: (6) ./test(foo::foo(void)+0x12) [0x8048d8e] [bt]: (7) ./test(main+0xe0) [0x8048d18] [bt]: (8) ./test(__libc_start_main+0x95) [0x42017589] [bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981] |
以下内容基于我最初的答案中的信号处理程序,可以在上面的示例中替换信号处理程序,以演示如何使用
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) { sig_ucontext_t * uc = (sig_ucontext_t *)ucontext; void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific std::cerr <<"signal" << sig_num <<" (" << strsignal(sig_num) <<"), address is" << info->si_addr <<" from" << caller_address << std::endl << std::endl; void * array[50]; int size = backtrace(array, 50); array[1] = caller_address; char ** messages = backtrace_symbols(array, size); // skip first stack frame (points here) for (int i = 1; i < size && messages != NULL; ++i) { char *mangled_name = 0, *offset_begin = 0, *offset_end = 0; // find parantheses and +address offset surrounding mangled name for (char *p = messages[i]; *p; ++p) { if (*p == '(') { mangled_name = p; } else if (*p == '+') { offset_begin = p; } else if (*p == ')') { offset_end = p; break; } } // if the line could be processed, attempt to demangle the symbol if (mangled_name && offset_begin && offset_end && mangled_name < offset_begin) { *mangled_name++ = '\0'; *offset_begin++ = '\0'; *offset_end++ = '\0'; int status; char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status); // if demangling is successful, output the demangled function name if (status == 0) { std::cerr <<"[bt]: (" << i <<")" << messages[i] <<" :" << real_name <<"+" << offset_begin << offset_end << std::endl; } // otherwise, output the mangled function name else { std::cerr <<"[bt]: (" << i <<")" << messages[i] <<" :" << mangled_name <<"+" << offset_begin << offset_end << std::endl; } free(real_name); } // otherwise, print the whole line else { std::cerr <<"[bt]: (" << i <<")" << messages[i] << std::endl; } } std::cerr << std::endl; free(messages); exit(EXIT_FAILURE); } |
也许值得一看谷歌断裂,一个交叉平台断裂发生器和工具来处理倾销。
您没有指定操作系统,因此这很难回答。如果您使用的是基于gnu libc的系统,那么您可能可以使用libc函数
GCC还有两个内置组件可以帮助您,但它们可能在您的体系结构上完全实现,也可能不完全实现,它们分别是
另外,如果您从gdb中运行程序,它将在"分段违规"时停止您的程序(
DDD和Nemiver是GDB的前端,这使得新手更容易使用它。
libc的某些版本包含处理堆栈跟踪的函数;您可以使用它们:
http://www.gnu.org/software/libc/manual/html_node/backtraces.html
我记得很久以前使用libunwind获取堆栈跟踪,但在您的平台上可能不支持它。
重要的是要注意,一旦生成了一个核心文件,就需要使用gdb工具来查看它。为了让gdb理解您的核心文件,您必须告诉gcc使用调试符号对二进制文件进行检测:为此,使用-g标志进行编译:
1 | $ g++ -g prog.cpp -o prog |
然后,您可以设置"ulimit-c unlimited"让它转储一个内核,或者在gdb中运行您的程序。我更喜欢第二种方法:
1 2 3 4 5 6 | $ gdb ./prog ... gdb startup output ... (gdb) run ... program runs and crashes ... (gdb) where ... gdb outputs your stack trace ... |
我希望这有帮助。
我一直在看这个问题。
并在谷歌性能工具中燃烧
http://code.google.com/p/google-perftools/source/browse/trunk/readme
谈论自由风
http://www.nongnu.org/libunwind/。
我想听听这个图书馆的意见。
RDYNAMIC的问题是,它可以在某些情况下增加二进制相对重要性的尺寸。
感谢热情的极客们让我注意到addr2line实用程序。
我编写了一个快速而脏的脚本来处理这里提供的答案的输出:(多亏了jschmier!)使用addr2line实用程序。
脚本接受一个参数:包含jschmier实用程序输出的文件名。
输出应该为跟踪的每个级别打印如下内容:
1 2 3 4 5 6 7 8 9 10 | BACKTRACE: testExe 0x8A5db6b FILE: pathToFile/testExe.C:110 FUNCTION: testFunction(int) 107 108 109 int* i = 0x0; *110 *i = 5; 111 112 } 113 return i; |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #!/bin/bash LOGFILE=$1 NUM_SRC_CONTEXT_LINES=3 old_IFS=$IFS # save the field separator IFS=$' ' # new field separator, the end of line for bt in `cat $LOGFILE | grep '\[bt\]'`; do IFS=$old_IFS # restore default field separator printf ' ' EXEC=`echo $bt | cut -d' ' -f3 | cut -d'(' -f1` ADDR=`echo $bt | cut -d'[' -f3 | cut -d']' -f1` echo"BACKTRACE: $EXEC $ADDR" A2L=`addr2line -a $ADDR -e $EXEC -pfC` #echo"A2L: $A2L" FUNCTION=`echo $A2L | sed 's/\.*//' | cut -d' ' -f2-99` FILE_AND_LINE=`echo $A2L | sed 's/.* at //'` echo"FILE: $FILE_AND_LINE" echo"FUNCTION: $FUNCTION" # print offending source code SRCFILE=`echo $FILE_AND_LINE | cut -d':' -f1` LINENUM=`echo $FILE_AND_LINE | cut -d':' -f2` if ([ -f $SRCFILE ]); then cat -n $SRCFILE | grep -C $NUM_SRC_CONTEXT_LINES"^ *$LINENUM\>" | sed"s/ $LINENUM/*$LINENUM/" else echo"File not found: $SRCFILE" fi IFS=$' ' # new field separator, the end of line done IFS=$old_IFS # restore default field separator |
忘记更改源代码,用backtrace()函数或宏进行一些黑客攻击——这些只是糟糕的解决方案。
作为一个有效的解决方案,我建议:
这将以人类可读的方式(使用源文件名和行号)打印程序的正确可读回溯。此外,这种方法还可以让您自由地使系统自动化:有一个简短的脚本,检查进程是否创建了核心转储,然后通过电子邮件向开发人员发送回溯,或者将其记录到某个日志系统中。
1 | ulimit -c unlimited |
是一个系统变量,wich将允许在应用程序崩溃后创建核心转储。在这种情况下,金额是无限的。在同一个目录中查找名为core的文件。确保编译代码时启用了调试信息!
当做
win:stackwalk64怎么样http://msdn.microsoft.com/en-us/library/ms680650.aspx
你可以使用DeaHealther-TrimeC++类,它为你做任何事情,可靠。
看:
人3回溯
还有:
1 2 | #include <exeinfo.h> int backtrace(void **buffer, int size); |
这些是GNU扩展。
See the stack trace facility in ACE(apdative communication environment).这是为覆盖所有主要平台(和更多)而写的。图书馆是BSD-风格许可证,所以如果你不想使用ACE,你甚至可以拷贝/输入代码。
我可以帮助Linux版本:可以使用函数backtrace、backtrace_symbols和backtrace_symbols_fd。请参阅相应的手册页。
我发现@tganblin解决方案不完整。它不能处理stackoverflow。我认为,因为默认情况下,信号处理程序是用同一堆栈调用的,并且SIGSEGV被抛出两次。为了保护您需要为信号处理程序注册一个独立的堆栈。
你可以用下面的代码来检查这个。默认情况下,处理程序失败。有了定义的宏堆栈溢出,一切正常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <iostream> #include <execinfo.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <string> #include <cassert> using namespace std; //#define STACK_OVERFLOW #ifdef STACK_OVERFLOW static char stack_body[64*1024]; static stack_t sigseg_stack; #endif static struct sigaction sigseg_handler; void handler(int sig) { cerr <<"sig seg fault handler" << endl; const int asize = 10; void *array[asize]; size_t size; // get void*'s for all entries on the stack size = backtrace(array, asize); // print out all the frames to stderr cerr <<"stack trace:" << endl; backtrace_symbols_fd(array, size, STDERR_FILENO); cerr <<"resend SIGSEGV to get core dump" << endl; signal(sig, SIG_DFL); kill(getpid(), sig); } void foo() { foo(); } int main(int argc, char **argv) { #ifdef STACK_OVERFLOW sigseg_stack.ss_sp = stack_body; sigseg_stack.ss_flags = SS_ONSTACK; sigseg_stack.ss_size = sizeof(stack_body); assert(!sigaltstack(&sigseg_stack, nullptr)); sigseg_handler.sa_flags = SA_ONSTACK; #else sigseg_handler.sa_flags = SA_RESTART; #endif sigseg_handler.sa_handler = &handler; assert(!sigaction(SIGSEGV, &sigseg_handler, nullptr)); cout <<"sig action set" << endl; foo(); return 0; } |
*NIX:您可以截获sigsegv(通常在崩溃前发出此信号)并将信息保存到文件中。(除了可以使用gdb进行调试的核心文件之外)。
胜利:从msdn检查这个。
你也可以查看谷歌的Chrome代码,看看它是如何处理崩溃的。它有一个很好的异常处理机制。
我在这里看到了很多答案,执行信号处理程序然后退出。这是一种方法,但请记住一个非常重要的事实:如果您想为生成的错误获取核心转储,就不能调用
我将使用在可视泄漏检测器中为泄漏内存生成堆栈跟踪的代码。不过,这只适用于Win32。
城里的新国王来了https://github.com/bobela/backward-cpp
1个要放在代码中的头和1个要安装的库。
我个人用这个函数来称呼它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include"backward.hpp" void stacker() { using namespace backward; StackTrace st; st.load_here(99); //Limit the number of trace depth to 99 st.skip_n_firsts(3);//This will skip some backward internal function from the trace Printer p; p.snippet = true; p.object = true; p.color = true; p.address = true; p.print(st, stderr); } |
除了上面的答案外,下面介绍如何使DebianLinux操作系统生成核心转储
作为一个仅限Windows的解决方案,您可以使用Windows错误报告获得相当于堆栈跟踪(包含更多的信息)。只需几个注册表项,就可以将其设置为收集用户模式转储:
Starting with Windows Server 2008 and Windows Vista with Service Pack 1 (SP1), Windows Error Reporting (WER) can be configured so that full user-mode dumps are collected and stored locally after a user-mode application crashes. [...]
This feature is not enabled by default. Enabling the feature requires administrator privileges. To enable and configure the feature, use the following registry values under the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps key.
您可以从安装程序设置注册表项,安装程序具有所需的权限。
与在客户机上生成堆栈跟踪相比,创建用户模式转储具有以下优势:
- 它已经在系统中实现了。如果您需要对要转储的信息量进行更细粒度的控制,您可以使用上述WER,也可以自己调用minidumpwritedump。(确保从其他进程调用它。)
- 比堆栈跟踪更完整。其中,它可以包含局部变量、函数参数、其他线程的堆栈、加载的模块等。数据量(以及大小)是高度可定制的。
- 无需发送调试符号。这两者都大大减小了部署的大小,同时也使得对应用程序进行反向工程变得更加困难。
- 很大程度上独立于所使用的编译器。使用WER甚至不需要任何代码。无论哪种方法,拥有一种获取符号数据库(PDB)的方法对于离线分析都非常有用。我相信GCC可以生成PDB,或者有工具将符号数据库转换为PDB格式。
请注意,WER只能由应用程序崩溃触发(即系统由于未处理的异常终止进程)。可以随时调用
强制阅读,如果要评估小型转储的适用性:
- 有效小型垃圾场
- 有效小型垃圾场(第2部分)
在Linux/Unix/MacOSX上,使用核心文件(您可以使用ulimit或兼容的系统调用启用它们)。在Windows上使用Microsoft错误报告(您可以成为合作伙伴并访问应用程序崩溃数据)。
我忘记了"apport"的gnome技术,但我对使用它知之甚少。它用于生成StackTraces和其他用于处理的诊断,并可以自动归档Bug。这当然值得一看。
如果你仍然想像我一样独自行动,你可以链接到
https://github.com/gnif/lookingglass/blob/master/common/src/crash.linux.c
这将产生输出:
1 2 3 4 5 6 7 | [E] crash.linux.c:170 | crit_err_hdlr | ==== FATAL CRASH (a12-151-g28b12c85f4+1) ==== [E] crash.linux.c:171 | crit_err_hdlr | signal 11 (Segmentation fault), address is (nil) [E] crash.linux.c:194 | crit_err_hdlr | [trace]: (0) /home/geoff/Projects/LookingGlass/client/src/main.c:936 (register_key_binds) [E] crash.linux.c:194 | crit_err_hdlr | [trace]: (1) /home/geoff/Projects/LookingGlass/client/src/main.c:1069 (run) [E] crash.linux.c:194 | crit_err_hdlr | [trace]: (2) /home/geoff/Projects/LookingGlass/client/src/main.c:1314 (main) [E] crash.linux.c:199 | crit_err_hdlr | [trace]: (3) /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f8aa65f809b] [E] crash.linux.c:199 | crit_err_hdlr | [trace]: (4) ./looking-glass-client(_start+0x2a) [0x55c70fc4aeca] |
它看起来像在最后一个C++Boost版本出现的库中提供精确的你想要的,可能代码会是多平台的。它是boost::stacktrace,可以像在boost示例中那样使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <filesystem> #include <sstream> #include <fstream> #include <signal.h> // ::signal, ::raise #include <boost/stacktrace.hpp> const char* backtraceFileName ="./backtraceFile.dump"; void signalHandler(int) { ::signal(SIGSEGV, SIG_DFL); ::signal(SIGABRT, SIG_DFL); boost::stacktrace::safe_dump_to(backtraceFileName); ::raise(SIGABRT); } void sendReport() { if (std::filesystem::exists(backtraceFileName)) { std::ifstream file(backtraceFileName); auto st = boost::stacktrace::stacktrace::from_dump(file); std::ostringstream backtraceStream; backtraceStream << st << std::endl; // sending the code from st file.close(); std::filesystem::remove(backtraceFileName); } } int main() { ::signal(SIGSEGV, signalHandler); ::signal(SIGABRT, signalHandler); sendReport(); // ... rest of code } |
在Linux中,您编译上面的代码:
1 | g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace |
从Boost文档复制的回溯示例:
1 2 3 4 5 6 7 | 0# bar(int) at /path/to/source/file.cpp:70 1# bar(int) at /path/to/source/file.cpp:70 2# bar(int) at /path/to/source/file.cpp:70 3# bar(int) at /path/to/source/file.cpp:70 4# main at /path/to/main.cpp:93 5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 6# _start |