Google Breakpad 完全解析(二) —— Windows前台实现篇
原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢。
好,看完了如何使用breakpad,我们现在看看breakpad在Windows下到底是如何实现的呢?
代码结构
在我们来看breakpad是如何实现其强大的功能之前,我们先来看一下他的代码结构吧。
Google breakpad的源代码都在src的目录下,他分为如下几个文件夹:
client:这下面包含了前台应用程序中捕捉dump的部分代码,里面按照平台分成各个子文件夹
common:前台后台都会用到的部分基础代码,字符串转换,内存读写,md5神马的
google_breakpad:breakpad中公共的头文件
processor:用于在后台处理崩溃的核心代码
testing:测试工程
third_party:第三方库
tools:一些小工具,用于处理dump文件和符号表
我们先来看Windows下前台实现的部分,也就是client文件夹下的代码。
breakpad的崩溃捕获机制
在Windows下捕获崩溃,大家很容易会想到那个捕获结构化异常的Api:SetUnhandledExceptionFilter。
breakpad中也使用了这个Api来实现的崩溃捕获,另外,breakpad还捕获了另外两种C++运行库提供的崩溃,一种是使用_set_purecall_handler捕获纯虚函数调用产生的崩溃,还有一种是使用_set_invalid_parameter_handler捕获错误的参数调用产生的崩溃。
1
2
3
4
5
6
7
8
9
10
|
if (handler_types & HANDLER_EXCEPTION) previous_filter_ = SetUnhandledExceptionFilter(HandleException); #if _MSC_VER >= 1400 // MSVC 2005/8 if (handler_types & HANDLER_INVALID_PARAMETER) previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); #endif // _MSC_VER >= 1400 if (handler_types & HANDLER_PURECALL) previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); |
另外由于C++运行库提供的崩溃回调中,并不会提供当前的线程现场和崩溃信息,所以breakpad会自己生成好这些信息,然后请求生成dump。
这里值得一说的是,在非异常崩溃处理中,breakpad获取线程现场使用的函数是RtlCaptureContext而不是GetThreadContext。
RtlCaptureContext只能捕获当前线程的现场,而GetThreadContext可以捕获任意线程的现场,只要有这个线程的句柄即可。
但是GetThreadContext有两个不好的地方:不能获取当前线程的现场;获取现场前必须先用SuspendThread暂停目标线程。
而RtlCaptureContext虽然只能获取当前线程的现场,但是调用他时可以不用暂停线程的运行。
对于breakpad来说,崩溃发生后越早获取现场就越好,所以breakpad使用RtlCaptureContext函数作为他的线程获取函数。
breakpad中的C/S结构
由于breakpad是在进程外抓取dump,所以breakpad需要实现一个C/S结构来处理崩溃进程抓取dump的请求。
1. breakpad跨进程通信的实现
breakpad中使用了命名管道来实现IPC。
在客户端,初始化ExceptionHandler的时候,如果指定了PipeName,也就表示此时需要使用进程外的dump抓取,ExceptionHandler,会建立一个 CrashGenerationClient的对象,由这个对象连接服务端,将自己注册到服务端上去。
大家可以参看exception_handler.cc中的ExceptionHandler::Initialize函数。
在服务端,初始化CrashGenerationServer的时候,就会建立一个命名管道,并等待客户端来连接。一旦有客户端连接上来,服务端会为每一个客户端生成一个ClientInfo的对象,之后用这个对象来管理所有的客户端,一旦有崩溃发生,服务端都会从这个对象中取出dump所需要的信息。
大家可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数。
2. breakpad捕获崩溃生成dump的流程
breakpad进程外生成dump的流程大概如下:
google-breakpad-out-of-process-dump:
这段流程的代码就是crash_generation_client.cc和crash_generation_server.cc。
有两个简单的问题,这里说明一下,高手们就请直接忽略吧,咩哈哈:
在服务端如何为客户端生成事件句柄?
使用DuplicateHandle,即可把任意一个内核对象的句柄复制到其他进程,并且可以指定产生的句柄的权限。
如何异步的等待一个事件?
使用RegisterWaitForSingleObject,即可异步的等待一个事件,当事件发生的时候,就可以回调到一个指定的回调函数中,但是要注意的是,RegisterWaitForSingleObject会在一个新的线程中来等待这个事件,此处很容易产生多线程的调用,需要注意线程问题。
3. 服务端关键数据结构:ClientInfo
ClientInfo是服务端中最重要的数据结构,服务端通过它来管理所有的客户端。客户端注册时,会保存或生成里面所有的信息,在客户端请求生成dump的时候,服务端就会通过ClientInfo获取所有客户端的信息。ClientInfo中保存了如下信息:
- 客户端进程pid和句柄
- 生成Minidump的类型
- 自定义的客户端信息
- 客户端崩溃的线程ID
- 客户端崩溃的信息
- 客户端请求崩溃所使用的事件句柄
这里有一个问题:在客户端发生崩溃时,服务器如何通过ClientInfo获取到客户端的崩溃信息呢?
客户端中有几个用于保存崩溃信息的变量,在注册时,客户端会将这几个变量的地址发送至服务端,服务端将其保存在ClientInfo中,然后当崩溃发生的时候,服务端就可以通过ReadProcessMemory读取客户端中的信息,从而生成dump。这样做就避免了每次发生崩溃,都要通过Pipe将崩溃信息传递到服务端中去了。
这些变量分别是:崩溃的线程ID,EXCEPTION_POINTERS和MDRawAssertionInfo。
EXCEPTION_POINTERS和MDRawAssertionInfo的区别在于,异常崩溃的信息会被写入EXCEPTION_POINTERS,非异常崩溃(非法参数和纯虚函数调用)的信息会被写入MDRawAssertionInfo中。
dump文件的上传
在breakpad的工程中,有一个工程叫做:crash_report_sender,里面是一个上传崩溃文件的类,他的实现很简单,他使用Windows Internet Api来完成dump文件的上传。
在使用crash_report_sender时,可以为其指定一个checkpoint_file。
1
|
explicit CrashReportSender( const wstring &checkpoint_file); |
这个文件只有一个作用,就是用来保存上次上传崩溃的时间和今天上传过的崩溃的次数。通过这个文件,我们就可以来设置每日上传的崩溃的最大数量。
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
|
CrashReportSender::CrashReportSender( const wstring &checkpoint_file) : checkpoint_file_(checkpoint_file), max_reports_per_day_(-1), last_sent_date_(-1), reports_sent_(0) { FILE *fd; if (OpenCheckpointFile(L "r" , &fd) == 0) { ReadCheckpoint(fd); fclose (fd); } } ReportResult CrashReportSender::SendCrashReport( const wstring &url, const map<wstring, wstring> ¶meters, const wstring &dump_file_name, wstring *report_code) { int today = GetCurrentDate(); if (today == last_sent_date_ && max_reports_per_day_ != -1 && reports_sent_ >= max_reports_per_day_) { return RESULT_THROTTLED; } // 上传文件部分代码,省略 } |
调整每日上传崩溃的最大数量的函数是set_max_reports_per_day。
需要注意的是:在上传dump文件的时候,crash_report_sender并不会对dump文件进行分析,而是直接上传整个dump文件,如果你需要上传的dump文件非常大的话,可以考虑把崩溃分析处理的逻辑放入前台,通过去重或者直接上传分析结果,减少上传的文件大小。
breakpad存在的问题
进程外生成dump有很多好处,其中最大的好处就是不会被崩溃进程影响,这样dump的过程就不容易出错,但是这样也有一定的弊端。
1. 部分崩溃无法抓取
在一些极端的崩溃,如堆栈溢出之类的崩溃,进程外抓取dump有时候会失败。
2. 无法抓取死锁或者其他原因导致的进程僵死
breakpad现在没有检测进程死锁的代码,也没有在服务端控制客户端请求dump的代码,所以现在breakpad无法抓取死锁等进程僵死的问题。不过因为breakpad的定位是处理崩溃,如果有这种需要的童鞋,可以自行修改breakpad的代码,添加这些功能。
3. 对服务端有依赖
如果指定了在使用进程外抓取dump,breakpad对服务端就有依赖。主要体现在抓取dump时,如果服务端不存在,客户端将无法正常抓取dump,甚至有时会出现阻塞。
当然对于这些问题,随着breakpad的发展肯定会越来越完善。如果,你遇到了了这些问题,而又绕过不了,那就改代码,并且提交给breakpad吧,开源项目就是这么发展的。
好,到此breakpad的Windows实现就已经说完了,如果有神马问题,还请多多指教。谢谢大家。
Google Breakpad 完全解析(二) —— Windows前台实现篇的更多相关文章
- Google Breakpad 完全解析(一) —— Windows入门篇
原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,他支持Windows,Lin ...
- Java 面试知识点解析(二)——高并发编程篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- C++库(Google Breakpad)
Google Breakpad是什么? 一个开源的多平台崩溃报告系统. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solari ...
- Google Breakpad 之一,跨平台crash 处理上报系统简介
Google Breakpad 之一,跨平台crash 处理上报系统简介 http://blog.csdn.net/wpc320/article/details/8290501 Google Brea ...
- google breakpad 使用初步总结
项目地址:https://code.google.com/p/google-breakpad/ 访问不了请挂VPN 这是一个由google主导的开源项目,官方介绍为:An open-source ...
- 使用Google提供的ZXing Core,Java生成、解析二维码
1.maven项目中,pom.xml中引入ZXing Core工具包: <!-- https://mvnrepository.com/artifact/com.google.zxing/core ...
- 在云平台上基于Go语言+Google图表API提供二维码生成应用
二维码能够说已经深深的融入了我们的生活其中.到处可见它的身影:但通常我们都是去扫二维码, 曾经我们分享给朋友一个网址直接把Url发过去,如今我们能够把自己的信息生成二维码再分享给他人. 这里就分享一下 ...
- Google Breakpad · 基础介绍
Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合. 三个主要组件 ◆ client 以library的形式内置在你的应用中,当崩溃发生时写 minidump文件 ◆ symbo ...
- ZXing 生成、解析二维码图片的小示例
概述 ZXing 是一个开源 Java 类库用于解析多种格式的 1D/2D 条形码.目标是能够对QR编码.Data Matrix.UPC的1D条形码进行解码. 其提供了多种平台下的客户端包括:J2ME ...
随机推荐
- python初学--文件操作、字典
文件读写 1.先打开文件 2.读取/写入内容 3.保存文件 文件的open模式有三种 1.w 写模式,它是不能读的 只要用w打开文件,文件中的东西都会被清空 w+, 写读模式,只要沾上w 就会清空 ...
- Can't load standard profile: GRAY.pf
报错: java.lang.IllegalArgumentException: Can't load standard profile: GRAY.pf at java.awt.color.ICC_P ...
- linux下查看机器配置
查看cpu信息:lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): ...
- 判断ios当前的sdk版本的方法
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0 // 当前支持的sdk版本是否低于6.0 //ios 6.0以下的处理 #else //io ...
- android之 Activity跳转出现闪屏
原文:http://blog.csdn.net/az313/article/details/17321549 同一个应用,在不同手机上测试,Activity之间跳转出现闪屏,界面来回跳转…… 查阅网上 ...
- 远程连接服务器上的MySQL
crt.navicat.Linux系统.MySQL 远程连接上Linux系统,确保Linux系统已经安装上了MySQL数据库.登陆数据库.mysql -uroot -p(密码). 创建用户用来远程连接 ...
- ubuntu wine 使用
运行程序 wine xxx.exe 图形界面程序(普通程序):直接使用 wine 命令行的DOS程序:wineconsole 代替 wine.这才是正常的运行方式.不使用wineconsole运行命令 ...
- List 集合中 均匀的取七个点 的值
场景: 一个未知 长度的 List 集合,可能 长度为7,10,50,100, 等等 这个时候 ,我们需要在 集合中 均匀的取七个点: 思路: n=6; int size = list.Size(); ...
- 剑指offer-二叉查找树的第 K 个结点
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * Tre ...
- [hdu-3007]Buried memory 最小覆盖圆
大致题意: 平面上有n个点,求一个最小的圆覆盖住所有点 最小覆盖圆裸题 学习了一波最小覆盖圆算法 #include<cstdio> #include<iostream> #in ...