我们知道Linux程序如果使用-g编译,若程序发生崩溃(coredump),是可以使用gdb调试生成的dump文件,找到崩溃的位置的。
然后C++有一些组件是提供崩溃堆栈打印的,本文给出如下几种方法。
+ 本文的测试代码已上传至gitee:https://gitee.com/liudegui/linux-cpp-crash-trace
如果没有安装,对于ubuntu系统,可使用如下方法安装
sudo apt-get install libboost-stacktrace-dev
#include
#include
#include
#include
#include
#include
#include
#define BOOST_STACKTRACE_USE_ADDR2LINE
#include
char *str = (char *)"test";
void core_test()
{
str[1] = 'T';
}
// This is definitely not async-signal-safe. Let's hope it doesn't crash or hang.
void handler(int signo)
{
if (std::ofstream file_stream("my_stacktrace.log", std::ios::trunc); file_stream.is_open())
{
std::stringstream ss;
ss << boost::stacktrace::stacktrace();
std::cout << boost::stacktrace::stacktrace() << std::endl;
file_stream.write(ss.str().c_str(), ss.str().size());
file_stream.close();
}
// Raise signal again. Should usually terminate the program.
raise(signo);
}
int main()
{
// Repeat this for each signal you want to catch and log.
struct sigaction act{};
// Allow signal handler to re-raise signal and reset to default handler after entering.
act.sa_flags = SA_NODEFER | SA_RESETHAND;
act.sa_handler = &handler;
sigfillset(&act.sa_mask);
sigdelset(&act.sa_mask, SIGSEGV /* or other signal */);
if (sigaction(SIGSEGV /* or other signal */, &act, nullptr) == -1)
std::exit(EXIT_FAILURE);
core_test();
}
输出结果为:
0# handler(int) at /usr/local/include/boost/stacktrace/stacktrace.hpp:129
1# 0x00007FF3691A0090 in /usr/lib/x86_64-linux-gnu/libc.so.6
2# core_test() at /home/dgliu/boost_stacktrace_demo/src/boost_stacktrace_demo.cpp:14
3# main at /home/dgliu/boost_stacktrace_demo/src/boost_stacktrace_demo.cpp:46
4# __libc_start_main in /usr/lib/x86_64-linux-gnu/libc.so.6
5# _start in ./bin/boost_stacktrace_demo
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -fno-pie -ggdb3 -O0 -no-pie")
关于no-pie
解释如下:
-no-pie (default): 生成position-dependent executable (ET_EXEC)。要求最宽松,源文件可用-fno-pic,-fpie,-fpic编译
-pie: 生成position-independent executable (ET_DYN)。源文件须要用-fpie,-fpic编译
Backward-cpp是一个支持多个平台堆栈打印的组件,支持x86和ARM平台.
它依赖与Linux其它组件,可以选择安装libbfd
、libdw
或者libdwarf。
#define BACKWARD_HAS_BFD 1
宏;#define BACKWARD_HAS_DW 1
宏;#define BACKWARD_HAS_DWARF 1
宏;sudo apt-get install libdw-dev
下载头文件backward.hpp;
在CMakeLists target_link_libraries 中添加 dw
在主程序中添加 backward
#define BACKWARD_HAS_DW 1
#include "backward.hpp"
namespace backward{
backward::SignalHandling sh;
}
非常简单的测试代码为:
#include
#include
#define BACKWARD_HAS_DW 1
#include "backward.hpp"
namespace backward{
backward::SignalHandling sh;
}
int main(){
char *c = "hello world";
c[1] = 'H';
}
#include
#include
#include
#include
#include
#define BACKWARD_HAS_DW 1
#include "backward.hpp"
namespace backward
{
backward::SignalHandling sh;
}
char *str = (char *)"test";
void core_test()
{
str[1] = 'T';
}
// This is definitely not async-signal-safe. Let's hope it doesn't crash or hang.
void handler(int signo)
{
if (std::ofstream file_stream("my_stacktrace.log", std::ios::trunc); file_stream.is_open())
{
file_stream << "Caught signal " << signo << ".\nTrace:\n";
backward::StackTrace st;
st.load_here(32);
backward::Printer p;
p.print(st, file_stream); // print to file
p.print(st, std::cout); // print to console
}
// Raise signal again. Should usually terminate the program.
raise(signo);
}
int main()
{
// Repeat this for each signal you want to catch and log.
struct sigaction act {};
// Allow signal handler to re-raise signal and reset to default handler after entering.
act.sa_flags = SA_NODEFER | SA_RESETHAND;
act.sa_handler = &handler;
sigfillset(&act.sa_mask);
sigdelset(&act.sa_mask, SIGSEGV /* or other signal */);
if (sigaction(SIGSEGV /* or other signal */, &act, nullptr) == -1)
std::exit(EXIT_FAILURE);
core_test();
}
输出结果为:
Stack trace (most recent call last):
#8 Object "", at 0xffffffffffffffff, in
#7 Object "/home/dgliu/backward-cpp_demo/bin/backward-cpp_demo", at 0x5619693b2f4d, in _start
#6 Source "../csu/libc-start.c", line 308, in __libc_start_main
#5 Source "/home/dgliu/backward-cpp_demo/src/backward-cpp_demo.cpp", line 51, in main
48: if (sigaction(SIGSEGV /* or other signal */, &act, nullptr) == -1)
49: std::exit(EXIT_FAILURE);
50:
> 51: core_test();
52: }
#4 Source "/home/dgliu/backward-cpp_demo/src/backward-cpp_demo.cpp", line 17, in core_test
14: char *str = (char *)"test";
15: void core_test()
16: {
> 17: str[1] = 'T';
18: }
19:
20: // This is definitely not async-signal-safe. Let's hope it doesn't crash or hang.
#3 Object "/usr/lib/x86_64-linux-gnu/libc-2.31.so", at 0x7ff6ce6ec08f, in
#2 Source "/home/dgliu/backward-cpp_demo/src/backward-cpp_demo.cpp", line 27, in handler
24: {
25: file_stream << "Caught signal " << signo << ".\nTrace:\n";
26: backward::StackTrace st;
> 27: st.load_here(32);
28: backward::Printer p;
29: p.print(st, file_stream);
30: p.print(st, std::cout);
#1 Source "/home/dgliu/backward-cpp_demo/include/backward.hpp", line 880, in load_here
877: return 0;
878: }
879: _stacktrace.resize(depth);
> 880: size_t trace_cnt = details::unwind(callback(*this), depth);
881: _stacktrace.resize(trace_cnt);
882: skip_n_firsts(0);
883: return size();
#0 Source "/home/dgliu/backward-cpp_demo/include/backward.hpp", line 862, in unwind::callback>
860: template size_t unwind(F f, size_t depth) {
861: Unwinder unwinder;
> 862: return unwinder(f, depth);
863: }
864:
865: } // namespace details
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
编译选项配置:
-fsanitize=leak
-fsanitize=address
-fno-omit-frame-pointer
以Qt工程为例子
.pro
项目文件:SOURCES += main.cpp
# -fsanitize=leak意思为开启内存泄露检查
QMAKE_CXXFLAGS += "-fsanitize=leak"
QMAKE_CFLAGS += "-fsanitize=leak"
QMAKE_LFLAGS += "-fsanitize=leak"
# -fsanitize=address意思为开启内存越界检查
# -fno-omit-frame-pointer意思为显示更详细的信息
QMAKE_CXXFLAGS += "-fsanitize=address -fno-omit-frame-pointer"
QMAKE_CFLAGS += "-fsanitize=address -fno-omit-frame-pointer"
QMAKE_LFLAGS += "-fsanitize=address"
windows下的堆栈打印一般使用Crashrpt
几年前,我也修改过一个版本,代码地址在:https://gitee.com/liudegui/CrashExporter