Chrome的Crash Report服務

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

本文翻譯自debugInfo網站上一篇文章generating debug information with visual c++。因爲Chrome的Crash產生的Debug信息和這個有一些關係,所以作一些背景知識介紹html

簡介c++


當咱們採用一個調試器調試一個應用程序時,咱們老是但願能單步跟蹤代碼、設置斷點、查看變量值,哪怕變量是自定義的用戶類型。可是對於一個EXE程序來講,基本上就是一堆二進制數據(目前的Windows中EXE程序中還包含了一些頭部信息,用於系統執行程序)。當一個EXE程序運行時,系統將爲這些EXE分配一些額外的內存用於存儲運行時數據(Stack,Heap)。可是依舊沒有任何調試方面的信息。當程序Coredump時,像WinDBG這些調試器是如何定位到哪一行的呢,這些都是Visual Studio中的一些編譯特性。chrome

調試信息種類安全

在Intel X86指令體系下的Windows平臺,一個EXE或者DLL中主要有下面幾種調試信息:服務器

Debug類型數據結構

說明app

Public functions and variableside

主要包括了一些全局變量和全局函數信息。在Debug信息中主要存儲了他們的位置、大小、名字信息函數

Private functions and variablespost

主要包含了非全局的變量和函數信息。

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的。

相關文章
相關標籤/搜索