我使用GCC编译器在Linux上工作。当我的c++程序崩溃时,我希望它自动生成一个堆栈跟踪。
我的程序由许多不同的用户运行,它也可以在Linux、Windows和Macintosh上运行(所有版本都使用gcc编译)。
我希望我的程序能够在崩溃时生成堆栈跟踪,下次用户运行它时,它会问他们是否可以将堆栈跟踪发送给我,这样我就可以跟踪到问题。我可以处理发送信息给我,但我不知道如何生成跟踪字符串。什么好主意吗?
我使用GCC编译器在Linux上工作。当我的c++程序崩溃时,我希望它自动生成一个堆栈跟踪。
我的程序由许多不同的用户运行,它也可以在Linux、Windows和Macintosh上运行(所有版本都使用gcc编译)。
我希望我的程序能够在崩溃时生成堆栈跟踪,下次用户运行它时,它会问他们是否可以将堆栈跟踪发送给我,这样我就可以跟踪到问题。我可以处理发送信息给我,但我不知道如何生成跟踪字符串。什么好主意吗?
当前回答
* nix: 你可以拦截SIGSEGV(通常这个信号是在崩溃之前发出的),并将信息保存到一个文件中。(除了可以使用GDB进行调试的核心文件之外)。
赢得: 从msdn检查这个。
你也可以看看谷歌的chrome代码,看看它是如何处理崩溃的。它有一个很好的异常处理机制。
其他回答
gdb -ex 'set confirm off' -ex r -ex bt -ex q <my-program>
对于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]
这显示了堆栈中每个帧的加载模块、偏移量和函数。在这里,您可以看到信号处理程序位于堆栈顶部,除了main、foo、bar和baz之外,还在main之前有libc函数。
它看起来就像在最后一个c++增强版本中出现的库提供了你想要的东西,可能代码会是多平台的。 它是boost::stacktrace,你可以像在boost sample中那样使用:
#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中编译上面的代码:
g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace
从boost文档复制的反向跟踪示例:
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
可能值得一看谷歌Breakpad,这是一个跨平台崩溃转储生成器和处理转储的工具。
ulimit -c unlimited
是一个系统变量,它将允许在应用程序崩溃后创建一个核心转储。在这种情况下是无限的。在同一目录中查找一个名为core的文件。确保在编译代码时启用了调试信息!
问候