关于Linux下C++程序内存dump的分析和工具

前言

         程序崩溃令人很崩溃,特别是让人找不到原因的崩溃,但是合适的工具可以帮助人很快的定位到问题,在AI基础能力ASR服务开发时,找到了一种比较实用和简单的内存崩溃的dump分析工具breakpad,

可以帮助在Linux下C++开发程序时发生崩溃快速定位

breakpad简介

       Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,支持Linux、mac、solaris、windows。可以借助Google breakpad来捕捉程序程序崩溃的错误报告。即在程序崩溃时会生成dump文件。

而dump文件是进程的内存镜像,能够保存程序中断时的进程状态,让我们在程序崩溃后能够了解具体原因。

breakpad 结构和原理示意图

获取breakpad

     breakpad在github网站上的地址为: GitHub - google/breakpad: Mirror of Google Breakpad project


     在ASR工程化服务中也已经集成好了breakpad库breakpad.zip

breakpad使用

代码示例

     网上搜索的办法不一而足,缺陷较多,很多没有兼顾到的地方,而且使用过程中有许多需要注意的地方;在AI基础能力开发过程中,我们已经形成了比较简单和易操作的方式来使用breakpad,使用breakpad需要嵌入的代码十分简单,以下是示例:

breakpad示例

#include "breakpad/src/client/linux/handler/exception_handler.h"
#include <string>bool DumpCallback(const google_breakpad::MinidumpDescriptor &descr, void *context, bool succeeded) {return succeeded;}int main(int argc, char** argv){//breakpad  只接受绝对路径的dump dirstd::string dump_dir = "/home/alex/dumpdir";//为breakpad创建dump存放目录mkdir(dump_dir.data(), 0775);google_breakpad::MinidumpDescriptor descriptor(dump_dir);// minidump文件目录google_breakpad::ExceptionHandler eh(descriptor, NULL, CppProcess::DumpCallback, NULL, true, -1);do_your_stuff();
}

编写代码工程时,包含breakpad的头文件,并指定程序链接libbreakpad_client.a库

如上,在为breakpad需要生成dump文件准备好相应的目录后,创建一个descriptor和eh实例即可,在程序崩溃后,breakpad会在/home/alex/dumpdir目录下创建一个后缀为dump的文件

注意事项

  1. breakpad所创建的实例,descriptor和eh,属于栈上的对象,在其生命期内可以接受异常,它要尽可能早的创建,和尽可能晚的关闭,基于这个原则,最好是把它放在main函数的开头
  2. breakpad生成dump的目录需要传入绝对路径
  3. 对于 DumpCallback回调函数,应当尽量写的简短,就像内联函数一样,因为程序在崩溃后所能做的操作有限,某些阻塞性的系统调用如申请内存,调用其他库的函数等操作可能无法完成
  4. 为了生成有用的信息,编译程序时,需要加上-g编译选项,使程序和库包含调试信息

崩溃分析

      当程序发生崩溃时,通过前面的方式获取到dump文件后,接下来就是分析崩溃文件,找到程序崩溃的位置和原因,需要做以下几步:

  1.  从breakpad的结构和原理示意图中可以了解到,要得到最终的信息,需要结合程序和库的符号信息,和dump文件,来生成可读的栈信息,breakpad提供了从程序和库中分离出符号的工具,附带在breakpad库中,会随着breakpad库一起编译出来,
    工具程序是dump_syms,下面是一个从程序或者库文件中分离出符号信息(注意编译时的-g选项)的示例代码:

    分离程序或库中的符号信息

    #!/bin/bashfor  program in `ls ./`
    do#生成相应库文件的sym文件
    ./dump_syms ./$program > $program.sym#获取属于该库文件的一个唯一编号,如00A5F6B1C92FB3657CC65C7B1C4E62920
    uuid=`head -n1 $program.sym | awk '{print $4}'`#获取该文件在符号信息中的名称,可能和程序名一致,如http_service
    prodir=`head -n1 $program.sym | awk '{print $5}'`#创建存放sym文件的目录
    mkdir -p ./symbols/$prodir/$uuid#将符号文件移动到相应位置下
    mv $program.sym ./symbols/$prodir/$uuid/$prodir.symdone

    以上是一个小脚本,可以为当前目录下所有文件生成符号信息,为后续做准备。
     

  2. 当程序崩溃时,会生成一个dump文件,一般是这种格式:59638c7c-ae27-4d04-bf4c4eac-75e328be.dmp在做好了上一步准备后,下一步是生成人类可读的栈信息,仍然需要使用从breakpad库中编译得到的一个工具命令minidump_stackwalk,使用方法如下:

  3. 生成堆栈信息

    ./minidump_stackwalk 59638c7c-ae27-4d04-bf4c4eac-75e328be.dmp symbols/

紧接上一步,就会生成本次程序崩溃的相关信息,以下是对于栈崩溃的分析示例:

崩溃栈示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

Operating system: Linux

                  0.0.0 Linux 4.15.0-52-generic #56-Ubuntu SMP Tue Jun 4 22:49:08 UTC 2019 x86_64

CPU: amd64

     family 6 model 85 stepping 4

     1 CPU

GPU: UNKNOWN

Crash reason:  SIGSEGV /SEGV_MAPERR

Crash address: 0x8

Process uptime: not available

Thread 35 (crashed)

 0  libtlvkaldi.so!sub_func_add_timeinfo [tlv_kaldi_dec_sub_func.cc : 34 + 0x8]

    rax = 0x0000000000000000   rdx = 0x00007fde7ce397c0

    rcx = 0x00007fde7c0008d0   rbx = 0x00007fde7cfe77e0

    rsi = 0x00007fde7c0008e8   rdi = 0x00007fde7cfe77e0

    rbp = 0x00007fde7ce397c0   rsp = 0x00007fdf0bffcc70

     r8 = 0x00007fe242ee48f8    r9 = 0x00007fde7c0008d0

    r10 = 0x0000000000000018   r11 = 0x00007fde7ce38930

    r12 = 0x00007fded5a21ba8   r13 = 0x00007fded5a21b40

    r14 = 0x0000000000000001   r15 = 0x0000000000000014

    rip = 0x00007fe242195c38

    Found by: given as instruction pointer in context

 1  libtlvkaldi.so!tlv_kaldi_dec_get_rslt [tlv_kaldi_dec.cc : 293 + 0x17]

    rbx = 0x00007fde77e17c10   rbp = 0x00007fde7c008310

    rsp = 0x00007fdf0bffcd20   r12 = 0x00007fde7c18bb50

    r13 = 0x00007fde5ab21300   r14 = 0x00007fdf0bffcdd4

    r15 = 0x00007fdf0bffcdd8   rip = 0x00007fe242194b39

    Found by: call frame info

 2  libprotos.so!TlvKaldiVadec::Decode(char*, unsigned int, VadecDataInfo const&) [tlv_kaldi_vadec.cc : 181 + 0x13]

    rbx = 0x00007fdf0bffd030   rbp = 0x00007fde88ffe230

    rsp = 0x00007fdf0bffcda0   r12 = 0x0000000000000c80

    r13 = 0x00007fdf0bffd1ac   r14 = 0x15f0b8657b590764

    r15 = 0x0000000000000000   rip = 0x00007fe243fef36b

    Found by: call frame info

 3  libprotos.so!boost::detail::thread_data<TlvKaldiVadec::Init(tlv_kaldi_vadec_callback_t, std::__cxx11::string)::<lambda()> >::run [tlv_kaldi_vadec.cc : 56 + 0xe]

    rbx = 0x00007fde88ffe230   rbp = 0x00007fde7c0010a0

    rsp = 0x00007fdf0bffd1a0   r12 = 0x00007fded3c4c940

    r13 = 0x00007fdf0bffd1ac   r14 = 0x00007fdf0bffd1a8

    r15 = 0x0000000000000004   rip = 0x00007fe243fefc0e

    Found by: call frame info

 4  libprotos.so!thread_proxy [thread.cpp : 171 + 0x9]

    rbx = 0x00007fded3c4c940   rbp = 0x0000000000000000

    rsp = 0x00007fdf0bffd200   r12 = 0x00007fdea54720e0

    r13 = 0x0000000000000000   r14 = 0x00007fded3c4c940

    r15 = 0x00007fe238e75470   rip = 0x00007fe243ffe38d

    Found by: call frame info

 5  libpthread-2.27.so + 0x76db

    rbx = 0x0000000000000000   rbp = 0x0000000000000000

    rsp = 0x00007fdf0bffd240   r12 = 0x00007fdf0bffd300

    r13 = 0x0000000000000000   r14 = 0x00007fded3c4c940

 示例开头描述了操作系统的一下信息:
Operating system: Linux
                                     0.0.0 Linux 4.15.0-52-generic #56-Ubuntu SMP Tue Jun 4 22:49:08 UTC 2019 x86_64


紧接着描述CPU指令集
CPU: amd64
           family 6 model 85 stepping 4
           1 CPU

崩溃原因描述:这里是进程的SIGSEGV/SEGV_MAPERR信号,一般是段错误造成的崩溃

Crash reason: SIGSEGV /SEGV_MAPERR
Crash address: 0x8
Process uptime: not available

其后就是我们所需要关注的程序的栈信息,它以线程thread为分组,从编号0开始向下递增,每一个编号的部分表示一层函数调用栈,也就是0层是本线程的最顶层栈,标号行也描述了栈调用的函数所在的库,文件和行号,以方便程序员定位和分析崩溃的代码
如下示例,Thread 35 (crashed)表示在35号线程崩溃,0号栈是线程调用栈顶层,右侧描述了该函数在libtlvkaldi.so库的tlv_kaldi_dec_sub_func.cc文件,函数名sub_func_add_timeinfo,在文件的34行,紧接着是该函数的栈的各寄存器的值,2号栈调用1号栈,1号栈
调用0号栈,崩溃发生在0号所在位置,此时即可分析代码,找出可能造成崩溃的原因

崩溃栈示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Thread 35 (crashed)

 0  libtlvkaldi.so!sub_func_add_timeinfo [tlv_kaldi_dec_sub_func.cc : 34 + 0x8]

    rax = 0x0000000000000000   rdx = 0x00007fde7ce397c0

    rcx = 0x00007fde7c0008d0   rbx = 0x00007fde7cfe77e0

    rsi = 0x00007fde7c0008e8   rdi = 0x00007fde7cfe77e0

    rbp = 0x00007fde7ce397c0   rsp = 0x00007fdf0bffcc70

     r8 = 0x00007fe242ee48f8    r9 = 0x00007fde7c0008d0

    r10 = 0x0000000000000018   r11 = 0x00007fde7ce38930

    r12 = 0x00007fded5a21ba8   r13 = 0x00007fded5a21b40

    r14 = 0x0000000000000001   r15 = 0x0000000000000014

    rip = 0x00007fe242195c38

    Found by: given as instruction pointer in context

 1  libtlvkaldi.so!tlv_kaldi_dec_get_rslt [tlv_kaldi_dec.cc : 293 + 0x17]

    rbx = 0x00007fde77e17c10   rbp = 0x00007fde7c008310

    rsp = 0x00007fdf0bffcd20   r12 = 0x00007fde7c18bb50

    r13 = 0x00007fde5ab21300   r14 = 0x00007fdf0bffcdd4

    r15 = 0x00007fdf0bffcdd8   rip = 0x00007fe242194b39

    Found by: call frame info

 2  libprotos.so!TlvKaldiVadec::Decode(char*, unsigned int, VadecDataInfo const&) [tlv_kaldi_vadec.cc : 181 + 0x13]

breakpad与工程代码的管理

            在实际工程应用中,编译出的二进制文件与符号表应当是分开存放的,二进制文件中不应当附带符号信息,从编译到部署到生产环境,大致分为以下几步:
    1)编译代码后,使用dump_syms工具将编译出的二进制文件内的符号表(symblos)分离出来,按照版本存放

    2)使用strip命令将编译后的二进制文件内的符号信息剥除

    3)将剥除后的程序部署,后按照正常测试,上线

    4)若运行过程中发生崩溃,生成了dump文件,将dump文件和1)中保存的相应版本的符号表信息按照前文所说的步骤获得栈崩溃上下文信息,然后分析崩溃原因

总结 


       工欲善其事,必先利其器!
在C++工程所生成的库和程序中,不应当附带符号信息,以防止逆向工程,而且带有符号信息会显著增加程序和库的文件大小,不方便传输;
可以在分离出符号信息并保存后,使用linux的stip命令将工程的C++程序和库的符号信息去除,缩小文件大小;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/446096.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QD1-P8 HTML 格式化标签(font、pre、b、strong、i、u、del、s、sub、sup)

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…

难点:Linux 死机定位(进程虚拟地址空间耗尽)

死机定位(进程虚拟地址空间耗尽) 一、死机现象 内存富裕,但内存申请失败。 死机时打印: 怀疑是: 1、内存碎片原因导致。 2、进程虚拟地址空间耗尽导致。 3、进程资源限制导致。 二、内存碎片分析 1、理论知识:如何分析内存碎片化情况 使用 /proc/buddyinfo: /proc/…

CCF推荐被调查,这8本被标记On Hold

近两年“On Hold”期刊频出&#xff0c;作为投稿选刊风向标&#xff0c;上榜期刊一定要避雷投稿&#xff01;本期科检易学术小编盘点目前被科睿唯安官方标记为“On Hold”的计算机工程类的8本期刊&#xff0c;提醒广大学者&#xff0c;选刊需谨慎&#xff0c;注意避雷哦&#x…

Spark高级用法-内置函数

目录 读取数据 1.字符串 2.数值类 3.时间类型 4.条件判断 5.窗口函数 读取数据 # 内置数据集 from pyspark.sql import SparkSession,functions as F ss SparkSession.builder.getOrCreate()# 读取文件准尉df df ss.read.csv(hdfs://node1:8020/data/students.csv,hea…

【uniapp】使用uniapp实现一个输入英文单词翻译组件

目录 1、组件代码 2、组件代码 3、调用页面 4、展示 前言&#xff1a;使用uniapp调用一个在线单词翻译功能 1、组件代码 2、组件代码 YouDaoWordTranslator <template><view class"translator"><input class"ipttext" type"te…

JVM 内存模型与垃圾回收过程详解

JVM 内存模型与垃圾回收过程详解 文章目录 JVM 内存模型与垃圾回收过程详解1. JVM内存分区1.1 具体分区1.2 JVM内存分区的必要性 2. 垃圾回收2.1 CMS垃圾回收器2.2 G1垃圾回收器2.3 JVM垃圾回收从新生代到老年代 1. JVM内存分区 1.1 具体分区 Java虚拟机&#xff08;JVM&#…

计算机毕业设计 内蒙古旅游景点数据分析系统的设计与实现 Python毕业设计 Python毕业设计选题 Spark 大数据【附源码+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

06-ArcGIS For JavaScript-requestAnimationFrame动画渲染

文章目录 概述setInterval&#xff08;&#xff09;与setTimeout()requestAnimationFrame()requestAnimationFrame在ArcGIS For JavaScript的应用结果 概述 本节主要讲解与时间相关的三个方法setTimeout()、setInterval()和requestAnimationFrame()&#xff0c;这三个方法都属…

Java每日面试题(集合)(day18)

目录 常见的集合有哪些&#xff1f;Collection和Collections有什么区别&#xff1f;ArrayList 和 Array&#xff08;数组&#xff09;的区别&#xff1f;ArrayList 和 LinkedList 的区别是什么&#xff1f;Arraylist 和 Vector 的区别HashMap和Hashtable的区别哪些集合类是线程…

计算机丢失mfc100u.dll的5种常用解决方法分享

1.mfc100u.dll的重要性 系统运行中的应用 mfc100u.dll 文件在 Windows 系统中扮演着至关重要的角色&#xff0c;尤其是在运行基于 MFC 库开发的应用程序时。以下是 mfc100u.dll 在系统运行中的应用和重要性分析&#xff1a; 系统稳定性与兼容性的关键 mfc100u.dll 文件确保…

map和set(一)

首先模拟一下key形式类 使用的结构是搜索二叉树 结点中有左孩子和右孩子 还有一个存储的值 template <class K>struct BSTnode//搜索二叉树不支持修改 中序遍历是有序的{K _key;BSTnode<K>* _left;BSTnode<K>* _right;BSTnode(const K& key):_key(key…

adum1201数字隔离器中文资料与应用

ADuM1201是ADI公司推出的一款数字隔离器&#xff0c;其典型应用有工业自动化、通讯电源管理、医疗设备以及汽车等领域。本文将对ADuM1201数字隔离器进行详细的介绍和应用分析&#xff0c;以帮助读者更好地了解和使用该产品。 一、ADuM1201数字隔离器概述 1、基本参数 ADuM120…

卷积神经网络细节问题及知识点

一、Batch Normalization Batch Normalization&#xff08;BN&#xff0c;批归一化&#xff09; 是深度学习中的一种技术&#xff0c;主要用于加速神经网络的训练过程&#xff0c;同时提高网络的稳定性和收敛速度。它通过对每一层的输出进行归一化&#xff0c;减少梯度消失和梯…

Ubuntu安装Mysql并实现远程登录【ubuntu 24.04/mysql 8.0.39】

一、安装MySQL sudo apt update # 更新软件源 sudo apt install mysql-server -y # 安装 mysql --version # 查看版本 sudo systemctl status mysql # 查看运行状态 netstat -tln # 以数字ip形式显示mysql的tcp监听状态二、设置MySQL的root密码 sudo mysql -u root # 使…

第二十三篇:网络拥塞了,TCP/IP如何解决的?

一.显示拥塞通知 当发生网络拥塞时&#xff0c;发送主机应该减少数据包的发送量。作为IP上层协议&#xff0c;TCP虽然也能控制网络拥塞&#xff0c;不过它是通过数据包的实际损坏情况来判断是否发生拥塞。然而这种方法不能在数据包损坏之前减少数据包的发送量。 为了解决这个…

pytorch与卷积神经网络实战笔记

课程视频链接 CNN卷积神经网络算法原理 全神经网络的整体结构 输入层&#xff08;x1, x2, x3…&#xff09;->隐藏层&#xff08;全连接&#xff09;->输出层&#xff0c;整体就类似于一个函数&#xff0c;输入x&#xff0c;经过函数module(x)得到输出y的过程&#xf…

笔试算法总结

文章目录 题目1题目2题目3题目4 题目1 使用 StringBuilder 模拟栈的行为&#xff0c;通过判断相邻2个字符是否相同&#xff0c;如果相同就进行删除 public class Main {public static String fun(String s) {if (s null || s.length() < 1) return s;StringBuilder builde…

闲谈Promise

预备知识 回调函数&#xff1a;当一个函数作为参数传入另一个函数中&#xff0c;并且它不会立刻执行&#xff0c;当满足一定条件之后&#xff0c;才会执行&#xff0c;这种函数称为回调函数。比如&#xff1a;定时器。异步任务&#xff1a;与之对应的概念是同步任务&#xff0…

【Axure原型分享】标签管理列表

今天和大家分享通过标签管理列表的原型模板&#xff0c;包括增删改查搜索筛选排序分页翻页等效果&#xff0c;这个模板是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;初始数据我们只要在中继器表格里填写即可&#xff0c;具体效果可以观看下方视频或者打开预览地址…

Cesium.js(SuperMap iClient3D for Cesium)进行三维场景展示和图层动画

1&#xff09;&#xff1a;参考API文档&#xff1a;SuperMap iClient3D for Cesium 开发指南 2&#xff09;&#xff1a;官网示例&#xff1a;support.supermap.com.cn:8090/webgl/Cesium/examples/webgl/examples.html#layer 3&#xff09;&#xff1a;SuperMap iServer&…