0. Google Breakpad介绍

Breakpad是一个库和工具套件,可以让你发布的应用程序(把编译器提供的调试信息剥离掉的)给用户,记录了崩溃紧凑的“dump”文件,发送回您的服务器,并从这些minidump产生C和C++堆栈踪迹。Breakpad可以根据请求使没有崩溃的程序也可以写出minidump。


Breakpad有三个主要组件:
客户端是一个库,包含在应用程序中。 它可以获取当前线程的状态和当前加载的可执行文件和共享库的ID写转储文件。您可以配置客户端发生了崩溃时写入一个minidump时,或明确要求时。
符号卸载器是一个程序,读取由编译器产生的调试信息,并生成一个使用Breakpad格式的符号文件 。
处理器(minidump processor)是一个程序,读取一个minidump文件,找到相应的版本的符号文件的(可执行文件和共享库的转储提到的),并产生了一个人可读的C / C + +堆栈跟踪。

小型转储文件格式(即minidump)

1.进程内抓取Dump文件

在Windows下使用breakpad的方法很简单,只需要创建一个ExceptionHandler的类即可

进程内抓取Dump文件是最简单的breakpad的用法。使用方法很简单:

const std::wstring s_strCrashDir = L"c:\dumps";bool InitBreakpad()
{   google_breakpad::ExceptionHandler *pCrashHandler = new google_breakpad::ExceptionHandler(s_strCrashDir,onExceptionFilter,onMinidumpDumped,NULL,google_breakpad::ExceptionHandler::HANDLER_ALL,MiniDumpNormal,       NULL,        NULL);    if(pCrashHandler == NULL) return false;return true;
}

2. 进程外抓取Dump文件

使用进程外抓取Dump时,需要指定服务端和客户端,在服务端中需要创建CrashGenerationServer的实例,而在客户端中则只需要创建ExceptionHandler即可。此外,如果服务端自己需要抓进程内的Dump,请将pipe的参数置为NULL。

const wchar_t s_pPipeName[] = L"\\.\pipe\breakpad\crash_handler_server";
const std::wstring s_strCrashDir = L "c:\dumps";bool InitBreakpad()
{   google_breakpad::CrashGenerationServer *pCrashServer = new google_breakpad::CrashGenerationServer(s_pPipeName,NULL,onClientConnected,NULL,onClientDumpRequest,NULL,onClientExited,NULL,true,&s_strCrashDir);if(pCrashServer == NULL) return false;   // 如果已经服务端已经启动了,此处启动会失败if(!pCrashServer->Start()) {delete pCrashServer;pCrashServer = NULL;}google_breakpad::ExceptionHandler *pCrashHandler = new google_breakpad::ExceptionHandler(s_strCrashDir,onExceptionFilter,onMinidumpDumped,NULL,google_breakpad::ExceptionHandler::HANDLER_ALL,MiniDumpNormal,(pCrashServer == NULL) ? s_pPipeName : NULL, // 如果是服务端,则直接使用进程内dumpNULL);if(pCrashHandler == NULL)return false;return true;
}

3. 注意事项

使用breakpad的时候,有两个地方需要注意:
① 记得把breakpad的solution下的几个工程,包含到你开发的工程中,或者直接包含他们的lib。
common:基础功能,包含一个对GUID的封装和http上传的类。
exception_handler:用来捕获崩溃的类。
crash_generation_server:breakpad的服务端,用来在产生崩溃时抓取dump。
crash_generation_client:breakpad的客户端,用来捕获当前进程的崩溃。

② 在初始化breakpad之前,记得先创建好dump文件的目录,不然breakpad服务端将不能正常的写dump,这会导致breakpad客户端在崩溃时无限等待服务端dump写完的消息,最后失去响应。

4. breakpad介绍

看完了如何使用breakpad,我们现在看看breakpad在Windows下到底是如何实现的呢?

breakpad代码结构:
Google breakpad的源代码都在src的目录下,分为如下几个文件夹:
client:这下面包含了前台应用程序中捕捉dump的部分代码,里面按照平台分成各个子文件夹
common:前台后台都会用到的部分基础代码,字符串转换,内存读写,md5等
google_breakpad:breakpad中公共的头文件
processor:用于在后台处理崩溃的核心代码
testing:测试工程
third_party:第三方库
tools:一些小工具,用于处理dump文件和符号表

breakpad的崩溃捕获机制
在Windows下捕获崩溃,大家很容易会想到那个捕获结构化异常的Api:SetUnhandledExceptionFilter

breakpad中也使用了这个API来实现的崩溃捕获,另外,breakpad还捕获了另外两种C++运行库提供的崩溃,一种是使用_set_purecall_handler捕获纯虚函数调用产生的崩溃,还有一种是使用_set_invalid_parameter_handler捕获错误的参数调用产生的崩溃。

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函数作为他的线程获取函数。

5. breakpad中C/S结构

C/S结构:客户机和服务器结构
由于breakpad是在进程外抓取dump,所以breakpad需要实现一个C/S结构来处理崩溃进程抓取dump的请求。
① breakpad跨进程通信的实现
breakpad中使用了命名管道来实现IPC。

在客户端,初始化ExceptionHandler的时候,如果指定了PipeName,也就表示此时需要使用进程外的dump抓取,ExceptionHandler,会建立一个 CrashGenerationClient的对象,由这个对象连接服务端,将自己注册到服务端上去。
可以参看exception_handler.cc中的ExceptionHandler::Initialize函数。

在服务端,初始化CrashGenerationServer的时候,就会建立一个命名管道,并等待客户端来连接。一旦有客户端连接上来,服务端会为每一个客户端生成一个ClientInfo的对象,之后用这个对象来管理所有的客户端,一旦有崩溃发生,服务端都会从这个对象中取出dump所需要的信息。
可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数。
② breakpad捕获崩溃生成dump的流程
有两个简单的问题,这里说明一下,:
在服务端如何为客户端生成事件句柄?
使用DuplicateHandle,即可把任意一个内核对象的句柄复制到其他进程,并且可以指定产生的句柄的权限。

如何异步的等待一个事件?
使用RegisterWaitForSingleObject,即可异步的等待一个事件,当事件发生的时候,就可以回调到一个指定的回调函数中,但是要注意的是,RegisterWaitForSingleObject会在一个新的线程中来等待这个事件,此处很容易产生多线程的调用,需要注意线程问题。

③ 服务端关键数据结构: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。

explicit CrashReportSender(const wstring &checkpoint_file);

这个文件只有一个作用,就是用来保存上次上传崩溃的时间和今天上传过的崩溃的次数。通过这个文件,我们就可以来设置每日上传的崩溃的最大数量。

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> &parameters, 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吧,开源项目就是这么发展的。

Google Breakpad的使用相关推荐

  1. google breakpad native crash分析工具

    一. BreakPad简介 Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合. Breakpad由三个主要组件: client,以library的形式内置在你的应用中,当崩溃发 ...

  2. Google Breakpad 完全解析(二) —— Windows前台实现篇

    原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢. 好,看完了如何使用breakpad,我们现在看看breakpad在Windows下到底是如何实现的呢? ...

  3. C++库(Google Breakpad)

    Google Breakpad是什么? 一个开源的多平台崩溃报告系统. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solari ...

  4. Google breakpad

    Google breakpad是一个非常实用的跨平台崩溃转储和分析模块,支持Windows,Linux和Mac和Solaris多个平台.由于它本身跨平台,所以很大的减少我们在平台移植时的工作,毕竟崩溃 ...

  5. 使用 Google Breakpad 来助力解决程序崩溃

    背景 作为一名程序,最头疼的莫过于项目上线后收到程序崩溃的通知,若能够在手头重现出该问题,那相对来说项目能够及时的修复并更新:如果无法重现外网崩溃的问题,那就十分的"头疼"了.要是 ...

  6. Google Breakpad 在 windows下捕获程序崩溃报告

    一.简述 Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,支持Linux.mac.solaris.windows. 通俗一点来讲,我们可以借助Google breakpad来 ...

  7. Breakpad查C++崩溃问题

    Breakpad 是 Google 用 C++ 编写的一个开源.跨平台的崩溃报告系统,它支持 Windows.Linux 和 macOS,并提供了一个上传器,可以在进程崩溃时向一个配置好的 URL 提 ...

  8. core文件怎么分析_c++ crash 分析工具:breakpad

    做为一位c++开发人员,如果你没有遇到过线上程序崩溃,说明你写的代码少或者说你的测试同学很给力,或者是你的服务太简单了,这个题外话哈.俗话说,"夜路走多了总会遇到鬼(bug)". ...

  9. google的开源工程,真多

    http://blog.drhack.net/google-open-source-projects-you-knew-that/这个页面介绍了google的开源工程,真多,也很实用. Text Fi ...

  10. 使用breakpad收集native奔溃日志及dump解析

    使用breakpad收集native奔溃日志及dump解析 本文是学习使用breakpad的一个总结,在这过程中看了很多博客,但是相对有用的有以下几篇: Android使用Google Breakpa ...

最新文章

  1. 深度学习可解释性问题如何解决?图灵奖得主Bengio有一个解
  2. 快速解决mongodb出现id重复问题
  3. python经典案例-20个Python练手经典案例,能全做对的人确实很少!
  4. CentOS6 kvm添加网卡桥接口脚本
  5. 作为团队技术负责人,我是这样面试前端的
  6. 强大的DataGrid组件[7]_自定义DataGrid——Silverlight学习笔记[15]
  7. C++:指针数组理解
  8. Android 系统(273)---分布式Redis主备复制
  9. 如何编制试算平衡表_试算平衡表的编制
  10. java 字符串优化_Java字符串优化
  11. springMVC的文件上传于下载
  12. 测试了几款mysql监控工具
  13. SAI:Switch Abstraction Interface
  14. 如何安装王码五笔字型输入法86版
  15. C++小游戏数字炸弹
  16. Web App - MUI框架实战
  17. 企业短信平台发展趋势和展望——SUBMAIL赛邮
  18. 一、Matlab图像处理入门
  19. iOS各大名人的博客
  20. 深度神经网络的成功应用,深度神经网络技术赋能

热门文章

  1. JS中某事件需要同时调用几个function另类解决办法。
  2. 查询用户表(按照依赖对象排序)
  3. Windows软件防火墙实现技术简述
  4. hive 建表_大数据面试必备 | Hive数据仓工具面试题!
  5. PHP 动态获取数组索引的值
  6. 学校计算机的使用作文,电脑课上作文(3篇)
  7. 虚拟服务器怎么安装网站,虚拟主机如何安装网站个人博客(WordPress)
  8. Android反编译:使用dex2jar查看dex文件
  9. JProfiler分析CPU占用实用教程
  10. Linux上RandomAccessFile访问FTP文件出错