windows下捕获dump之Google breakpad_client的理解

 

  breakpad是Google开源的一套跨平台工具,用于dump的处理。很全的一套东西,我这里只简单涉及breakpad客户端,不涉及纯文本符号生成,不涉及dump解析。

一、使用

  最简单的是使用进程内dump捕获,使用者只需要跟ExceptionHandler打交道,在自己的程序里定义一个ExceptionHandler对象,ExceptionHandler会挂上异常处理、CRT参数错误处理、purecall错误处理,当发生crash时,breakpad会写好dump,然后回调通知使用者。进程内dump并不推荐,但也不算太差,它在程序启动时就开启了一个“Handler thread”,等到有crash,触发该线程去写dump,写完回调使用者,从google的久未更新的ClientDesign文档可以猜到以前是只有进程内写dump的,它已经符合了让dump尽可能真实而设置下的规定。以前所在团队在chromium上做二次开发,使用的是进程内dump,没发现有问题。现在我安装的chrome浏览器,没发现有crash_server进程,估计要么是没抓dump,要么是进程内dump,我看到有文章说有一个GoogleCrashHandler.exe进程,但我这里没有发现,可能是后来修改掉了吧,之前我还一直以为是对crash_server.exe重命名了。

进程外写dump,使用者一样要定义一个ExceptionHandler对象,这对象有管道名称。另外还需要写一个server进程,server进程负责:写dump、上传dump,当客户进程发生crash时,只需要通过Event置位通知服务进程。server进程只需要定义一个breakpad提供的CrashGenerationServer类对象。客户进程和服务进程是通过管道通信的,通信可以只发生在客户进程初始化阶段,server进程要先于客户进程启动,否则客户进程就会因为管道连接不上而使用进程内dump捕获。

  进程内、外dump捕获,都是异步而阻塞的,异步具体是说,进程内dump会让写dump、回调通知使用者写dump完成在另一个安全的线程中做;进程外dump会让写dump在另一个进程中做、回调通知写dump完成在crash线程中做、dump上传可以放到另一个进程中做。阻塞具体是说,虽然发生crash的线程把dump相关的工作扔给别人做了,但是它会等待别人的工作做完才继续完下走。

二、内部实现 

  ExceptionHandler部分。

当使用进程内dump时,会有一个handler thread,该线程启动之后,等待semaphore触发写dump行为,进程外dump则没有该线程。另外,异常处理初始化是在ExceptionHandler对象构造中做的,如果没有进程外dump的需求,那么只需要ExceptionHandler就可以搞定,不需要CrashGenerationClient 和 CrashGenerationServer。

     生成dump的流程在ExceptionHandler里面跑的很杂,还提供了非崩溃产生dump的接口。画个流程图出来(流程图只是函数的罗列,块与块之间可能存在包含关系):
     CrashGenerationClient 和 CrashGenerationServer。
     这两个类主要用于进程间通信,包括:管道数据传输、Event通知。跟ExceptionHandler有关联的就是CrashGenerationClient,这里的client可以当作管道的client来理解。ExceptionHandler已经做了异常处理初始化,而进程外dump的生成是另外的进程做的,所以CrashGenerationClient,并不涉及异常、dump生成,它只是为了进程间通信而存在。

以CrashGenerationClient为主线来介绍客户进程服务进程间的交互。CrashGenerationClient对外提供Register、RequestDump、RequestUpload。
1、Register
     1-1 客户进程连接上服务进程:连接上管道,设置管道状态
     1-2 客户进程向服务进程注册:通过TransactNamedPipe,将客户进程的信息传递给服务进程,也从服务进程读取到数据。
         客户进程传递的数据包括:服务进程ID、dump类型、crash线程id的地址、EXCEPTION_POINTERS指针的地址、参数异常和纯虚函数异常的断言信息地址、客户进程信息。服务进程会监控客户进程的退出。
         客户进程接收的数据包括:客户进程用于触发生成dump的Event Handle;客户进程用于监听的dump生成完毕Event Handle;客户进程用于监听的服务进程活着Mutex handle;服务进程进程id(后面没用上)。
         这里Handle,都是服务进程通过DuplicateHandle API 复制到客户进程的,为什么不使用命名Handle呢?这样子两个进程只需要约定名字就好,不需要DuplicateHandle,然后再通过管道传递。我的理解:命名Handle之所以不用,是因为服务进程要为多个客户进程提供dump服务,如果使用命名Handle,那么命名会是一个很麻烦的事情,再则,约定名字也增加了耦合,breakpad现在的做法使得客户和服务的耦合只有管道名、管道数据协议。
     客户进程在TransactNamePipe函数执行完毕之后,再执行了一次WriteFile操作,发送MESSAGE_TAG_REGISTRATION_ACK消息给服务进程,服务进程收到该ACK消息时,关闭跟客户进程的连接。服务进程对管道的操作顺序为:读-->写-->读,第二次读是客户进程通知服务进程关闭管道。
2、RequestDump
     当发生crash时,或者是在非crash状态下要求生成dump,就需要调用RequestDump函数。客户进程触发之前从服务进程收到的崩溃Event Handle,等待dump完成Event和服务进程Event处于触发状态,release下最多等15秒,debug下无限等待,异步阻塞,为了不让现场被破坏。
     服务进程通过RegisterWaitForSingleObject注册了崩溃Event Handle的回调函数。回调函数里做了这么几件事情:(1)、通过ReadProcessMemory读取客户进程的信息;(2)、生成dump;(3)、触发dump生成事件,通知客户进程,复位触发dump事件。
3、RequestUpload
     首先客户进程连接上服务进程,接着客户进程写MESSAGE_TAG_UPLOAD_REQUEST消息到管道,写完关闭管道。
     服务进程读取数据,发现是MESSAGE_TAG_UPLOAD_REQUEST消息,则调用创建服务进程时注册的上传回调函数。注意到chromium并没有使用这个通道,而是在服务进程生成dump的回调里做了dump上传操作,这样子似乎更好,减少了crash时两进程的沟通。客户进程、服务进程、breakpad客户端之间的联系图:

可以看到breakpad客户端主要包含了CrashGenerationServer\ExceptionHandler\CrashGenerationClient三部分,另外有dump上传未画出。

三、从代码中学到的

学习breakpad_client的代码,不是为了在工作上使用,以前的、现在的团队都已经有成熟的dump捕获、dump分析工具。学习它,是为了体会它的优点和缺点。

   breakpad_client的层次划分很好,使用者不需要知道进程间通信的存在,通过回调实现层次间的通知。(这种比较简单,一般人都可以做到。)

crash之后崩溃线程尽可能少的操作,在客户进程初始化时就把崩溃时服务进程需要用的全局数据的地址通知服务进程,崩溃时,只需要触发Event。(我之前的做法是在crash的时候再把崩溃信息通知服务进程,现在看来是不合理的。)

API的使用。RegisterWaitForSingleObject的使用,这个API是我之前没用过的,非常方便,直到前阵子才通过QueueUserWorkItem API(chromium通过它异步上传dump)知道windows有自带线程池的存在;进程间通信对管道+Event的善用;dump生成的各种处理,不仅仅是MiniDumpWriteDump。(这可以说是我知识面不广带来的惊喜。)

  阅读ClientDesign文档,虽然文档可能老了,但引导我明白了为什么进程内dump会导致现场破坏,最直接的理解是:因为堆坏了导致的崩溃,这时候异常处理函数里又干了堆内存分配的事情,那肯定就又继续crash。

  breakpad_client对使用者的通知是用回调函数做的,回调函数是在对象初始化时传递的函数指针,有一个函数有三个回调函数指针(客户进程连接、客户进程崩溃、客户进程要求上传dump), 我更喜欢用抽象类指针,这样子只需要一个指针就够了,参数不需要那么多,而且代码更像C++。(这是目前唯一能想到的不喜欢。)

四、资料推荐

http://code.google.com/p/google-breakpad/wiki

五、杂
 (1)、获取breakpad_client 代码及其demo。
     breakpad代码所在svn:http://google-breakpad.googlecode.com/svn/trunk 
     生成sln:D:\breakpad\src\tools\gyp>gyp.bat --no-circular-check "../../client/windows/breakpad_client.gyp"
     需要先安装python,使用2.7.4版本python正常生成sln文件,2.4.3、3.3.2版本均生成失败。搜索发现,\src\client\windows\build\common.gypi文件下有 'python_ver%': '2.5',,不确定是否要依据它来确定python使用的版本。因为我是在可以编译chromium的环境下使用breakpad_client的,所以不需要对编译环境做处理。 
 (2)、chromium在breakpad_client的使用上,做了些值得看的事情。譬如:log、crash之后的处理...
 (3)、相比之下,之前分析的windows下捕获dump,真是简单到极点的入门级demo。
 (4)、breakpad服务端dump处理的开源代码:https://github.com/mozilla/socorro(我没看,不确定是否靠谱)
 (5)、breakpad官网的文档估计比较旧了,但是思路还是正确的。符号文件部分没有看,因为比较深,而我目前又没有深究它的需要,看breakpad里有pdb文件生成为纯文本格式后的样子,估计是为了统一各个平台吧。(又是一个《纯文本的威力》?)
  
 
 
标签: windowsdump

breakpad是Google开源的一套跨平台工具的更多相关文章

  1. 【Protocol Buffers】grpc默认使用的Google 开源的一套成熟的结构数据序列化机制

    grpc默认使用的Google 开源的一套成熟的结构数据序列化机制 参考地址:https://blog.csdn.net/shensky711/article/details/69696392 参考地 ...

  2. google开源的C++性能分析工具 - gperftools

    gperftools是Google提供的一套工具,其中的一个功能是CPU profiler,用于分析程序性能,找到程序的性能瓶颈. 安装 gperftools:http://code.google.c ...

  3. (视频) 开源,免费和跨平台 - MVP ComCamp 2015 KEYNOTE

    2015年1月31日,作为KEYNOTE演讲嘉宾,我和来自全国各地的开发人员分享了作为一名MVP的一些体会. Keynote – Open Source, Free Tools and Cross P ...

  4. 转:玩转Google开源C++单元测试框架Google Test系列

    转自http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Google ...

  5. 玩转Google开源C++单元测试框架Google Test系列(转载)

    越来越多公司采用敏捷开发,单元和回归测试越来越重要,GTest作为最佳C++单元测试工具越来越多的被使用.转自 http://www.cnblogs.com/coderzh/archive/2009/ ...

  6. 开源,免费和跨平台 - MVP ComCamp 2015 KEYNOTE

    2015年1月31日,作为KEYNOTE演讲嘉宾,我和来自全国各地的开发人员分享了作为一名MVP的一些体会. Keynote – Open Source, Free Tools and Cross P ...

  7. 玩转Google开源C++单元测试框架Google Test系列(gtest)(转)

    转自:http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Googl ...

  8. [转]Google开源Leak Finder—用于检测内存泄漏的JavaScript工具-----可惜,暂时打不开google的网站,下载不了

    近日,Google开源了Leak Finder,这款工具可以查看JavaScript应用的堆,进而发现内存泄漏. 作为一门垃圾收集语言,JavaScript并不会出现常见的内存泄露情况,特别是像C++ ...

  9. 玩转Google开源C++单元测试框架Google Test系列(gtest)(总)

    原文地址:http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html 前段时间学习和了解了下Google的开源C++单元测试框架Goo ...

随机推荐

  1. 从头学起android<AutoCompleteTextView文章提示文本框.十九.>

    文章提示可以很好的帮助用户输入信息,以方便.在Android它也设置有类似特征,而要实现这个功能需要依靠android.widget.AutoCompleteTextView完毕,此类的继承结构例如以 ...

  2. C---数组名作函数参数

    数组名可以作函数的实参和形参.如: #include<stdio.h> int main(void) { ]; f(array,); } f(int arr[],int n) { } ar ...

  3. MVC创建XML,并实现增删改

    原文:MVC创建XML,并实现增删改 如果创建如下的XML: <?xml version="1.0" encoding="utf-8" standalon ...

  4. JAVA基本的编程50称号(7-9称号)详细解释

    一个.叙述性说明 1.输入一行字符.在这些信件统计.空格.出现频率的数字和其他字符的总数和每一个字符.         程序分析:使用String类的matchs()分别统计符合正則表達式的每类字符的 ...

  5. CORS(跨域资源共享)跨域问题及解决

    当使用ajax跨域请求时,浏览器报错:XmlHttpRequest error: Origin null is not allowed by Access-Control-Allow-Origin.肯 ...

  6. Warning: Cannot modify header information - headers already sent by (output started at

    一般来说在header函数前不能输出html内容,类似的还有setcookie() 和 session 函数,这些函数需要在输出流中增加消息头部信息.如果在header()执行之前有echo等语句,当 ...

  7. 轻型ORM--Dapper

    分享一个轻型ORM--Dapper选用理由 推荐理由:Dapper只有一个代码文件,完全开源,你可以放在项目里的任何位置,来实现数据到对象的ORM操作,体积小速度快:) Google Code下载地址 ...

  8. 1.1 什么是LinQ

    如今,软件应用环境越来越多样化,软件需要处理的数据量也日渐庞大,数据之间的关系日渐复杂.从而带动了存储技术的不断发展,越来越多的数据存储格式被应用到各种软件中. 通常,针对数据的查询是用简单的字符串文 ...

  9. 《Visual Studio Magazine》2013年读者选择奖—软件类

    <Visual Studio Magazine>会在每年的下半年向读者发出投票邀请,读者将在28个大类,超过500个开发工具的名单中选出他们认为最好的产品,以票数评出各分类的金.银.铜奖. ...

  10. CodeIgniter学习一:基础知识

    1. url片段(CI域名组成说明)        example.com/index.php/test/index   第一部分(test):控制器 第二部分(index):方法,动作 如果第二部分 ...