优雅地打印堆栈跟踪信息——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使用示例 








![[移动通讯]【Carrier Aggregation-4】【LTE-4】](https://img-blog.csdnimg.cn/bfd45e55e3a24bf3a3d524c3def0cd3b.png)










