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

forward-loss-backward-update 是一种在神经网络中常用的训练方法。它由以下四个步骤组成: 首先是"forward"(前向传播)步骤。在该步骤中,输入数据会通过网络的各个层级进行计算传递。每一层都会执行一些权重计算激活函数的操作,将结果传递给下一层。最终,前向传播会生成网络的输出。 接下来是"loss"(损失计算)步骤。该步骤主要用于计算网络的预测结果与真实标签之间的差距,即损失函数。损失函数可以是交叉熵、平方误差等。通过计算损失,我们可以得到一个衡量网络预测准确性的值。 然后是"backward"(反向传播)步骤。在该步骤中,反向传播算法会利用损失函数的梯度信息来计算每个参数的梯度。这些梯度指示了网络中各个参数对最终损失函数的贡献程度。通过反向传播,我们可以得到网络中各个参数的梯度值。 最后是"update"(参数更新)步骤。在该步骤中,我们使用梯度下降法或其他优化算法来更新网络中的参数。通过使用计算得到的梯度信息,我们可以调整网络中的参数,使其向着最小化损失函数的方向更新。 总结起来,forward-loss-backward-update 是一种有效的神经网络训练方法。通过前向传播计算网络输出,计算损失函数评估网络性能,利用反向传播计算参数梯度,最后使用参数更新方法来优化网络性能。这个过程迭代进行,使得网络能够逐渐学习到更好的表示预测能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

橘色的喵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值