如何在我的gcc C ++应用程序崩溃时生成堆栈跟踪

当我的c + +应用程序崩溃,我想生成一个堆栈跟踪。

我已经问过这个,但我想我需要澄清我的需求。

我的应用程序由许多不同的用户运行,它也运行在Linux,Windows和Macintosh上(所有版本都使用gcc编译)。

我希望我的程序能够在崩溃时生成堆栈跟踪,下次用户运行它时,会询问是否可以将堆栈跟踪发送给我,以便我可以跟踪这个问题。 我可以处理发送信息给我,但我不知道如何生成跟踪string。 有任何想法吗?

对于Linux,我相信Mac OS X,如果你使用gcc,或者任何使用glibc的编译器,你可以使用execinfo.h的backtrace()函数打印execinfo.h ,并在出现分段错误时正常退出。 文档可以在libc手册中find。

下面是一个示例程序,它安装了一个SIGSEGV处理程序,并在出现段stderr时将一个堆栈跟踪打印到stderr 。 这里的baz()函数会导致触发处理程序的segfault:

 #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:\n", sig); backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } void baz() { int *foo = (int*)-1; // make a bad pointer printf("%d\n", *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. } 

使用-g -rdynamic进行编译可以在输出中获得符号信息,glibc可以使用它来创build一个不错的堆栈跟踪:

 $ gcc -g -rdynamic ./test.c -o test 

执行这个让你输出:

 $ ./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] 

这显示堆栈中每个帧来自的加载模块,偏移量和函数。 在这里,您可以看到堆栈顶部的信号处理程序,以及除mainfoobarbaz之外的main函数之前的libc函数。

Linux的

虽然已经build议使用execinfo.h中的backtrace()函数来打印堆栈跟踪并在出现分段错误时正常退出,但是我没有看到提及确保所产生的回溯指向实际位置的错综复杂(至less对于某些架构 – x86和ARM)。

进入信号处理程序时,堆栈帧链中的前两个条目在信号处理程序中包含一个返回地址,而在libc中包含一个sigaction()。 信号之前调用的最后一个函数(即故障位置)的堆栈帧丢失。

 #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\n", 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\n", 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)\n", SIGSEGV, strsignal(SIGSEGV)); exit(EXIT_FAILURE); } foo1(); exit(EXIT_SUCCESS); } 

产量

 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] 

在信号处理程序中调用backtrace()函数的所有危险仍然存在,不应该被忽略,但是我发现我在这里描述的function在debugging崩溃时非常有用。

请注意,我提供的示例是在Linux for Linux上开发/testing的。 我也使用uc_mcontext.arm_pc而不是uc_mcontext.eip成功地在ARM上实现了这个uc_mcontext.eip

这里有一篇文章的链接,我学习了这个实现的细节: http : //www.linuxjournal.com/article/6391

它比“人回溯”更容易,还有一个小的文档库(GNU特定的),以glibc作为libSegFault.so分发,我认为这是由Ulrich Drepper编写的,用于支持程序catchsegv(请参阅“man catchsegv”)。

这给了我们3种可能性。 而不是运行“程序 – 海”:

  1. 在catchsegv中运行:

     $ catchsegv program -o hai 
  2. 在运行时与libSegFault链接:

     $ LD_PRELOAD=/lib/libSegFault.so program -o hai 
  3. 在编译时与libSegFault链接:

     $ gcc -g1 -lSegFault -o program program.cc $ program -o hai 

在所有这三种情况下,你都会得到更清晰的回溯,而且优化程度较低(gcc -O0或-O1)和debugging符号(gcc -g)。 否则,你可能会得到一堆内存地址。

你也可以用类似的方法来捕获更多的堆栈跟踪信号:

 $ export SEGFAULT_SIGNALS="all" # "all" signals $ export SEGFAULT_SIGNALS="bus abrt" # SIGBUS and SIGABRT 

输出将如下所示(注意底部的回溯):

 *** 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

尽pipe提供了正确的答案 ,描述了如何使用GNU libc backtrace()函数1 ,我提供了自己的答案 ,描述了如何确保从信号处理程序的回溯指向故障2的实际位置,我不没有看到任何提及的从backtrace输出的demangling C ++符号。

当从C ++程序获得回溯时,可以通过c++filt 1运行输出来对符号进行abi::__cxa_demangle或直接使用abi::__cxa_demangle 1

  • 1 Linux和OS X 请注意, c++filt__cxa_demangle是GCC特定的
  • 2 Linux

以下C ++ Linux示例使用与我的其他答案相同的信号处理程序,并演示如何使用c++filt来对符号进行解构。

代码

 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; } 

输出./test ):

 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] 

Demangled输出./test 2>&1 | c++filt ):

 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] 

以下内容基于原始答案中的信号处理程序,可以replace上例中的信号处理程序,以演示如何使用abi::__cxa_demangle对符号进行abi::__cxa_demangle 。 这个信号处理程序产生与上面的例子相同的demangled输出。

代码

 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); } 

可能值得看看谷歌Breakpad ,一个跨平台的崩溃转储生成器和工具来处理转储。

你没有指定你的操作系统,所以这很难回答。 如果您使用的是基于gnu libc的系统,那么您可以使用libc函数backtrace()

海湾合作委员会也有两个内置的,可以帮助你,但可能或可能不会完全在你的架构上实现,这些是__builtin_frame_address__builtin_return_address 。 两者都需要立即整数级别(通过立即,我的意思是它不能是一个variables)。 如果给定级别的__builtin_frame_address非零,则应该可以安全地获取同一级别的返回地址。

ulimit -c <value>设置unix的核心文件大小限制。 默认情况下,核心文件大小限制为0.您可以使用ulimit -a查看您的ulimit值。

另外,如果你从gdb中运行你的程序,它会停止你的程序的“分段违规”( SIGSEGV ,通常是当你访问一块你没有分配的内存),或者你可以设置断点。

ddd和nemiver是gdb的前端,这使得新手更容易使用它。

 ulimit -c unlimited 

是一个系统variables,这将允许在应用程序崩溃后创build核心转储。 在这种情况下,无限量。 在同一个目录下查找一个名为core的文件。 确保你编译的代码中包含debugging信息!

问候

请注意,一旦生成核心文件,您需要使用gdb工具来查看它。 为了让gdb了解你的核心文件,你必须告诉gcc使用debugging符号来testing二进制文件:为此,使用-g标志编译:

 $ g++ -g prog.cpp -o prog 

然后,你可以设置“ulimit -c unlimited”让它转储一个核心,或者在gdb中运行你的程序。 我更喜欢第二种方法:

 $ gdb ./prog ... gdb startup output ... (gdb) run ... program runs and crashes ... (gdb) where ... gdb outputs your stack trace ... 

我希望这有帮助。

我一直在看这个问题。

深入Google性能工具自述文件

http://code.google.com/p/google-perftools/source/browse/trunk/README

谈到libunwind

http://www.nongnu.org/libunwind/

很想听听这个图书馆的意见。

-rdynamic的问题是在某些情况下可以相对显着地增加二进制文件的大小

libc的某些版本包含处理堆栈跟踪的函数; 你可能可以使用它们:

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

我记得很久以前使用libunwind获取堆栈跟踪,但可能不支持您的平台。

感谢thermistgeek将我的注意力吸引到了addr2line工具。

我已经写了一个快速和肮脏的脚本来处理这里提供的答案的输出:(非常感谢jschmier!)使用addr2line实用程序。

该脚本接受一个参数:包含jschmier实用程序输出的文件的名称。

对于每个级别的跟踪,输出应该打印如下内容:

 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; 

码:

 #!/bin/bash LOGFILE=$1 NUM_SRC_CONTEXT_LINES=3 old_IFS=$IFS # save the field separator IFS=$'\n' # new field separator, the end of line for bt in `cat $LOGFILE | grep '\[bt\]'`; do IFS=$old_IFS # restore default field separator printf '\n' 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/\<at\>.*//' | 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=$'\n' # new field separator, the end of line done IFS=$old_IFS # restore default field separator 

忘记改变你的源代码,并用backtrace()函数或macros做一些黑客 – 这些只是很差的解决scheme。

作为一个正确的工作解决scheme,我会build议:

  1. 用“-g”标志编译你的程序,将debugging符号embedded到二进制文件中(不要担心这不会影响你的性能)。
  2. 在linux上运行下一个命令:“ulimit -c unlimited” – 允许系统进行大的崩溃转储。
  3. 当你的程序崩溃时,在工作目录中你将看到文件“core”。
  4. 运行下一个命令将回溯打印到stdout:gdb -batch -ex“backtrace”./your_program_exe ./core

这将以可读的方式打印程序的正确可读回溯(使用源文件名和行号)。 此外,这种方法可以让您自由地自动化您的系统:有一个简短的脚本来检查进程是否创build了核心转储,然后通过电子邮件向开发者发送回溯,或将其logging到某个日志logging系统中。

你可以使用DeathHandler – 为你做的一切的小C ++类,可靠的。

看着:

男人3回溯

和:

 #include <exeinfo.h> int backtrace(void **buffer, int size); 

这些是GNU扩展。

请参阅ACE (适应性通信环境)中的堆栈跟踪function。 它已经被写入涵盖所有主要平台(以及更多)。 库是BSD风格的许可,所以如果你不想使用ACE,你甚至可以复制/粘贴代码。

我可以帮助Linux版本:可以使用函数backtrace,backtrace_symbols和backtrace_symbols_fd。 请参阅相应的手册页。

* nix:你可以截取SIGSEGV (通常这个信号是在崩溃之前产生的)并且把信息保存到一个文件中。 (除了你可以使用gdbdebugging的核心文件)。

win:从msdn检查。

你也可以看看谷歌的铬代码,看看它如何处理崩溃。 它有一个很好的exception处理机制。

  • 使用-g标志编译您的代码,以在二进制文件中包含debugging符号。
  • 设置您的系统,以便在应用程序崩溃时生成核心文件(例如ulimit -c unlimited )。
  • 当应用程序崩溃时,可以在debugging器(例如gdb,通过运行gdb ./core )中使用核心文件来获取回溯(gdb命令: bt )。

请注意,C ++符号名称有时非常乱码,回溯可能有点难以理解。

更有用的回溯可能需要邪恶的诡计(我听说过的一个解决scheme要求您在所有写入的方法的开头添加一个特殊的macros)。

我将使用生成堆栈跟踪的代码来检测Visual Leak Detector中的泄漏内存。 但是,这只适用于Win32。

我在这里看到很多答案,执行一个信号处理程序,然后退出。 这是要走的路,但请记住一个非常重要的事实:如果您想获得生成错误的核心转储,则不能调用exit(status) 。 调用abort()来代替!

我发现@tgamblin解决scheme不完整。 它不能处理与stackoverflow。 我认为,因为默认情况下,信号处理程序被调用相同的堆栈和SIGSEGV被抛出两次。 为了保护您的信号处理程序,需要注册一个独立的堆栈。

你可以用下面的代码来检查这个。 默认情况下处理程序失败。 用定义的macrosSTACK_OVERFLOW就没事了。

 #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; } 

除了上面的答案,在这里你如何使Debian Linux操作系统生成核心转储

  1. 在用户的主文件夹中创build一个“coredumps”文件夹
  2. 转至/etc/security/limits.conf。 在''行下面input“soft core unlimited”和“root soft core unlimited”,如果启用root的核心转储,允许核心转储的空间不受限制。
  3. 注意:“* soft core unlimited”不包含root,这就是为什么root必须在自己的行中指定的原因。
  4. 要检查这些值,请注销,重新login,然后键入“ulimit -a”。 “核心文件大小”应该设置为无限制。
  5. 检查.bashrc文件(用户和root,如果适用)以确保ulimit没有在那里设置。 否则,上面的值将在启动时被覆盖。
  6. 打开/etc/sysctl.conf。 在底部input以下内容:“kernel.core_pattern = /home//coredumps/%e_%t.dump”。 (%e将是进程名称,%t将是系统时间)
  7. 退出并键入“sysctl -p”加载新的configuration检查/ proc / sys / kernel / core_pattern,并validation它是否与您刚input的内容匹配。
  8. 核心转储可以通过在命令行(“&”)上运行一个进程来testing,然后用“kill -11”来杀死它。 如果核心转储成功,则会在分段故障指示后看到“(核心转储)”。

新的国王在城里已经到了https://github.com/bombela/backward-cpp

1个标题的地方在你的代码和1个库来安装。

我个人使用这个函数来调用它

 #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); } 

如果程序崩溃,则操作系统本身会生成故障转储信息。 如果你使用的是* nix操作系统,你只需要不要阻止它(请查看ulimit命令的'coredump'选项)。

在Linux / Unix / MacOSX上使用核心文件(可以使用ulimit或兼容的系统调用启用它们)。 在Windows上使用Microsoft错误报告(您可以成为合作伙伴,并可以访问您的应用程序崩溃数据)。

我忘记了“apport”的GNOME技术,但我不太了解如何使用它。 It is used to generate stacktraces and other diagnostics for processing and can automatically file bugs. It's certainly worth checking in to.