S.l.e!ep.¢%

像打了激速一样,以四倍的速度运转,开心的工作
简单、开放、平等的公司文化;尊重个性、自由与个人价值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Chrome的Crash Report服务

Posted on 2010-08-18 13:34 S.l.e!ep.¢% 阅读(969) 评论(0)  编辑 收藏 引用 所属分类: VC

Chrome的Crash Report服务

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

简介


当我们采用一个调试器调试一个应用程序时,我们总是希望能单步跟踪代码、设置断点、查看变量值,哪怕变量是自定义的用户类型。但是对于一个EXE程序来说,基本上就是一堆二进制数据(目前的WindowsEXE程序中还包含了一些头部信息,用于系统执行程序)。当一个EXE程序运行时,系统将为这些EXE分配一些额外的内存用于存储运行时数据(StackHeap)。但是依旧没有任何调试方面的信息。当程序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接口:DBGHelpDIA供用户调用。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),可以用下图来描述:

 clip_image001

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

 clip_image002

在缺省情况下,编译器和链接器不会产生调试信息,我们需要在编译和链接选项中设置参数,告诉编译器和链接器我们需要生成DEBUG信息、生成什么格式的调试信息、调试信息存储在哪里等。
下面我们按照Visual C++6.0Visual 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 同时产生COFFCodeView/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中不被支持了。

clip_image003

链接器选项

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

  静态库的调试信息

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

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

调试信息是否影响最终EXE文件的大小,依赖于调试信息存储的地方,说到底依赖于我们选择的格式。
当采用COFFCodeView方式时,通常调试信息存储在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

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

 

上一篇中描述了在Windows平台下产生Debug信息的一些背景知识,这一篇中我们介绍一下ChromeCrash 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会把代码流程切换到异常处理服务例程。操作系统异常处理服务例程会查看当前进程是否处于调试状态,如果是,则通知调试器发生了异常,如果不是则操作系统会查看当前线程是否安装了的异常帧链,如果安装了SEHtry.... 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文件。大致流程如下:

clip_image004

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

clip_image005

crash_genration_client 的实现

几个关键信号量变量


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

HANDLE crash_event_;
表示crash_generation_client是否有exception事件发生的信号量。在crash_generation_clientcrash_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 paramterpurecall两种在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方式提交到服务器上。估计googlecrash report serve r(https://clients2.google.com/cr/report ) 也就是一个简单的网页处理脚本,完全可以认为是通过一个表单提交上来的信息。

Browser 如何使用crash report服务

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

clip_image006

其次,在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的。




 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理