zoukankan      html  css  js  c++  java
  • C++库(Google Breakpad)

    Google Breakpad是什么?

      一个开源的多平台崩溃报告系统。

      Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solaris。由于他本身跨平台,所以很大程度上减少了我们在平台移植时的工作,毕竟崩溃转储,每个平台下都不同,使用起来很难统一,而Google breakpad就帮我们做到了这一点,不管是哪个平台下的崩溃,都能够进行统一的分析。

      现在很多工程都在使用它:最著名的几个如Chrome,Firefox,Picasa和Google Earth。另外他的License是BSD的,也就是说,我们即便是在商业软件中使用,也是合法的。好东西!

    Google Breakpad原理(比较抽象)

     

          breakpad把应用程序分成三个部分,代码,breakpad客户端和调试信息。

          1. 在build system中,通过symbol dumper用平台相关的调试信息生成平台无关的symbol文件。这样做的好处很明显,一旦平台无关了,所有平台的崩溃就可以做统一的分析了。

      2. breakpad采取进程外转储和分析崩溃的方式,他使用C/S结构,客户端用来捕获当前进程中发生的崩溃,并通知服务端崩溃发生。服务端用来响应客户端,抓取dump文件。这样做的目的是为了减少崩溃进程对dump的影响

      3. Dump生成后转发到崩溃分析器中,这个部分可以在本地也可以在服务器上,它对Dump文件进行解析,生成可读的堆栈信息。

    Google Breakpad安装和编译(Windows)

      1、  下载Google breakpad源代码(从svn中签出最新代码)

      2、  安装python(2.7版本可用)

      3、  生成Windows工程文件

      cd  "源码目录/src/tools/gyp"
       # 注意,此处不能使用全路径,不然会出错
      gyp.bat --no-circular-check "../../client/windows/breakpad_client.gyp"

      

      4、  Build All

      备注:如果无法通过svn下载源代码,可在CSDN上利用网友分享的。

    Google Breakpad的使用

           在Windows下使用breakpad的方法很简单,只需要创建一个ExceptionHandler的类即可,这个ExceptionHandler就是用户捕获崩溃的类。

     1  handler = new ExceptionHandler(const wstring& dump_path,
     2                                FilterCallback filter,
     3                                MinidumpCallback callback,
     4                                void* callback_context,
     5                                int handler_types,
     6                                MINIDUMP_TYPE dump_type,
     7                                const wchar_t* pipe_name,
     8                                const CustomClientInfo* custom_info);

    参数说明:

      l  //dump文件路径。

      l  //crash时调用回调函数,返回ture/false来继续/停止异常处理。

      l  //minidump写入后调用的回调函数

      l  //设备上下文,回调使用的

      l  //HandlerType异常类型,可在exception_handler.h查看

      l  //minidump的类型,使用DbgHelp.h中MINIDUMP_TYPE类型

      l  //接收crash的server端的管道名

      l  //使用OOP产生minidump时,使用这个自定义客户信息类指针来发送自定义数据

         使用breakpad的时候,有两个地方需要注意:

      1. 把breakpad的solution下的几个工程,包含到你开发的工程中,或者直接包含它们的lib。

        common:基础功能,包含一个对GUID的封装和http上传的类。

        exception_handler:用来捕获崩溃的类。

        crash_generation_server:breakpad的服务端,用来在产生崩溃时抓取dump。

        crash_generation_client:breakpad的客户端,用来捕获当前进程的崩溃。

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

    进程内抓取Dump

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

     1 bool InitBreakpad()
     2 {
     3     google_breakpad::ExceptionHandler *pCrashHandler =
     4         new google_breakpad::ExceptionHandler(L"c:dumps",
     5         onExceptionFilter,
     6         onMinidumpDumped,
     7         NULL,
     8         google_breakpad::ExceptionHandler::HANDLER_ALL,
     9         MiniDumpNormal,
    10         NULL,
    11         NULL);
    12 
    13     if(pCrashHandler == NULL)
    14     {
    15         return false;
    16     }
    17 
    18     return true;
    19 }

    进程外抓取Dump

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

     1 const wchar_t s_pPipeName[] = L"\.pipereakpadcrash_handler_server";
     2 const std::wstring s_strCrashDir = L"c:dumps";
     3 
     4 bool InitBreakpad()
     5 {
     6     google_breakpad::CrashGenerationServer *pCrashServer = 
     7         new google_breakpad::CrashGenerationServer(s_pPipeName,
     8                 NULL,
     9                 onClientConnected,
    10                 NULL,
    11                 onClientDumpRequest,
    12                 NULL,
    13                 onClientExited,
    14                 NULL,
    15                 true,
    16                 &s_strCrashDir);
    17 
    18     if(pCrashServer == NULL) 
    19     {
    20          return false;
    21     }
    22 
    23     if(!pCrashServer->Start()) 
    24     {
    25         delete pCrashServer;
    26         pCrashServer = NULL;
    27     }
    28 
    29     google_breakpad::ExceptionHandler *pCrashHandler = 
    30         new google_breakpad::ExceptionHandler(s_strCrashDir,
    31                   onExceptionFilter,
    32                   onMinidumpDumped,
    33                   NULL,
    34                   google_breakpad::ExceptionHandler::HANDLER_ALL,
    35                   MiniDumpNormal,
    36                   (pCrashServer == NULL) ? s_pPipeName : NULL,
    37                   NULL);
    38 
    39     if(pCrashHandler == NULL) 
    40     {
    41         return false;
    42     }
    43
    44    return true;
    45}

    Google Breakpad代码分析

           代码结构

      在我们来看breakpad是如何实现其强大的功能之前,我们先来看一下他的代码结构吧。

          Google breakpad的源代码都在src的目录下,分为如下几个文件夹:

      client:这下面包含了前台应用程序中捕捉dump的部分代码,里面按照平台分成各个子文件夹

      common:前台后台都会用到的部分基础代码,字符串转换,内存读写,md5神马的

      google_breakpad:breakpad中公共的头文件

      processor:用于在后台处理崩溃的核心代码

      testing:测试工程

      third_party:第三方库

      tools:一些小工具,用于处理dump文件和符号表

    Google Breakpad的崩溃捕获机制

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

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

    breakpad中的C/S结构

      由于breakpad是在进程外抓取dump,所以breakpad需要实现一个C/S结构来处理崩溃进程抓取dump的请求。

      breakpad中使用了命名管道来实现IPC。

      (1)、Register

      客户进程连接上服务进程:连接上管道,设置管道状态

      客户进程向服务进程注册:通过NamedPipe,将客户进程的信息传递给服务进程,也从服务进程读取到数据。

      客户进程传递的数据包括:服务进程ID、dump类型、crash线程id的地址、EXCEPTION_POINTERS指针的地址、参数异常和纯虚函数异常的断言信息地址、客户进程信息。服务进程会监控客户进程的退出。

      客户进程接收的数据包括:客户进程用于触发生成dump的Event Handle;客户进程用于监听的dump生成完毕Event Handle;客户进程用于监听的服务进程活着Mutex handle等。

      客户进程在TransactNamePipe函数执行完毕之后,再执行了一次WriteFile操作,发送MESSAGE_TAG_REGISTRATION_ACK消息给服务进程,服务进程收到该ACK消息时,关闭跟客户进程的连接。服务进程对管道的操作顺序为:读-->写-->读,第二次读是客户进程通知服务进程关闭管道。

      在客户端,初始化ExceptionHandler的时候,如果指定了PipeName,也就表示此时需要使用进程外的dump抓取,ExceptionHandler会建立一个 CrashGenerationClient的对象,由这个对象连接服务端,将自己注册到服务端上去。注册的过程会顺序调用IsRegistered、ConnectToServer、ConnectToPipe、RegisterClient等函数。

      大家可以参看exception_handler.cc中的ExceptionHandler::Initialize函数。

     

      在服务端,初始化CrashGenerationServer的时候,就会建立一个命名管道,并等待客户端来连接(OnPipeConnected)。一旦有客户端连接上来(HandleReadDoneState),服务端会为每一个客户端生成一个ClientInfo的对象,之后用这个对象来管理所有的客户端(ClientInfo::Initialize()),并创建客户连接句柄将连接结果信息(PrepareReply、DuplicateHandle)回送给客户端(RespondToClient),客户端接收到回送信息(ValidateResponse)。

           (2)、RequestDump

      一旦有崩溃发生,客户端就会向服务端请求Dump(RequestDump),服务端响应(OnDumpRequest)就会从这个ClientInfo对象中取出dump所需要的信息,具体地,通过RegisterWaitForSingleObject注册了崩溃Event Handle的回调函数。回调函数里做了这么几件事情:

      1)、通过ReadProcessMemory读取客户进程的信息;

      2)、生成dump;  

      3)、触发dump生成事件,通知客户进程,复位触发dump事件。

      大家可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数。

    Google Breakpad存在的问题

      进程外生成dump有很多好处,其中最大的好处就是不会被崩溃进程影响,这样dump的过程就不容易出错,但是这样也有一定的弊端。

      1. 部分崩溃无法抓取。在一些极端的崩溃,如堆栈溢出之类的崩溃,进程外抓取dump有时候会失败。

      2. 无法抓取死锁或者其他原因导致的进程僵死。breakpad现在没有检测进程死锁的代码,也没有在服务端控制客户端请求dump的代码,所以现在breakpad无法抓取死锁等进程僵死的问题。

      3. 对服务端有依赖。如果指定了在使用进程外抓取dump,breakpad对服务端就有依赖。主要体现在抓取dump时,如果服务端不存在,客户端将无法正常抓取dump,甚至有时会出现阻塞。

    PS:深入理解实现方式请调试Breakpad源代码。

  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/MakeView660/p/6077436.html
Copyright © 2011-2022 走看看