我的gcc C ++程序崩溃时如何自动生成stacktrace
回答
ulimit -c unlimited
是一个系统变量,它允许我们在应用程序崩溃后创建核心转储。在这种情况下,数量不受限制。在同一目录中查找名为core的文件。确保在启用调试信息的情况下编译代码!
问候
回答
某些版本的libc包含处理堆栈跟踪的函数。我们也许可以使用它们:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
我记得很久以前使用libunwind来获取堆栈跟踪,但是平台可能不支持它。
回答
我们没有指定操作系统,因此很难回答。如果我们使用的是基于gnu libc的系统,则可以使用libc函数backtrace()
。
GCC还具有两个可以为我们提供帮助的内建函数,但是可能无法在体系结构上完全实现,它们分别是__builtin_frame_address和__builtin_return_address。两者都需要立即整数级别(即时,我的意思是它不能是变量)。如果给定级别的__builtin_frame_address不为零,则可以安全地获取相同级别的返回地址。
回答
看着:
男子3回溯
和:
#include <exeinfo.h> int backtrace(void **buffer, int size);
这些是GNU扩展。
回答
重要的是要注意,一旦生成核心文件,就需要使用gdb工具进行查看。为了使gdb能够理解核心文件,我们必须告诉gcc使用调试符号来检测二进制文件:为此,请使用-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 ...
我希望这有帮助。
当我的C ++程序崩溃时,我希望它自动生成一个堆栈跟踪。
我的程序由许多不同的用户运行,并且还可以在Linux,Windows和Macintosh上运行(所有版本均使用gcc
进行编译)。
我希望我的程序能够在崩溃时生成堆栈跟踪,并在用户下次运行时生成一个堆栈跟踪,它将询问他们是否可以将堆栈跟踪发送给我,这样我就可以找到问题所在。我可以处理向我发送信息,但是我不知道如何生成跟踪字符串。有任何想法吗?
解决方案
回答
我将使用在Visual Leak Detector中为泄漏的内存生成堆栈跟踪的代码。不过,这仅适用于Win32.
回答
我可以为Linux版本提供帮助:可以使用函数backtrace,backtrace_symbols和backtrace_symbols_fd。请参见相应的手册页。
回答
如果程序崩溃,则由操作系统本身生成崩溃转储信息。如果我们使用的是* nix操作系统,则只需避免阻止它(请查看ulimit命令的'coredump'选项)。
回答
- nix:
我们可以拦截SIGSEGV(通常在崩溃前会发出此信号)并将信息保存到文件中。 (例如,可以使用gdb调试的核心文件除外)。
赢:
从msdn进行检查。
我们还可以查看Google的Chrome浏览器代码,了解其如何处理崩溃。它有一个很好的异常处理机制。
回答
在Linux / unix / MacOSX上,请使用核心文件(我们可以使用ulimit或者兼容的系统调用来启用它们)。在Windows上,使用Microsoft错误报告(我们可以成为合作伙伴并获得对应用程序崩溃数据的访问权限)。
回答
ulimit -c <值>设置Unix上的核心文件大小限制。默认情况下,核心文件大小限制为0。我们可以使用ulimit -a来查看ulimit值。
同样,如果我们从gdb中运行程序,则它将在"分段违规"(通常是" SIGSEGV",通常在我们访问未分配的内存时)停止程序,也可以设置断点。
ddd和nemiver是gdb的前端,对于新手来说,使用它变得更加容易。
回答
胜利:关于StackWalk64如何http://msdn.microsoft.com/en-us/library/ms680650.aspx
回答
我忘记了GNOME的" appport"技术,但是我对使用它并不了解。它用于生成堆栈跟踪和其他诊断以进行处理,并且可以自动归档错误。当然值得检查。
回答
对于Linux并且我相信Mac OS X,如果我们使用的是gcc或者使用glibc的任何编译器,则可以使用execinfo.h中的backtrace()函数打印堆栈跟踪并在遇到分段错误时正常退出。文档可以在libc手册中找到。
这是一个示例程序,该程序安装一个" SIGSEGV"处理程序,并在出现段错误时将堆栈跟踪打印到" stderr"。这里的baz()
函数引起触发处理程序的段错误:
#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可以使用它来创建一个很好的stacktrace:
$ 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]
这显示了堆栈中每一帧的加载模块,偏移量和功能。在这里,我们可以看到堆栈顶部的信号处理程序,以及libc在main之前,除了main,foo,bar和baz之外。
回答
请参阅ACE(自适应通信环境)中的堆栈跟踪工具。它已经被编写为涵盖所有主要平台(以及更多)。该库是BSD样式许可的,因此,如果我们不想使用ACE,甚至可以复制/粘贴代码。
回答
可能值得研究Google Breakpad,跨平台崩溃转储生成器和用于处理转储的工具。
回答
我一直在研究这个问题已有一段时间了。
并深深地嵌入到Google Performance Tools自述文件中
http://code.google.com/p/google-perftools/source/browse/trunk/README
谈论libunwind
http://www.nongnu.org/libunwind/
很想听听这个图书馆的意见。
-rdynamic的问题在于,在某些情况下,它会相对较大地增加二进制文件的大小
回答
的Linux
虽然已经建议在execinfo.h中使用backtrace()函数来打印堆栈跟踪并在遇到分段错误时正常退出,但我看不到要确保生成的backtrace指向实际位置的必要复杂性故障(至少对于某些架构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()函数的所有危险仍然存在,不应忽视,但是我发现我在此描述的功能对于调试崩溃很有帮助。
重要的是要注意,我提供的示例是在Linux for x86上开发/测试的。我也已经使用uc_mcontext.arm_pc
而不是uc_mcontext.eip
在ARM上成功实现了这一点。
这是本文的链接,我在其中学习了此实现的详细信息:
http://www.linuxjournal.com/article/6391
回答
即使已经提供了描述如何使用GNU libcbacktrace()
函数的正确答案,而我提供了自己的答案,其中描述了如何确保从信号处理程序返回的轨迹指向故障2的实际位置,但我没有没提到从回溯中解散C ++符号输出。
当从C ++程序获取回溯信息时,可以通过c ++ filt
1运行输出以对符号进行去散,或者直接使用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]
分解输出(./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]
以下内容基于我最初的回答中的信号处理程序,并且可以替换上面示例中的信号处理程序,以演示如何使用" abi :: __ cxa_demangle"对符号进行解缠。该信号处理器产生与上面的示例相同的去杂乱的输出。
代码:
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++ = '##代码##'; *offset_begin++ = '##代码##'; *offset_end++ = '##代码##'; 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); }