优雅地打印堆栈跟踪信息——Backward-cpp
文章目录
- 优雅地打印堆栈跟踪信息——Backward-cpp
- 介绍
- 编译Backward-cpp
- 下载源码
- 文件结构
- 安装第三方库
- 编译
- 测试
- 集成Backward-cpp
- 测试代码
- 安装
- backward.hpp
- backward.cpp
- 使用方法
- g++直接编译
- CMake
- backward.hpp backward.cpp加入程序编译
- backward-cpp作为子目录编译
- 使用`FetchContent()`:
- 修改CMAKE_MODULE_PATH
- Reference
- >>>>> 欢迎关注公众号【三戒纪元】 <<<<<
介绍
项目地址:backward-cpp
C/C++编程的同学经常会遇到程序出现段错误:
Segmentation fault (core dumped)
而 Backward 会将堆栈信息打印出来,帮助定位。有点像gdb时的backtrace
只有当源文件可访问时,它才能显示代码片段。
所有以管道“|”为前缀的“源”行和代码片段都是内联下一帧的帧。
项目源码本质上只有backward.hpp
文件,集成到自己的程序中非常方便,如果加入backward.cpp
文件一起编译,则自己代码中不需要调用Backward-cpp中的函数,非常方便。
编译Backward-cpp
下载源码
git clone https://github.com/bombela/backward-cpp.git
文件结构
主文件:
.
├── backward.cpp
└── backward.hpp
其中 backward.cpp
是在调用backward.hpp
文件:
#include "backward.hpp"
namespace backward {
backward::SignalHandling sh;
} // namespace backward
测试文件:
.test
├── rectrace.cpp
├── select_signals.cpp
├── stacktrace.cpp
├── suicide.cpp
├── test.cpp
├── test.hpp
└── _test_main.cpp
安装第三方库
sudo apt-get install libdw-dev
sudo apt-get install binutils-dev
sudo apt-get install libdwarf-dev
编译
# cmake
(base) qiancj@qiancj-HP-ZBook-G8:~/codes/download/backward-cpp/build$ cmake ..
-- The CXX compiler identification is GNU 9.4.0
...
-- Found libdw: /usr/lib/x86_64-linux-gnu/libdw.so
-- Found libbfd: /usr/lib/x86_64-linux-gnu/libbfd.so
-- Found libdwarf: /usr/lib/x86_64-linux-gnu/libdwarf.so
-- Found Backward: /home/qiancj/codes/download/backward-cpp
-- Configuring done
-- Generating done
-- Build files have been written to: /home/qiancj/codes/download/backward-cpp/build
# make
(base) qiancj@qiancj-HP-ZBook-G8:~/codes/download/backward-cpp/build$ make
Scanning dependencies of target backward_object
[ 7%] Building CXX object CMakeFiles/backward_object.dir/backward.cpp.o
...
Scanning dependencies of target test_rectrace
[ 21%] Building CXX object CMakeFiles/test_rectrace.dir/test/rectrace.cpp.o
...
[ 85%] Linking CXX executable test_suicide
...
[ 92%] Building CXX object CMakeFiles/test_select_signals.dir/test/select_signals.cpp.o
[100%] Linking CXX executable test_select_signals
[100%] Built target test_select_signals
# 编译生成文件
(base) qiancj@qiancj-HP-ZBook-G8:~/codes/download/backward-cpp/build$ ls
CMakeCache.txt cmake_install.cmake libbackward.a test_rectrace test_stacktrace test_test
CMakeFiles CTestTestfile.cmake Makefile test_select_signals test_suicide
测试
(base) qiancj@qiancj-HP-ZBook-G8:~/codes/download/backward-cpp/build$ ./test_stacktrace
-- running test case: minitrace
Stack trace (most recent call last):
#9 Object "", at 0xffffffffffffffff, in
#8 Object "/home/qiancj/codes/download/backward-cpp/build/test_stacktrace", at 0x55c82d46edad, in _start
#7 Source "../csu/libc-start.c", line 308, in __libc_start_main
#6 Source "/home/qiancj/codes/download/backward-cpp/test/_test_main.cpp", line 227, in main
224: }
225:
226: total_cnt += 1;
> 227: if (run_test(test)) {
228: printf("-- test case success: %s\n", test.name);
229: success_cnt += 1;
230: } else {
#5 Source "/home/qiancj/codes/download/backward-cpp/test/_test_main.cpp", line 140, in run_test
138: pid_t child_pid = fork();
139: if (child_pid == 0) {
> 140: exit(static_cast<int>(test.run()));
141: }
142: if (child_pid == -1) {
143: error(EXIT_FAILURE, 0, "unable to fork");
#4 Source "/home/qiancj/codes/download/backward-cpp/test/test.hpp", line 92, in run
90: TestStatus run() {
91: try {
> 92: do_test();
93: return SUCCESS;
94: } catch (const AssertFailedError &e) {
95: printf("!! %s\n", e.what());
#3 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 37, in do_test
34: Printer printer;
35:
36: StackTrace st;
> 37: collect_trace(st);
38:
39: printer.print(st, std::cout);
40: }
#2 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 31, in collect_trace
29: using namespace backward;
30:
> 31: void collect_trace(StackTrace &st) { st.load_here(); }
32:
33: TEST(minitrace) {
34: Printer printer;
#1 Source "/home/qiancj/codes/download/backward-cpp/backward.hpp", line 879, in load_here
876: return 0;
877: }
878: _stacktrace.resize(depth);
> 879: size_t trace_cnt = details::unwind(callback(*this), depth);
880: _stacktrace.resize(trace_cnt);
881: skip_n_firsts(0);
882: return size();
#0 Source "/home/qiancj/codes/download/backward-cpp/backward.hpp", line 861, in unwind<backward::StackTraceImpl<backward::system_tag::linux_tag>::callback>
859: template <typename F> size_t unwind(F f, size_t depth) {
860: Unwinder<F> unwinder;
> 861: return unwinder(f, depth);
862: }
863:
864: } // namespace details
-- test case success: minitrace
-- running test case: smalltrace
Stack trace (most recent call last):
#12 Object "", at 0xffffffffffffffff, in
#11 Object "/home/qiancj/codes/download/backward-cpp/build/test_stacktrace", at 0x55c82d46edad, in _start
#10 Source "../csu/libc-start.c", line 308, in __libc_start_main
#9 Source "/home/qiancj/codes/download/backward-cpp/test/_test_main.cpp", line 227, in main
224: }
225:
226: total_cnt += 1;
> 227: if (run_test(test)) {
228: printf("-- test case success: %s\n", test.name);
229: success_cnt += 1;
230: } else {
#8 Source "/home/qiancj/codes/download/backward-cpp/test/_test_main.cpp", line 140, in run_test
138: pid_t child_pid = fork();
139: if (child_pid == 0) {
> 140: exit(static_cast<int>(test.run()));
141: }
142: if (child_pid == -1) {
143: error(EXIT_FAILURE, 0, "unable to fork");
#7 Source "/home/qiancj/codes/download/backward-cpp/test/test.hpp", line 92, in run
90: TestStatus run() {
91: try {
> 92: do_test();
93: return SUCCESS;
94: } catch (const AssertFailedError &e) {
95: printf("!! %s\n", e.what());
#6 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 54, in do_test
51: Printer printer;
52:
53: StackTrace st;
> 54: a(st);
55:
56: printer.print(st, std::cout);
57: }
#5 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 48, in a
46: void b(StackTrace &st) { return c(st); }
47:
> 48: NOINLINE void a(StackTrace &st) { return b(st); }
49:
50: TEST(smalltrace) {
51: Printer printer;
#4 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 46, in b
44: void c(StackTrace &st) { return d(st); }
45:
> 46: void b(StackTrace &st) { return c(st); }
47:
48: NOINLINE void a(StackTrace &st) { return b(st); }
#3 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 44, in c
42: void d(StackTrace &st) { st.load_here(); }
43:
> 44: void c(StackTrace &st) { return d(st); }
45:
46: void b(StackTrace &st) { return c(st); }
#2 Source "/home/qiancj/codes/download/backward-cpp/test/stacktrace.cpp", line 42, in d
39: printer.print(st, std::cout);
40: }
41:
> 42: void d(StackTrace &st) { st.load_here(); }
43:
44: void c(StackTrace &st) { return d(st); }
#1 Source "/home/qiancj/codes/download/backward-cpp/backward.hpp", line 879, in load_here
876: return 0;
877: }
878: _stacktrace.resize(depth);
> 879: size_t trace_cnt = details::unwind(callback(*this), depth);
880: _stacktrace.resize(trace_cnt);
881: skip_n_firsts(0);
882: return size();
#0 Source "/home/qiancj/codes/download/backward-cpp/backward.hpp", line 861, in unwind<backward::StackTraceImpl<backward::system_tag::linux_tag>::callback>
859: template <typename F> size_t unwind(F f, size_t depth) {
860: Unwinder<F> unwinder;
> 861: return unwinder(f, depth);
862: }
863:
864: } // namespace details
-- test case success: smalltrace
-- tests passing: 2/2 (100%)
集成Backward-cpp
测试代码
#include<stdio.h>
#include<stdlib.h>
int main(){
char *c = "hello world";
c[1] = 'H';
}
安装
backward.hpp
Backward 仅是一个头文件库。
安装 Backward 很容易,只需将“backward.hpp”的副本与其他源文件一起放入C++项目中即可。你也可以使用 git submodule
或任何其他最适合你环境的方式,只要你能包含 ‘backward.hpp’。
backward.cpp
如果您希望 Backward 自动打印最常见的致命错误(段错误、中止、未处理的异常等)的堆栈跟踪,只需将“backward.cpp”的副本添加到项目中,一起编译即可。
使用方法
g++直接编译
源码中添加
#include<stdio.h>
#include<stdlib.h>
#define BACKWARD_HAS_DW 1
#include "backward.hpp"
namespace backward{
backward::SignalHandling sh;
}
int main(){
char *c = "hello world";
c[1] = 'H';
}
编译程序
g++ -o randy test_backward.cpp -g -ldw
test_backward.cpp: In function ‘int main()’:
test_backward.cpp:10:15: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
10 | char *c = "hello world";
| ^~~~~~~~~~~~~
运行程序
$ ./randy
Stack trace (most recent call last):
#3 Object "", at 0xffffffffffffffff, in
#2 Object "/home/qiancj/codes/test/backward/randy", at 0x562025c8eecd, in _start
#1 Source "../csu/libc-start.c", line 308, in __libc_start_main [0x7f5ff0c6d082]
#0 Source "/home/qiancj/codes/test/backward/test_backward.cpp", line 11, in main [0x562025c8efa4]
9: int main(){
10: char *c = "hello world";
> 11: c[1] = 'H';
12: }
Segmentation fault (Invalid permissions for mapped object [0x562025c9d0e8])
Segmentation fault (core dumped)
CMake
backward.hpp backward.cpp加入程序编译
文件结构
.
├── backward.cpp
├── backward.hpp
├── CMakeLists.txt
└── test_backward.cpp
CMakeLists.txt
cmake_minimum_required ( VERSION 2.6 FATAL_ERROR)
project(randy)
add_definitions(-std=c++14 -ofast)
set(CMAKE_CXX_FLAGS "-O0 -g -Wall -ggdb")
include_directories(${PROJECT_NAME} .)
add_executable(${PROJECT_NAME} test_backward.cpp backward.cpp)
运行结果
(base) qiancj@qiancj-HP-ZBook-G8:~/codes/test/backward/build$ ./randy
Stack trace (most recent call last):
#3 Object "[0xffffffffffffffff]", at 0xffffffffffffffff, in
#2 Object "./randy", at 0x55821e769b6d, in _start
#1 Object "/lib/x86_64-linux-gnu/libc.so.6", at 0x7f2471945082, in __libc_start_main
#0 Object "./randy", at 0x55821e769c44, in main
Segmentation fault (Invalid permissions for mapped object [0x55821e776005])
Segmentation fault (core dumped)
backward-cpp作为子目录编译
文件结构
.
├── backward-cpp # Backward-cpp 源文件夹,作为项目子目录
├── backward.cpp
├── backward.hpp
├── CMakeLists.txt
└── test_backward.cpp
CMakeLists.txt 中需要包含
add_subdirectory(/path/to/backward-cpp)
# This will add backward.cpp to your target
add_executable(mytarget mysource.cpp ${BACKWARD_ENABLE})
# This will add libraries, definitions and include directories needed by backward
# by setting each property on the target.
add_backward(mytarget)
实际CMakeLists.txt :
cmake_minimum_required ( VERSION 2.6 FATAL_ERROR)
project(randy)
add_definitions(-std=c++14 -ofast)
add_subdirectory(./backward-cpp)
set(CMAKE_CXX_FLAGS "-g -Wall")
include_directories(${PROJECT_NAME} .)
add_executable(${PROJECT_NAME} test_backward.cpp ${BACKWARD_ENABLE})
add_backward(${PROJECT_NAME})
使用FetchContent()
:
如果使用的是最新版本的CMake,则可以通过FetchContent向后集成
include(FetchContent)
# Also requires one of: libbfd (gnu binutils), libdwarf, libdw (elfutils)
FetchContent_Declare(backward
GIT_REPOSITORY https://github.com/bombela/backward-cpp
GIT_TAG v1.6)
FetchContent_MakeAvailable(backward)
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.cpp)
add_executable(example ${SOURCES} ${BACKWARD_ENABLE}) # Notice the "BACKWARD_ENABLE" here
add_backward(example)
这种做法不需要自己手动clone backward-cpp
源码。
修改CMAKE_MODULE_PATH
在这种情况下,可以将Backward-cpp安装为子目录:
list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp)
find_package(Backward)
# This will add libraries, definitions and include directories needed by backward
# through an IMPORTED target.
target_link_libraries(mytarget PUBLIC Backward::Backward)
这等效于使用 add_subdirectory()
的方法,但它使用 cmake 导入的目标机制。
Reference
-
github backward-cpp
-
C程序集成Backward-cpp使用示例