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

news2024/11/25 14:23:15

前言

         程序崩溃令人很崩溃,特别是让人找不到原因的崩溃,但是合适的工具可以帮助人很快的定位到问题,在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 dir
     
    std::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/bash
     
    for  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.sym
     
    done

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

  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.coloradmin.cn/o/2208701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Skyeye 云智能制造 v3.14.8 发布,ERP 商城 + AI

Skyeye 云智能制造&#xff0c;采用 Springboot winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表…

(JAVA)2-3树思想与红黑树的实现与基本原理

1. 平衡树 ​ 学习过二叉查找树&#xff0c;发现它的查询效率比单纯的链表和数组的查询效率要高很多。 ​ 大部分情况下确实是这样的&#xff0c;但不幸的是&#xff0c;在最坏情况下&#xff0c;二叉查找树的性能还是很糟糕。 ​ 例如我们一次往二叉树中插入9,8,7,6,5,4,3,…

【LeetCode】动态规划—714. 买卖股票的最佳时机含手续费(附完整Python/C++代码)

动态规划—714. 买卖股票的最佳时机含手续费 题目描述前言基本思路1. 问题定义2. 理解问题和递推关系状态定义&#xff1a;状态转移公式&#xff1a;初始条件&#xff1a; 3. 解决方法动态规划方法伪代码&#xff1a; 4. 进一步优化5. 小总结 Python代码Python代码解释总结&…

出海电商新怎样用海外云手机引流?

随着互联网行业的迅猛发展&#xff0c;出海电商、海外社交媒体营销以及游戏产业等领域对技术工具的需求不断增加。在这种趋势下&#xff0c;海外云手机作为一种新型解决方案&#xff0c;正在受到广泛关注。 特别是在出海电商中&#xff0c;平台如亚马逊、速卖通、eBay等通过结合…

Mysql(八) --- 视图

文章目录 前言1.什么是视图&#xff1f;2.创建视图3. 使用视图4. 修改数据4.1.注意事项 5. 删除视图6.视图的优点 前言 前面我们学习了索引&#xff0c;这次我们来学习视图 1.什么是视图&#xff1f; 视图是一个虚拟的表&#xff0c;它是基于一个或多个基本表或其他视图的查询…

8款宝藏手机app,适配安卓和苹果手机

好用的手机APP太多&#xff0c;差点挑花了眼&#xff01;今天来分享4款苹果手机和4款安卓手机上的宝藏软件&#xff0c;看看你喜欢哪一款~ IOS系统APP 1.搜图神器 一款拥有海量图片资源的图片搜索神器&#xff0c;它聚合海内外知名搜索引擎&#xff0c;想要图片直接搜索就行…

用java来编写web界面

一、ssm框架整体目录架构 二、编写后端代码 1、编写实体层代码 实体层代码就是你的对象 entity package com.cv.entity;public class Apple {private Integer id;private String name;private Integer quantity;private Integer price;private Integer categoryId;public…

【JavaScript】LeetCode:61-65

文章目录 61 课程表62 实现Trie&#xff08;前缀树&#xff09;63 全排列64 子集65 电话号码的字母组合 61 课程表 Map BFS拓扑排序&#xff1a;将有向无环图转为线性顺序。遍历prerequisites&#xff1a;1. 数组记录每个节点的入度&#xff0c;2. 哈希表记录依赖关系。n 6&a…

Vulnhub靶场案例渗透[7]- DC7

文章目录 1. 靶场搭建2. 信息收集2.1 确定靶机ip2.2 服务信息收集2.3 社工信息收集 3. 提权 1. 靶场搭建 靶场源地址 检验下载文件的检验码&#xff0c;对比没问题使用vmware打开 # windwos 命令 Get-FileHash <filePath> -Algorithm MD5 # linux md5sum filepath2. 信…

视频汇聚平台EasyCVR支持云端录像丨监控存储丨录像回看丨录像计划丨录像配置

EasyCVR视频汇聚融合平台&#xff0c;是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。平台以其强大的视频处理、汇聚与融合能力&#xff0c;在构建视频监控系统中展现出了独特的优势。 EasyCVR视频汇聚平台可接入传统监控行业中高清网络摄像机的RTSP…

提升实验室效率的秘籍

有组织、高效的实验室而言&#xff0c;业务“人、机、料、法、环、测”的多维度发展至关重要&#xff0c;为了提高实验室管理效率和质量&#xff0c;许多实验室开始采用LIMS&#xff08;实验室信息管理系统&#xff09;软件来辅助管理。LIMS软件能够帮助实验室实现信息化、自动…

leetcode 3217 从链表中移除在数组中的结点

1.题目要求: 给你一个整数数组 nums 和一个链表的头节点 head。从链表中移除所有存在于 nums 中的节点后&#xff0c;返回修改后的链表的头节点。 示例 1&#xff1a; 输入&#xff1a; nums [1,2,3], head [1,2,3,4,5] 输出&#xff1a; [4,5] 解释&#xff1a; 移除数值…

Java中的枚举

1.1 认识枚举 枚举是一种特殊的类&#xff0c;它的格式是&#xff1a; public enum 枚举类名{枚举项1,枚举项2,枚举项3; } 其实枚举项就表示枚举类的对象&#xff0c;只是这些对象在定义枚举类时就预先写好了&#xff0c;以后就只能用这几个固定的对象。 定义一个枚举类&am…

使用VS2015编写C语言程序

前面我们给出了一段完整的C语言代码&#xff0c;就是在显示器上输出“C语言中文网”&#xff0c;如下所示&#xff1a; #include <stdio.h>int main(){puts("C语言中文网");return 0;}本节我们就来看看如何通过 VS2015 来运行这段代码。 1) 创建项目&#xf…

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…

Tkinter:为什么多个Frame相互覆盖?

在 Tkinter 中&#xff0c;Frame 是一个容器部件&#xff0c;用于组织和管理布局。如果多个 Frame 出现在同一个父容器中并且看起来相互覆盖&#xff0c;通常与布局管理器的使用方式或控件的创建顺序有关。 以下是几个常见的原因和解决方案&#xff0c;帮助你了解为什么多个 F…

生产报工信息化全流程大讲解

在企业的生产管理中&#xff0c;生产报工是一个关键环节&#xff0c;但传统的生产报工方式存在诸多痛点&#xff0c;制约了企业的发展。随着数字化技术的发展&#xff0c;多个平台为企业提供了有效的解决方案。基于生产报工信息化方案报告》白皮书&#xff0c;本文深入探讨生产…

三菱FX3U PLC绝对定位- DRVA指令

指令格式 相关软元件一览 功能和动作 这是采用绝对驱动的单速定位指令。采用从原点(0点)开始的距离指定方式&#xff0c; 也被称为绝对驱动方式。 1、在指令执行过程中&#xff0c;即使改变操作数的内容&#xff0c;也不反映到当前的运行中。 在下次的指令驱动时才有效…

客户服务的未来趋势:智能化与人性化的融合

在当今这个日新月异的数字时代&#xff0c;企业的竞争已不再局限于产品或服务的本身&#xff0c;而是延伸到了客户体验的每一个细微之处。数字化转型作为推动这一变革的重要力量&#xff0c;正深刻改变着客户服务的面貌&#xff0c;使之变得更加智能、便捷且充满人性化。随着人…

最长回文子串-双下标动态规划

题目来源&#xff1a;Leetcode 5.最长回文子串 DP定义&#xff1a; 容易想到&#xff0c;用一个二维数字dp[i][j]来表示s[i:j]是否是回文串&#xff0c;如s“daba”。dp[1][3]1表示"aba"为回文串&#xff1b; 递归条件 想要判断字符串"aba"是否为回文…