C++的Linux程序在崩溃(coredump)后,打印调用堆栈的方法,支持x86和ARM

1. 概述

在Linux程序开发中,当使用-g选项编译程序时,如果程序发生崩溃(coredump),可以利用gdb调试生成的dump文件定位崩溃位置。对于C++程序,有几种组件可以用于打印崩溃时的堆栈信息。本文介绍了其中几种方法。

测试代码已上传至gitee

2. 使用boost::stacktrace

boost::stacktrace 是从boost 1.65版本开始支持的堆栈跟踪功能。使用前请确保已安装libboost-stacktrace-dev

安装方法(针对Ubuntu系统)

sudo apt-get install libboost-stacktrace-dev

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>

char *str = (char *)"test";
void core_test()
{
    str[1] = 'T';
}

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(signo);
}

int main()
{
    struct sigaction act{};
    act.sa_flags = SA_NODEFER | SA_RESETHAND;
    act.sa_handler = &handler;
    sigfillset(&act.sa_mask);
    sigdelset(&act.sa_mask, SIGSEGV);
    if (sigaction(SIGSEGV, &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编译配置

set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -fno-pie -ggdb3 -O0 -no-pie")

关于no-pie的说明已在文中提及。

3. 使用第三方库Backward-cpp

Backward-cpp 是一个支持多平台堆栈跟踪的库,适用于x86和ARM平台。

安装及配置示例(以BACKWARD_HAS_DW为例)

Ubuntu系统安装libdw-dev
sudo apt-get install libdw-dev
示例代码
#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';
}
输出到日志文件的示例代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fstream>
#include <iostream>

#define BACKWARD_HAS_DW 1
#include "backward.hpp"
namespace backward
{
    backward::SignalHandling sh;
}

char *str = (char *)"test";
void core_test()
{
    str[1] = 'T';
}

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);
        p.print(st, std::cout);
    }

    raise(signo);
}

int main()
{
    struct sigaction act {};
    act.sa_flags = SA_NODEFER | SA_RESETHAND;
    act.sa_handler = &handler;
    sigfillset(&act.sa_mask);
    sigdelset(&act.sa_mask, SIGSEGV);
    if (sigaction(SIGSEGV, &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<backward::StackTraceImpl<backward::system_tag::linux_tag>::callback>
        860: template <typename F> size_t unwind(F f, size_t depth) {
        861:   Unwinder<F> unwinder;
      > 862:   return unwinder(f, depth);
        863: }

CMake编译配置

set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

4. Qt Creator启用内存泄漏/越界检查工具

配置方法示例:

SOURCES += main.cpp

# 开启内存泄露检查功能
QMAKE_CXXFLAGS += "-fsanitize=leak"
QMAKE_CFLAGS   += "-fsanitize=leak"
QMAKE_LFLAGS   += "-fsanitize=leak"

# 开启地址越界检查功能
# 显示更详细的信息
QMAKE_CXXFLAGS += "-fsanitize=address -fno-omit-frame-pointer"
QMAKE_CFLAGS   += "-fsanitize=address -fno-omit-frame-pointer"
QMAKE_LFLAGS   += "-fsanitize=address"

5. 参考

其他

在Windows下,一般使用Crashrpt来进行堆栈打印。如果有需要,几年前我曾修改过一个版本,代码地址在gitee

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 下,程序崩溃后可以通过调用栈信息来帮助我们定位问题。我们可以使用一些工具和技术来获取程序崩溃时的调用栈信息。 1. 使用 GDB 调试器 GDB 是一个功能强大的调试器,在程序崩溃时可以用它来获取调用栈信息。我们可以在终端中使用以下命令来使用 GDB 调试器: ``` gdb <程序名> ``` 进入 GDB 调试器后,可以使用 `bt` 命令来打印当前的调用栈信息。 2. 使用 Core Dump 文件 Core Dump 文件是程序崩溃时产生的一种文件,它可以包含程序崩溃时的状态信息,包括调用栈信息。我们可以使用一些工具来分析 Core Dump 文件,比如 GDB 或者 Valgrind 工具。 在 Linux 中,可以使用以下命令来开启 Core Dump 文件的生成: ``` ulimit -c unlimited ``` 这个命令会将 Core Dump 文件的大小设置为无限制,这样当程序崩溃时就会生成 Core Dump 文件。 3. 使用 backtrace() 函数 在程序崩溃时,我们可以使用 backtrace() 函数来获取当前的调用栈信息。这个函数定义在 execinfo.h 头文件中,使用时需要链接 libexecinfo 库。 backtrace() 函数的原型如下: ```c int backtrace(void **buffer, int size); ``` 第一个参数是一个指向 void* 类型的数组,用来存储调用栈信息;第二个参数是数组的大小,用来控制 backtrace() 函数最多返回多少个调用栈帧的信息。 在程序崩溃时,我们可以在程序的信号处理函数中调用 backtrace() 函数来获取当前的调用栈信息,然后输出到日志文件中。 4. 使用 libunwind 库 libunwind 是一个开源的 C/C++ 库,用于获取当前线程的调用栈信息。它可以在程序崩溃时获取调用栈信息,也可以在程序运行时获取调用栈信息。 使用 libunwind 库时,我们需要在程序中链接 libunwind 库,并且包含 libunwind.h 头文件。然后,我们可以使用 libunwind 库中的函数来获取当前线程的调用栈信息,例如: ```c #include <libunwind.h> void print_backtrace() { unw_cursor_t cursor; unw_context_t context; // 获取当前线程的上下文信息 unw_getcontext(&context); // 初始化 cursor unw_init_local(&cursor, &context); // 遍历调用栈帧,输出每个帧的信息 while (unw_step(&cursor) > 0) { unw_word_t ip; unw_get_reg(&cursor, UNW_REG_IP, &ip); printf("%lx\n", (long)ip); } } ``` 上面的代码中,我们定义了一个函数 `print_backtrace()`,它可以输出当前线程的调用栈信息。使用时,我们只需要在程序崩溃调用这个函数即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值