# LinuxCppCrashTrace **Repository Path**: liudegui/linux-cpp-crash-trace ## Basic Information - **Project Name**: LinuxCppCrashTrace - **Description**: C++的Linux程序在崩溃(coredump)后,打印调用堆栈的方法,支持x86和ARM.md - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2022-07-25 - **Last Updated**: 2025-04-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 1、概述 我们知道Linux程序如果使用-g编译,若程序发生崩溃(coredump),是可以使用gdb调试生成的dump文件,找到崩溃的位置的。 然后C++有一些组件是提供崩溃堆栈打印的,本文给出如下几种方法。 ## 2 、使用boost::stacktrace + boost::stacktrace是boost1.65版本以后支持的堆栈打印功能,使用前请确保安装了libboost-stacktrace; 如果没有安装,对于ubuntu系统,可使用如下方法安装 ```bash sudo apt-get install libboost-stacktrace-dev ``` + 测试代码 ```cpp #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 ``` + cmake编译需添加如下 ```cmake 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编译 ``` ## 3、Backward-cpp + [Backward-cpp](https://github.com/bombela/backward-cpp)是一个支持多个平台堆栈打印的组件,支持x86和ARM平台. + 它依赖与Linux其它组件,可以选择安装`libbfd`、`libdw`或者libdwarf。 + 如果是安装libbfd,使用时需启用`#define BACKWARD_HAS_BFD 1`宏; + 如果是安装libdw,使用时需启用`#define BACKWARD_HAS_DW 1`宏; + 如果是安装libdwarf,使用时需启用`#define BACKWARD_HAS_DWARF 1`宏; ### 3.1 以使用BACKWARD_HAS_DW举例 + 如果是Ubuntu系统,首先安装第三方库libdw-dev ```bash sudo apt-get install libdw-dev ``` + 下载头文件backward.hpp; + 在CMakeLists target_link_libraries 中添加 dw + 在主程序中添加 backward ```cpp #define BACKWARD_HAS_DW 1 #include "backward.hpp" namespace backward{ backward::SignalHandling sh; } ``` + 非常简单的测试代码为: ```cpp #include #include #define BACKWARD_HAS_DW 1 #include "backward.hpp" namespace backward{ backward::SignalHandling sh; } int main(){ char *c = "hello world"; c[1] = 'H'; } ``` + 如果想输出到日志文件,需要使用到backward的Print接口,完整实例代码可为: ```cpp #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 ``` + cmake编译需添加如下 ```cmake set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") ``` ## 4、Qt Creator启用内存泄漏/越界检查工具 **编译选项配置**: 1. 开启内存泄露检查功能:`-fsanitize=leak` 2. 开启地址越界检查功能:`-fsanitize=address` 3. 开启越界详细错误信息:`-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" ``` ## 5、参考 + [backward-cpp](https://github.com/bombela/backward-cpp) + [Backward-cpp 妈妈再也不用担心segmentation fault!](https://zhuanlan.zhihu.com/p/397148839) + [开源项目推荐:C++堆栈跟踪打印器,backward-cpp](https://blog.csdn.net/libaineu2004/article/details/114594091) + [C++打印调用栈](https://blog.csdn.net/u011889952/article/details/118762819) + [How to store back trace info to a file?](https://github.com/bombela/backward-cpp/issues/260)