google breakpad for linux(2)
breakpad 是什么
breakpad 是一个包含了一系列库文件和工具的开源工具包,使用它可以帮助我们在程序崩溃后进行一系列的后续处理,如现场的保存(core dump),及事后分析(重建 call stack )等,它提供了非常有效且易用的工具来帮助开发者处理程序的异常崩溃。该项目由 google 所开发维护并开源,代码托管在 google code 上。
breakpad 具有跨平台的特性,支持 window, linux, mac 三大平台,可以运行于一系列架构的 cpu 上,现在已经被广泛运用在 google 的一系列产品及其它公司的桌面程序上,如 chrome,piscal,firefox 等。 这篇文章主要介绍一下其在 linux 下结构,breakpad 主要包括三大主件:
1) client.
2) symbol dumper.
3) processor.
各个模块
(1) client module.
client 模块作为一个静态库将会与使用者的程序编译在一块。它的主要作用是在程序崩溃后,接管程序的异常处理,具体来说,它主要做了两方面的事情:
a) 响应程序崩溃时接收到的 signal,包括:SIGSEGV,SIGABRT,SIGFPE,SIGILL,SIGBUS。(另外两个 SIGSTOP 和 SIGKILL 无法处理)
b) 获取程序崩溃那一刻的运行时信息,保存为一个 mini dump 格式的文件(可以想像为一个特殊格式的 core dump)
下图描述了 client module 中几个类的关系及工作的流程:
如上,breakpad 通过提供信号处理函数来响应程序的崩溃,然后在信号处理函数中,保存程序崩溃时的现场信息,在这里有几点需要说明:
a) 程序崩溃后,整个进程空间已经处于一个不稳定的状态,在这样的不稳定状态下,再进行内存的分配,和调用动态库里的函数,都是不安全的。之所以说处在不稳定状态,主要是指如果当前的崩溃是由 SIGSEGV 引起的,那么此时程序的内存可能已经被破坏了(heap,stack,数据段),而引用动态库是需要查找一堆数据段里的表项(plt,got),这些数据很可能已经被破坏,因此没法正确加载动态库,至于不能 malloc 那是基本同理的,heap 可能已经被破坏了,malloc 内部维护的数据未必还正常,也就无法保证还能正确进行 malloc。
b) 信号处理函数会 clone 出一个新的进程,dump core 这件事情则是在这个 clone 出来的子进程中进行的,子进程通过 ptrace 来与父进程进行交互,从而读取父进程的相关信息。
c) breakpad 的 exception handling 有两种模式,一种是 in process,一种是 out of process。但在 Linux 平台上,暂时只有 in process 这种模式,in process 的实现相对简单些,流程很清楚明了,out of process 相对就复杂了。按照开发者的设计意想,是为每一个登陆 Linux 的登陆用户起一个 deamon 程序运行于后台,当该用户的其它程序崩溃后,崩溃程序通过与这个 deamon 程序进行交互,从而保存 core dump。其中,交互方面将通过 socket 按照client/server 的模式进行,但是至今为止,这种模式还未可用。
在上图中,MinidumpWriter 这个类是一个包装,提供一些与 write dump 相关的接口函数给上层的函数处理函数进行调用,真正与 linux process 相关的操作都放在 LinuxPtraceDump 这个类中进行。client 程序崩溃之后中,dump 出来的内容主要包含以下几个内容:
a) 各个线程相关的运行时信息。如 stack pointer,context,mapping 等,以一个线程为单位保存在一个数组中(list)。
b) 当前进程的各种内存映射(Mapping)
c) 用户指定的内存区域(application-provided memory regions)
d) 异常信息(crash address, signal, thread id, context)
e) 当前系统的信息 (cpu, os info)
上述的内容会以 minidump 的格式组织起来保存为一个二进制文件,minidump 格式是一种简单的流格式,由微软所设计,具体可看下图:
上图中,_MINIDUMP_DIRECTOR 就是各种流的 container,流的类型不同,它所包含的数据形式就有所不同。具体各个流的数据格式是怎样的,可以参考一下 MSDN 的说明:http://msdn.microsoft.com/en-us/library/windows/desktop/ms680394(v=vs.85).aspx
(2) symbol dumper.
这个模块主要是用来从可执行程序中提取与符号相关的信息,并保存为一种特定格式的文件。为什么要提取符号信息? 根据前面的介绍,client 模块在程序崩溃时保存了一个 core dump 文件,但这个 core dump 出于简单及实用考虑,保存的都是些二进制的数据,只通过这些数据,我们根本无法重建出可读的 call stack. 因此 symbol dumper 就是用来产生一个可与 core dum p配合起来使用的符号文件。
编译非 release 版本的程序时(如,gcc 开了-g 选项),编译器通常会将带有符号相关的信息以某种格式(DWARF,STABS)组织起来,存放在可执行文件的某个段位里。breakpad 的 symbol dumper 就是要从这些段位里提取出它认为有用的信息。
下面具体来说一下这个 symbol file:
1)symbol file 中全部内容都是ascii文本。
2)symbol file 的内容以行单位,每一行称作一条记录,每条记录中有多个字段,每个字段以空格分开。
3)每条记录的开头是一个串字符,这个字符标记这条记录是什么类型的记录。但 Line record 除外,这种类型的记录,默认省略掉标记符,也就是如果有一行没有标记类型,这一行就是一个Line record.
4)记录中有些字段是10进制或16进制的字符串,16进制也没有以0x开头,要分清某个数字具体是哪种进制,就要看这些数字是在哪种记录里,属于哪个字段,这些都是规定死了的。
记录的类型主要有以下几种:
- MODULE: 这种记录用来描述当前这个可执行文件。这条记录是 symbol file 的第一条记录。
- FILE: 这种记录用来记录源文件,包含有文件名及路径信息。这个类型的记录会被分配一个整形符号来作标记,然后在别的记录中可能会引用它。
- FUNC: 这种记录用来描述一个函数,包含函数名,函数在可执行文件中的地址等信息。
- 行记录: 这种记录用来描述,一个给定范围的机器指令对应哪个源文件的哪一行。行记录总是跟在FUNC记录后面,从而描述每个函数里的指令对应在源码里的位置。
- PUBLIC: 这种记录用来描述每一个链接符号的地址,如汇编函数里的各个入口点。
- STACK WIN: 这种记录用来描述函数调用时,函数帧(stack frame)的布局。有了这个记录,给定一特定的函数帧 F,就可以找到哪个函数帧调用了F。
- STACK CFI: CFI,就是 Call Frame Info,这种记录用来表述当执行到某条指令的时候,怎样去查看当前的函数调用栈。
上面主要讲了 symbol file 中的内容是怎样组织的,这里并不管其中的 symbol 是来自 DWARF,还是 STABS,这也正是 breakpad 定制自己的 symbol 格式的意义所在。
(3) processor module.
前面所介绍的两个模块,分别输出了 coredump,及 symbol file。这里要介绍的 processor 模块,它的作用就是根据 coredump 及 symbol file,构建出可读的 call stack. stackwalking 从 MinidumpProcessor 这个类开始,入口函数 MinidumpProcessor::Process() 以 symbol file,minidump file 为参数。
需要说明的是,stack walking 是针对每一个线程进行的。minidump 中保存了每个线程运行时的相关信息,这些信息都会在 Process()函数中被提取出来。MinidumpMemoryRegion 包含着线程的调用栈,MinidumpContext 包含着线程的 cpu context。stackwalking 开始时,Stackwalker::Walk() 根据不同的cpu,构建出当前线程的 top frame,也就是函数调用的最顶一层。然后从 top frame 开始,对整个调用栈的栈帧进行解析,解析的过程,包含有几方面的内容:
(a) 查找模块
根据当前帧的 eip(x86) 来调用 CodeModules::GetModuleForAddress() 返回当前 frame 所属的模块信息。
(b) 定位符号
前面找到模块后,找到只是二进制相关的信息。要找到这个模块相应的名字及模块里其它函数,变量的名字等,还需要用到之前 symbole file.这里需要注意的是,symbol file 可能不止一个,因此需要能够根据当前的模块来定位到,与这个模块相关的 symbol file 是哪个。SimpleSymbolSupplier 这个类就是用来做这个事情,它会结合当前模块的信息,定位到与当前 module 相关的 symbol file。
(c) 查找符号
前面一步找到了 symbol file。这里就需要根据 symbol file 来输出具体的符号。SourceLineResolverInterface 这个类的 LoadModuleUsingMemoryBuffer() 用来把 symbol file 加载进内存,并解析。BasicSourceLineResolver 这个类则是提供对外的接口,用于根据某个地址,查找出对应的符号名字,如,输入一个函数地址,返回函数的名字。
(d) 查找出当前帧的调用帧
当前帧解析完后,需要继续去解析调用当前帧的父帧。要做到这件事情,必须要有 symbol file 的支持。回忆一下,symbol file 中有二种记录类型:stack win,stack cfi。这两种类型的记录完整的描述了各类函数调用的栈帧布局,因此借助这些记录理论上就可以找回当前帧的调用帧。SourceLineResolverInterface就是用来做这些事情。具体可以查看它的成员函数,FindWindowsFrameInfo() 及FindCFIFrameInfo()。
总结
工欲善其事必先利其器,对程序开发来说,尤其如此,好的工具常常能对我们的工作起到事半工倍的作用,而对于工具的使用我们不应仅仅满足于知道怎么用,知其然也要能知其所以然,学习和分析别人的工具是怎么做出来的,不仅能帮助我们更好地理解和使用这些工具,更重要的是能帮助我们开阔视野和增长知识。前文对 breakpad 在 linux 平台下的实现做了简单介绍,从中我们可以看出一个完善的工具链实现起来是一项浩大的工程,涉及到许多方方面面的知识,里面可以学习的东西很多,需要完善的东西也很多,breakpad 作为一个开源项目,现在仍处在开发和完善的过程中,回馈开源的最好方式就是加入其中贡献你的力量,希望本文能对有兴趣的读者有帮助。
google breakpad for linux(2)的更多相关文章
- C++库(Google Breakpad)
Google Breakpad是什么? 一个开源的多平台崩溃报告系统. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solari ...
- QT中使用google breakpad捕获程序崩溃异常
今天给大家介绍一个在linux下如何捕获程序崩溃异常的方法 一.google breakpad源码的下载和编译 1.https://github.com/google/breakpad.git,源码地 ...
- Google BreakPad使用集
Google Breakpad 学习笔记 - 简书 Qt中使用Google Breakpad捕获程序崩溃异常_Linux编程_Linux公社-Linux系统门户网站
- Google Breakpad 完全解析(一) —— Windows入门篇
原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,他支持Windows,Lin ...
- Google Breakpad 之一,跨平台crash 处理上报系统简介
Google Breakpad 之一,跨平台crash 处理上报系统简介 http://blog.csdn.net/wpc320/article/details/8290501 Google Brea ...
- Google Breakpad: 实战crash .
Google Breakpad: 实战crash . http://blog.csdn.net/zm_21/article/details/24795205 C/C++程序最棘手的时候就是一个字“挂” ...
- google breakpad 使用初步总结
项目地址:https://code.google.com/p/google-breakpad/ 访问不了请挂VPN 这是一个由google主导的开源项目,官方介绍为:An open-source ...
- an introduction of google breakPad for android
一.背景 众所周知,Android JNI层的Crash问题是个比较头疼的问题.相对Java层来说,由于c/c++造成的crash没有输出如同 Java的Exception Strace,所以cras ...
- Google Breakpad · 基础介绍
Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合. 三个主要组件 ◆ client 以library的形式内置在你的应用中,当崩溃发生时写 minidump文件 ◆ symbo ...
随机推荐
- redis集群中的主从复制架构(3主3从)
架构图如下 首先开启6个实例,这里为了演示方便,只是在一个linux上开启了6个redis实例 6380 (主) 6480 (从) 6381(主) 6481(从) 6382(主) 6482 ...
- 在RedHat 和 Ubuntu 中配置 Delphi 的Linux开发环境(转)
原文地址:http://chapmanworld.com/2016/12/29/configure-delphi-and-redhat-or-ubuntu-for-linux-development/ ...
- clion配置c/c++环境
打开这个界面 点击添加Cygwin选择下载的Cygwin在进行下面的配置 去网站https://www.cygwin.com/选择路径即可(这里只写了配置过程中的关键步骤并且附上IDE的链接直接安装 ...
- HMM(隐马尔可夫模型)不断学习中
HMM(隐马尔可夫模型)是用来描述隐含未知参数的统计模型,举一个经典的例子:一个东京的朋友每天根据天气{下雨,天晴}决定当天的活动{公园散步,购物,清理房间}中的一种,我每天只能在twitter上看到 ...
- Educational Codeforces Round 60 C 思维 + 二分
https://codeforces.com/contest/1117/problem/C 题意 在一个二维坐标轴上给你一个起点一个终点(x,y<=1e9),然后给你一串字符串代表每一秒的风向, ...
- 在运行bat文件时,报错发生系统错误123,文件名,目录名或卷标语法不正确
报错:发生系统错误123,文件名,目录名或卷标语法不正确 这个错误就相当于你在dos命令行中输入一个命令,报错“不是内部或外部命令,也不是可运行的程序”.此时你可以在系统环境变量中检查是否配置了这个命 ...
- cacti+CentOS6.5
系统版本:CentOS6.5 软件版本:cacti-0.88f 需要预安装的软件有以下几种,可以通过yum安装全部 yum -y install net-snmp* yum -y install op ...
- c# 动态数组-----“动态”数组
其实在大多数工作中我们能通过前处理来确定我们的数组有多大,这样我们就可以声明相应大小的数组了.我感觉这种“动态”数组就够我用了.比如我要处理excel中数据,数据有m行*n列,这样我就可以通过读取ex ...
- for循环添加的闭包问题
function test(){ var arr = []; for (var i=0;i;i++){ arr[i] = function(){ console.log(i); } } return ...
- 20155205 《Java程序设计》实验一(Java开发环境的熟悉)实验报告
20155205 <Java程序设计>实验一(Java开发环境的熟悉)实验报告 一.实验内容及步骤 (一)使用JDK编译.运行简单的java程序 命令行下的程序开发 步骤一:打开mac下的 ...