• Chrome的Crash Report服务


    《本文转自:http://www.cppblog.com/woaidongmao/archive/2009/10/22/99211.aspx》

    本文翻译自debugInfo网站上一篇文章generating debug information with visual c++。由于Chrome的Crash产生的Debug信息和这个有一些关系,因此做一些背景知识介绍

    简介


    当我们采用一个调试器调试一个应用程序时,我们总是希望能单步跟踪代码、设置断点、查看变量值,哪怕变量是自定义的用户类型。但是对于一个EXE程序来说,基本上就是一堆二进制数据(目前的Windows中EXE程序中还包含了一些头部信息,用于系统执行程序)。当一个EXE程序运行时,系统将为这些EXE分配一些额外的内存用于存储运行时数据(Stack,Heap)。但是依旧没有任何调试方面的信息。当程序Coredump时,像WinDBG这些调试器是如何定位到哪一行的呢,这些都是Visual Studio中的一些编译特性。

    调试信息种类

    在Intel X86指令体系下的Windows平台,一个EXE或者DLL中主要有下面几种调试信息:

    Debug类型

    说明

    Public functions and variables

    主要包括了一些全局变量和全局函数信息。在Debug信息中主要存储了他们的位置、大小、名字信息

    Private functions and variables

    主要包含了非全局的变量和函数信息。

    Source File and  Line Information

    主要包含了每一行代码在EXE中的对应位置信息

    Type Information

    主要存储了各种数据类型信息,包括用户自定义的数据类型

    FPO Information

    FPO(Frame Pointer Omission)。Frame Pointer 是一种用来在调用堆栈(Call stack)中找到下一个将要被调用的函数的数据结构源代码的行序号(Source-line numbers);编译器可针对这一特性做优化,Debug信息中依旧可以存储一些信息,用来查询函数的栈区帧大小信息。

    Edit and Continue Information

    主要包含了要实现用户编辑后可以继续执行特性的相关信息。

    表1 Windows平台下调试信息分类


    调试文件格式分类
    在过去二十多年的时间里,微软采用了三种形式来存储DEBUG信息:COFF,CodeView,Program Database。我们从三个维度来对比分析一下这三种格式:
    1. 每种格式中存储了哪些调试信息?
    2. 每种格式的调试信息存储在哪里?(包含在EXE中还是单独的调试信息文件)
    3. 每种格式的设计文档是否齐全?

    COFF

    这是最老的一种格式,只能存储三种信息:Public functions and variables, source file and line information, FPO信息。COFF信息是存储在EXE文件中的,不能单独存储。这种格式文档有详细的说明: Microsoft Portable Executable and Common Object File Format Specification

    CodeView

    这是在COFF基础上推出的一个更为复杂一些的格式。它可以存储表1中除了Edit and Continue Information外的其他信息。CodeView信息通常存储在EXE文件中,但是它也可以存储在单独的文件(.DBG)中。CodeView的格式文档在MSDN上有部分说明,不是很齐全。

    Program Database

    这是微软最新的格式。他可以存储表1中所有信息。另外,他还存储了增量链接(increase Linking)信息。这在其他格式中不可能存在的。
    Program Database格式信息通常存储在单独的文件(.PDB)中。
    Program Database格式微软并没有提供格式文档说明。但是微软提供了两套SDK接口:DBGHelp和DIA供用户调用。PDB有两个版本,一个是PDB 2.0, 主要在VS6.0中使用。一个是PDB7.0,主要用在Visual Studio.NET之后的版本。DBGHelp是普通的API接口。而DIA提供的是COM接口。相对来说DBGHelp使用起来相对简单一些,但是DIA提供的信息相对丰富一些。
    下表是三种格式的对比:

    格式

    文档齐全度

    存储

    public function and variables

    Type information

    FPO information

    EnC information

    COFF

    齐全

    EXE

    +

    -

    +

    -

    CodeView

    部分

    EXE或者单独文件(.DBG)

    +

    +

    +

    -

    Program Database

    单独文件(.PDB)

    +

    +

    +

    +

    表2:三种不同格式的对比

    如何产生调试信息

    在Windows下,一个EXE典型的生成过程主要分为两步:编译(Compile)和链接(Link),可以用下图来描述:

    如果我们想产生DEBUG信息,同样需要分为两步:我们要求编译器(Compiler)为每一个源文件产生相应的调试信息文件;然后由链接器(Linker)把各个调试文件合并成一个大的调试文件。可以用下图来描述:

    在缺省情况下,编译器和链接器不会产生调试信息,我们需要在编译和链接选项中设置参数,告诉编译器和链接器我们需要生成DEBUG信息、生成什么格式的调试信息、调试信息存储在哪里等。
    下面我们按照Visual C++6.0和Visual C++.NET两种不同版本的IDE分别介绍。

    Visual C++ 6.0

    编译器选项

    主要包含了下面几个选项:
    /Zd 产生COFF格式调试信息,并保存在目标文件中。
    /Z7 产生CodeView格式调试信息,并保证在目标文件中。
    /Zi 产生Program Database格式调试信息,并单独存储在.PDB文件中。
    /ZI 和Zi类似。并在Zi基础上增加了Edit and Continue信息。

    选项

    格式

    存储格式

    包含内容

    /Zd

    COFF

    .obj

    • Public functions and variables
    • Source file and line information
    • FPO information

    /Z7

    CodeView

    .obj

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information

    /Zi

    Program Database

    .PDB

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information

    /ZI

    Program Database

    .PDB

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information
    • Edit and Continue data

    链接器选项

    主要包含了一下链接选项:

    /debug 告诉Linker产生调试信息,如果该选项未设置,其它选项设置都不起作用。

    /debugtype 告诉Linker采用哪个格式的调试信息,主要包含了下面几种:/debugtype:coff COFF格式; /debugtype:cv CodeView或者Program Database格式(依赖 /pdb 选项); /debugtype:both 同时产生COFF和CodeView/Program Database信息。

    /pdb 告诉Linker到底采用CodeView还是Program Database格式. /pdb:none 告诉Linker采用CodeView格式, /pdb:filename 告诉linker采用Program Database格式而且制定了PDB文件的名字.如果debugtype:coff 选项设置了, /pdb 选项不起作用.

    /pdbtype选项主要用在有多个文件需要链接时,告诉链接器如何处理各个文件的调试信息。/pdbtype:sept表示Linker不会将各个文件的PDB文件合并到最后一个PDB文件中。如果要调试,需要准备各个PDB文件,而/pdbtype:con选项就是将各个PDB文件合并到一个PDB文件中。

    /debugtype

    /pdb

    格式

    存储

    coff

    无作用

    COFF

    EXE

    coff

    无作用

    COFF

    EXE

    cv

    /pdb:none

    CodeView

    EXE

    cv

    /pdb:filename

    Program Database

    .PDB

    both

    /pdb:none

    COFF and CodeView

    EXE

    both

    /pdb:filename

    COFF and Program Database

    COFF信息存储在EXE中,Program Database存储在单独PDB文件中

    表3 不同的Linker选项

    Visual C++ 2002,2003,2005

    编译器选项

    主要包含了/Zd, /Z7, /Zi, /ZI。但是/Zd已经在Visual C++ 2005中不被支持了。

    链接器选项

    主要包含三个选项:
    /debug 告诉Linker产生调试信息,如果该选项未设置,其它选项设置都不起作用。 
    /pdb:filename 告诉linker采用Program Database格式而且制定了PDB文件的名字.
    /pdbstripped 告诉Linker产生单独的PDB文件,只包含两种信息:public functions and variables;FPO information.
    在Visual C++.NET中,Linker已经不支持COFF和CODEVIEW两种格式了。

     静态库的调试信息

    由于静态库不需要Linker,因此静态库的调试信息相对来说就简单多了,设置/Z*(Z7,Zd,Zi,ZI)选项就可以产生相应的调试信息。
    对于Z7和Zd选项,调试信息存储在相应的.lib文件中,而Zi和ZI选项,调试信息存储在独立的.PDB文件中。

    调试信息和可执行文件大小关系

    调试信息是否影响最终EXE文件的大小,依赖于调试信息存储的地方,说到底依赖于我们选择的格式。
    当采用COFF和CodeView方式时,通常调试信息存储在EXE文件中,将会导致EXE文件极度膨胀,基本上会翻倍。
    当采用Program Database方式时,EXE文件就几乎不受影响了。EXE文件仅仅增加了几百个字节的头域,用于定位相应的PDB文件信息。

    选项

    格式

    存储

    内容

    /Z7

    CodeView

    .OBJ

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information

    /Zi

    Program Database

    .PDB

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information

    /ZI

    Program Database

    .PDB

    • Public functions and variables
    • Private functions and variables
    • Source file and line information
    • Type information
    • FPO information
    • Edit and Continue data

    表4:Visual C++.NET下的编译选项

    上一篇中描述了在Windows平台下产生Debug信息的一些背景知识,这一篇中我们介绍一下Chrome的Crash Report服务上报了哪些信息。
        按照我们上篇所介绍的,如果应用程序比较复杂,堆栈比较深,一个异常产生的PDB文件也许是几十MB,甚至上百MB,要把这么大的文件上传到服务器,无论从性能上、还是可靠性上都是一个问题,如果用户知道了,估计也不会买账。


        在Windows XP之后,Microsoft为我们提供了一个新的dump库,称为minidumps库,这个库为我们提供了定制化的实现,我们可以根据自己的需要定制产生的dump内容。缺省设置下,已经可以获取到发生异常时的堆栈信息以及一些局部变量值,而相应产生的dump文件只有几十到几百KB级别。这个数量级的内容,传输起来就相对方便多了。


        minidumps主要包含在DBGHelp.dll库中,这个库中包含了MiniDumpWriteDump 函数:

    BOOL MiniDumpWriteDump( 
    HANDLE hProcess, 
    DWORD ProcessId, 
    HANDLE hFile, 
    MINIDUMP_TYPE DumpType, 
    PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 
    PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 
    PMINIDUMP_CALLBACK_INFORMATION CallbackParam 
    );


       其中 DumpType参数表示了dump的类型:

    typedef enum _MINIDUMP_TYPE { 
    MiniDumpNormal = 0x00000000, 
    MiniDumpWithDataSegs = 0x00000001, 
    MiniDumpWithFullMemory = 0x00000002, 
    MiniDumpWithHandleData = 0x00000004, 
    MiniDumpFilterMemory = 0x00000008, 
    MiniDumpScanMemory = 0x00000010, 
    MiniDumpWithUnloadedModules = 0x00000020, 
    MiniDumpWithIndirectlyReferencedMemory = 0x00000040, 
    MiniDumpFilterModulePaths = 0x00000080, 
    MiniDumpWithProcessThreadData = 0x00000100, 
    MiniDumpWithPrivateReadWriteMemory = 0x00000200, 
    MiniDumpWithoutOptionalData = 0x00000400, 
    MiniDumpWithFullMemoryInfo = 0x00000800, 
    MiniDumpWithThreadInfo = 0x00001000, 
    MiniDumpWithCodeSegs = 0x00002000, 
    MiniDumpWithoutManagedState = 0x00004000, 
    } MINIDUMP_TYPE;


       大家可以观察到可定制化的种类还是挺多的。具体的参数意义和函数说明,请大家参考MSDN上的说明,亦可以参考DebugInfo上的 effective minidumps 一文介绍。
       Chrome上报的内容就是基于minidumps库来实现的,Chrome在此基础上稍微做了一些调整。 

        在Chrome中,Crash Report服务当程序Crash时,将会上报Dump信息到Google的一个URL(https://clients2.google.com/cr/report )中。

       在下一篇中,我们将进入正题,讨论Chrome中是如何实现Crash信息采集和上报的。
         1. Chrome是如何捕获到异常的?
         2. Chrome是如何在进程外实现dump文件的转储的?
         3. Chrome是如何实现上传的?

    一个C++程序, 当发生异常时,比如内存访问违例时,CPU硬件会发现此问题,并产生一个异常(你可以把它理解为中断),然后CPU会把代码流程切换到异常处理服务例程。操作系统异常处理服务例程会查看当前进程是否处于调试状态,如果是,则通知调试器发生了异常,如果不是则操作系统会查看当前线程是否安装了的异常帧链,如果安装了SEH(try.... catch....),则调用SEH,并根据返回结果决定是否全局展开或者局部展开。如果异常链中所有的SEH都没有处理此异常,而且此进程还处于调试状态,则操作系统会再次通知调试器发生异常(二次异常)。如果还没人处理,则调用操作系统的默认异常处理代码UnhandledExceptionHandler,不过操作系统允许你Hook这个函数,就是通过 SetUnhandledExceptionFilter函数来设置。大部分异常通过此种方法都能捕获。

      不过在Visual C++ 2005之后, Microsoft 对 CRT ( C 运行时库)的一些与安全相关的代码做了些改动,典型的,例如增加了对缓冲溢出的检查。新 CRT 版本在出现错误时强制把异常抛给默认的调试器(如果没有配置的话,默认是 Dr.Watson ),而不再通知应用程序设置的异常捕获函数,这种行为主要在以下两种情况出现。 
      (1)   遇到 _invalid_parameter 错误,而应用程序又没有主动调用 _set_invalid_parameter_handler 设置错误捕获函数。

      (2) 虚函数调用错误, 而应用程序又没有主动调用_set_purecall_handler设置捕获函数。
    在Chrome中对这两种情况也做了特殊处理。专门设置了两个回调函数进行捕获处理。

    Chrome的Crash Report主要流程

    在Chrome中,支持两种不同模式的Dump。
    进程外Dump :由独立的Crash Handle Process处理Dump的生成过程,主进程产生异常时,通过IPC方式通知Crash Handle Process。由Crash Handle Process中的crash_generation_server负责写Dump文件。大致流程如下:

     

    上图中,crash_generation_client和crash_generation_server之间是进程间通讯(IPC)。crash_report_sender负责将dump信息发送到google的crash report server(https://clients2.google.com/cr/report)。
    进程内Dump :与进程外方式类似,只不过在Browser进程中增加了一个crash_handle_thread线程,由此线程负责写dump.基本流程如下:

     

    crash_genration_client的实现

    几个关键信号量变量


      HANDLE server_alive_; 
    表示crash_handle_process是否活动的变量

    HANDLE crash_event_; 
    表示crash_generation_client是否有exception事件发生的信号量。在crash_generation_client和crash_generation_server建立IPC通道后,crash_generation_server将等待这个信号量。

    HANDLE crash_generated_; 
    表示crash_generation_server是否已写完dump文件的信号量。由crash_generation_server在写完dum文件后,设置该信号量。

    几个关键变量

    CustomClientInfo custom_info_; 
    描述当前发生Exception的进程的一些信息,在这里可能是Browser进程,也可能是Render进程。

    EXCEPTION_POINTERS* exception_pointers_; 
    异常发生时,所有异常信息保存该指针指向的内存中。

    MDRawAssertionInfo assert_info_; 
    Assert异常信息指针。

    在crash_generation_client初始化时,将向crash_generation_server注册,建立ICP通道,且把上面几个地址发送给crash_generation_server,当后续crash_generation_client发生异常时,crash_generation_server将从这几个地址中读取信息,生成dump文件。(当然这是进程外模式,进程内模式由browser进程内的独立线程完成这些工作。)

    一个关键函数

    下面函数是

    1.           bool CrashGenerationClient::SignalCrashEventAndWait() {

    2.             assert(crash_event_);

    3.             assert(crash_generated_);

    4.             assert(server_alive_);

    5.            

    6.             // Reset the dump generated event before signaling the crash

    7.             // event so that the server can set the dump generated event

    8.             // once it is done generating the event.

    9.             if (!ResetEvent(crash_generated_)) {

    10.           return false ;

    11.         }

    12.        

    13.         if (!SetEvent(crash_event_)) {

    14.           return false ;

    15.         }

    16.        

    17.         HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};

    18.        

    19.         DWORD result = WaitForMultipleObjects(kWaitEventCount,

    20.                                               wait_handles,

    21.                                               FALSE,

    22.                                               kWaitForServerTimeoutMs);

    23.        

    24.         // Crash dump was successfully generated only if the server

    25.         // signaled the crash generated event.

    26.         return result == WAIT_OBJECT_0;

    27.       }

    这个函数是crash_generation_client产生exception时,如何和服务器交互的。基本上在上面介绍变量时已经介绍到了。
    crash_generation_client是如何捕获异常的
    在本文开始部分已经描述了原理。我们可以看一下实现。

    1.           void ExceptionHandler::Initialize(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) {

    9.             LONG instance_count = InterlockedIncrement(&instance_count_);

    10.         filter_ = filter;

    11.         callback_ = callback;

    12.         callback_context_ = callback_context;

    13.         dump_path_c_ = NULL;

    14.         next_minidump_id_c_ = NULL;

    15.         next_minidump_path_c_ = NULL;

    16.         dbghelp_module_ = NULL;

    17.         minidump_write_dump_ = NULL;

    18.         dump_type_ = dump_type;

    19.         rpcrt4_module_ = NULL;

    20.         uuid_create_ = NULL;

    21.         handler_types_ = handler_types;

    22.         previous_filter_ = NULL;

    23.       #if _MSC_VER >= 1400  // MSVC 2005/8

    24.         previous_iph_ = NULL;

    25.       #endif   // _MSC_VER >= 1400

    26.         previous_pch_ = NULL;

    27.         handler_thread_ = NULL;

    28.         is_shutdown_ = false ;

    29.         handler_start_semaphore_ = NULL;

    30.         handler_finish_semaphore_ = NULL;

    31.         requesting_thread_id_ = 0;

    32.         exception_info_ = NULL;

    33.         assertion_ = NULL;

    34.         handler_return_value_ = false ;

    35.         handle_debug_exceptions_ = false ;

    36.        

    37.         // Attempt to use out-of-process if user has specified pipe name.

    38.         if (pipe_name != NULL) {

    39.           scoped_ptr<CrashGenerationClient> client(

    40.               new CrashGenerationClient(pipe_name,

    41.                                         dump_type_,

    42.                                         custom_info));

    43.        

    44.           // If successful in registering with the monitoring process,

    45.           // there is no need to setup in-process crash generation.

    46.           if (client->Register()) {

    47.             crash_generation_client_.reset(client.release());

    48.           }

    49.         }

    50.        

    51.         if (!IsOutOfProcess()) {

    52.           // Either client did not ask for out-of-process crash generation

    53.           // or registration with the server process failed. In either case,

    54.           // setup to do in-process crash generation.

    55.        

    56.           // Set synchronization primitives and the handler thread.  Each

    57.           // ExceptionHandler object gets its own handler thread because that's the

    58.           // only way to reliably guarantee sufficient stack space in an exception,

    59.           // and it allows an easy way to get a snapshot of the requesting thread's

    60.           // context outside of an exception.

    61.           InitializeCriticalSection(&handler_critical_section_);

    62.           handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);

    63.           assert(handler_start_semaphore_ != NULL);

    64.        

    65.           handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);

    66.           assert(handler_finish_semaphore_ != NULL);

    67.        

    68.           // Don't attempt to create the thread if we could not create the semaphores.

    69.           if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {

    70.             DWORD thread_id;

    71.             handler_thread_ = CreateThread(NULL,         // lpThreadAttributes

    72.                                            kExceptionHandlerThreadInitialStackSize,

    73.                                            ExceptionHandlerThreadMain,

    74.                                            this ,         // lpParameter

    75.                                            0,            // dwCreationFlags

    76.                                            &thread_id);

    77.             assert(handler_thread_ != NULL);

    78.           }

    79.        

    80.           dbghelp_module_ = LoadLibrary(L"dbghelp.dll" );

    81.           if (dbghelp_module_) {

    82.             minidump_write_dump_ = reinterpret_cast <MiniDumpWriteDump_type>(

    83.                 GetProcAddress(dbghelp_module_, "MiniDumpWriteDump" ));

    84.           }

    85.        

    86.           // Load this library dynamically to not affect existing projects.  Most

    87.           // projects don't link against this directly, it's usually dynamically

    88.           // loaded by dependent code.

    89.           rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll" );

    90.           if (rpcrt4_module_) {

    91.             uuid_create_ = reinterpret_cast <UuidCreate_type>(

    92.                 GetProcAddress(rpcrt4_module_, "UuidCreate" ));

    93.           }

    94.        

    95.           // set_dump_path calls UpdateNextID.  This sets up all of the path and id

    96.           // strings, and their equivalent c_str pointers.

    97.           set_dump_path(dump_path);

    98.         }

    99.        

    100.     // There is a race condition here. If the first instance has not yet

    101.     // initialized the critical section, the second (and later) instances may

    102.     // try to use uninitialized critical section object. The feature of multiple

    103.     // instances in one module is not used much, so leave it as is for now.

    104.     // One way to solve this in the current design (that is, keeping the static

    105.     // handler stack) is to use spin locks with volatile bools to synchronize

    106.     // the handler stack. This works only if the compiler guarantees to generate

    107.     // cache coherent code for volatile.

    108.     // TODO(munjal): Fix this in a better way by changing the design if possible.

    109.    

    110.     // Lazy initialization of the handler_stack_critical_section_

    111.     if (instance_count == 1) {

    112.       InitializeCriticalSection(&handler_stack_critical_section_);

    113.     }

    114.    

    115.     if (handler_types != HANDLER_NONE) {

    116.       EnterCriticalSection(&handler_stack_critical_section_);

    117.    

    118.       // The first time an ExceptionHandler that installs a handler is

    119.       // created, set up the handler stack.

    120.       if (!handler_stack_) {

    121.         handler_stack_ = new vector<ExceptionHandler*>();

    122.       }

    123.       handler_stack_->push_back(this );

    124.    

    125.       if (handler_types & HANDLER_EXCEPTION)

    126.         previous_filter_ = SetUnhandledExceptionFilter(HandleException);

    127.    

    128.   #if _MSC_VER >= 1400  // MSVC 2005/8

    129.       if (handler_types & HANDLER_INVALID_PARAMETER)

    130.         previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);

    131.   #endif   // _MSC_VER >= 1400

    132.    

    133.       if (handler_types & HANDLER_PURECALL)

    134.         previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);

    135.    

    136.       LeaveCriticalSection(&handler_stack_critical_section_);

    137.     }

    138.   }


    在该函数的Line126中,调用了SetUnhandledExceptionFilter函数,设置了我们要处理的回调函数。
    另外针对invalid paramter和purecall两种在VC2005中不支持的特性,做了特殊处理。

    crash_generation_server 的实现

    crash_generation_server基本上就是一个IPC Server。负责监听各个crash_generation_client的请求。
    crash_generation_server的关键函数也就是一个简单的状态机函数:

    void CrashGenerationServer::HandleConnectionRequest() {

      // If we are shutting doen then get into ERROR state, reset the event so more

      // workers don't run and return immediately.

      if (shutting_down_) {

        server_state_ = IPC_SERVER_STATE_ERROR;

        ResetEvent(overlapped_.hEvent);

        return ;

      }

      switch (server_state_) {

        case IPC_SERVER_STATE_ERROR:

          HandleErrorState();

          break ;

        case IPC_SERVER_STATE_INITIAL:

          HandleInitialState();

          break ;

        case IPC_SERVER_STATE_CONNECTING:

          HandleConnectingState();

          break ;

        case IPC_SERVER_STATE_CONNECTED:

          HandleConnectedState();

          break ;

        case IPC_SERVER_STATE_READING:

          HandleReadingState();

          break ;

        case IPC_SERVER_STATE_READ_DONE:

          HandleReadDoneState();

          break ;

        case IPC_SERVER_STATE_WRITING:

          HandleWritingState();

          break ;

        case IPC_SERVER_STATE_WRITE_DONE:

          HandleWriteDoneState();

          break ;

        case IPC_SERVER_STATE_READING_ACK:

          HandleReadingAckState();

          break ;

        case IPC_SERVER_STATE_DISCONNECTING:

          HandleDisconnectingState();

          break ;

        default :

          assert(false );

          // This indicates that we added one more state without

          // adding handling code.

          server_state_ = IPC_SERVER_STATE_ERROR;

          break ;

      }

    }

    这个函数负责维护IPC的各种连接状态。并进行不同处理,相当直观,无须赘述!

    crash_report_sender的实现

    这个实现非常简单,模拟了一个表单的提交,将minidump信息封装成一个MIME类型,通过HTTP方式提交到服务器上。估计google的crash report server(https://clients2.google.com/cr/report )也就是一个简单的网页处理脚本,完全可以认为是通过一个表单提交上来的信息。

    Browser如何使用crash report服务

    首先,crash_handle process是一个独立运行的程序,负责监听chrome进程的请求。

     

    其次,在Browser初始化时,生成crash_generation_client实例,

    在chrome的主函数入口中包含了

      // Initialize the crash reporter.
      InitCrashReporterWithDllPath(dll_full_path);

    这一行代码,在这个函数中生成了一个全局变量

      g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, NULL, callback,
                       NULL, google_breakpad::ExceptionHandler::HANDLER_ALL,
                       dump_type, pipe_name.c_str(), info->custom_info);

    其中ExceptionHandler类包含了CrashGenerationClient实例。

    由于Crash Report服务应该是越早启动越好,因此我们也可以看到chrome初始化该变量的位置也是相当的靠前。

    小节

    Google的crash_report服务几个关键点:
    1.Minidump的定制化处理机制。
    2.进程外dump写机制。
    3.chrome是如何捕获Exception的。

  • 相关阅读:
    分类管理模块
    Java8新特性 集合的stream的map
    条件和分页查询
    工作常用系统汇总
    Dubbo简单环境搭建
    深入浅出 TCP/IP 协议栈
    非对称加密
    理解Cookie和Session机制
    jQuery学习
    数据库系列学习(六)-函数之数学函数
  • 原文地址:https://www.cnblogs.com/kanego/p/4081287.html
Copyright © 2020-2023  润新知