优雅地打印堆栈跟踪信息——Backward-cpp

优雅地打印堆栈跟踪信息——Backward-cpp

优雅地打印堆栈跟踪信息——Backward-cpp_第1张图片

文章目录

  • 优雅地打印堆栈跟踪信息——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-cpp_第2张图片

只有当源文件可访问时,它才能显示代码片段。

所有以管道“|”为前缀的“源”行和代码片段都是内联下一帧的帧。

项目源码本质上只有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::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::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
#include

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
#include
#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使用示例


>>>>> 欢迎关注公众号【三戒纪元】 <<<<<

你可能感兴趣的:(C++,c++,backtrace)