• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 319, comments - 22, trackbacks - 0, articles - 11
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            Google Breakpad 完全解析(二) —— Windows前臺(tái)實(shí)現(xiàn)篇

            2011年02月7日 — Asp J

            Table of contents for Google Breakpad 完全解析

            1. Google Breakpad 完全解析(一) —— Windows入門篇
            2. Google Breakpad 完全解析(二) —— Windows前臺(tái)實(shí)現(xiàn)篇

            原創(chuàng)文章,轉(zhuǎn)載請(qǐng)標(biāo)明出處:Soul Apogee (http://bigasp.com),謝謝。

            好,看完了如何使用breakpad,我們現(xiàn)在看看breakpad在Windows下到底是如何實(shí)現(xiàn)的呢?

            代碼結(jié)構(gòu)

            在我們來看breakpad是如何實(shí)現(xiàn)其強(qiáng)大的功能之前,我們先來看一下他的代碼結(jié)構(gòu)吧。

            Google breakpad的源代碼都在src的目錄下,他分為如下幾個(gè)文件夾:
            client:這下面包含了前臺(tái)應(yīng)用程序中捕捉dump的部分代碼,里面按照平臺(tái)分成各個(gè)子文件夾
            common:前臺(tái)后臺(tái)都會(huì)用到的部分基礎(chǔ)代碼,字符串轉(zhuǎn)換,內(nèi)存讀寫,md5神馬的
            google_breakpad:breakpad中公共的頭文件
            processor:用于在后臺(tái)處理崩潰的核心代碼
            testing:測(cè)試工程
            third_party:第三方庫(kù)
            tools:一些小工具,用于處理dump文件和符號(hào)表

            我們先來看Windows下前臺(tái)實(shí)現(xiàn)的部分,也就是client文件夾下的代碼。

            breakpad的崩潰捕獲機(jī)制

            在Windows下捕獲崩潰,大家很容易會(huì)想到那個(gè)捕獲結(jié)構(gòu)化異常的Api:SetUnhandledExceptionFilter

            breakpad中也使用了這個(gè)Api來實(shí)現(xiàn)的崩潰捕獲,另外,breakpad還捕獲了另外兩種C++運(yùn)行庫(kù)提供的崩潰,一種是使用_set_purecall_handler捕獲純虛函數(shù)調(diào)用產(chǎn)生的崩潰,還有一種是使用_set_invalid_parameter_handler捕獲錯(cuò)誤的參數(shù)調(diào)用產(chǎn)生的崩潰。

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
                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++運(yùn)行庫(kù)提供的崩潰回調(diào)中,并不會(huì)提供當(dāng)前的線程現(xiàn)場(chǎng)和崩潰信息,所以breakpad會(huì)自己生成好這些信息,然后請(qǐng)求生成dump。
            這里值得一說的是,在非異常崩潰處理中,breakpad獲取線程現(xiàn)場(chǎng)使用的函數(shù)是RtlCaptureContext而不是GetThreadContext。
            RtlCaptureContext只能捕獲當(dāng)前線程的現(xiàn)場(chǎng),而GetThreadContext可以捕獲任意線程的現(xiàn)場(chǎng),只要有這個(gè)線程的句柄即可。
            但是GetThreadContext有兩個(gè)不好的地方:不能獲取當(dāng)前線程的現(xiàn)場(chǎng);獲取現(xiàn)場(chǎng)前必須先用SuspendThread暫停目標(biāo)線程。
            而RtlCaptureContext雖然只能獲取當(dāng)前線程的現(xiàn)場(chǎng),但是調(diào)用他時(shí)可以不用暫停線程的運(yùn)行。
            對(duì)于breakpad來說,崩潰發(fā)生后越早獲取現(xiàn)場(chǎng)就越好,所以breakpad使用RtlCaptureContext函數(shù)作為他的線程獲取函數(shù)。

            breakpad中的C/S結(jié)構(gòu)

            由于breakpad是在進(jìn)程外抓取dump,所以breakpad需要實(shí)現(xiàn)一個(gè)C/S結(jié)構(gòu)來處理崩潰進(jìn)程抓取dump的請(qǐng)求。

            1. breakpad跨進(jìn)程通信的實(shí)現(xiàn)
            breakpad中使用了命名管道來實(shí)現(xiàn)IPC。

            在客戶端,初始化ExceptionHandler的時(shí)候,如果指定了PipeName,也就表示此時(shí)需要使用進(jìn)程外的dump抓 取,ExceptionHandler,會(huì)建立一個(gè) CrashGenerationClient的對(duì)象,由這個(gè)對(duì)象連接服務(wù)端,將自己注冊(cè)到服務(wù)端上 去。
            大家可以參看exception_handler.cc中的ExceptionHandler::Initialize函數(shù)。

            在服務(wù)端,初始化CrashGenerationServer的時(shí)候,就會(huì)建立一個(gè)命名管道,并等待客戶端來連接。一旦有客戶端連接上來,服務(wù)端會(huì) 為每一個(gè)客戶端生成一個(gè)ClientInfo的對(duì)象,之后用這個(gè)對(duì)象來管理所有的客戶端,一旦有崩潰發(fā)生,服務(wù)端都會(huì)從這個(gè)對(duì)象中取出dump所需要的信 息。
            大家可以參看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函數(shù)。

            2. breakpad捕獲崩潰生成dump的流程
            breakpad進(jìn)程外生成dump的流程大概如下:
            google-breakpad-out-of-process-dump:
            google-breakpad-out-of-process-dump
            這段流程的代碼就是crash_generation_client.cc和crash_generation_server.cc。

            有兩個(gè)簡(jiǎn)單的問題,這里說明一下,高手們就請(qǐng)直接忽略吧,咩哈哈:
            在服務(wù)端如何為客戶端生成事件句柄?
            使用DuplicateHandle,即可把任意一個(gè)內(nèi)核對(duì)象的句柄復(fù)制到其他進(jìn)程,并且可以指定產(chǎn)生的句柄的權(quán)限。

            如何異步的等待一個(gè)事件?
            使用RegisterWaitForSingleObject,即可異步的等待一個(gè)事件,當(dāng)事件發(fā)生的時(shí)候,就可以回調(diào)到一個(gè)指定的回 調(diào)函數(shù)中,但是要注意的是,RegisterWaitForSingleObject會(huì)在一個(gè)新的線程中來等待這個(gè)事件,此處很容易產(chǎn)生多線程的調(diào)用,需 要注意線程問題。

            3. 服務(wù)端關(guān)鍵數(shù)據(jù)結(jié)構(gòu):ClientInfo
            ClientInfo是服務(wù)端中最重要的數(shù)據(jù)結(jié)構(gòu),服務(wù)端通過它來管理所有的客戶端。客戶端注冊(cè)時(shí),會(huì)保存或生成里面所有的信息,在客戶端請(qǐng)求生成dump的時(shí)候,服務(wù)端就會(huì)通過ClientInfo獲取所有客戶端的信息。ClientInfo中保存了如下信息:

            • 客戶端進(jìn)程pid和句柄
            • 生成Minidump的類型
            • 自定義的客戶端信息
            • 客戶端崩潰的線程ID
            • 客戶端崩潰的信息
            • 客戶端請(qǐng)求崩潰所使用的事件句柄

            這里有一個(gè)問題:在客戶端發(fā)生崩潰時(shí),服務(wù)器如何通過ClientInfo獲取到客戶端的崩潰信息呢?

            客戶端中有幾個(gè)用于保存崩潰信息的變量,在注冊(cè)時(shí),客戶端會(huì)將這幾個(gè)變量的地址發(fā)送至服務(wù)端,服務(wù)端將其保存在ClientInfo中,然后當(dāng)崩潰 發(fā)生的時(shí)候,服務(wù)端就可以通過ReadProcessMemory讀取客戶端中的信息,從而生成dump。這樣做就避免了每次發(fā)生崩潰,都要通過Pipe 將崩潰信息傳遞到服務(wù)端中去了。

            這些變量分別是:崩潰的線程ID,EXCEPTION_POINTERS和MDRawAssertionInfo。
            EXCEPTION_POINTERS和MDRawAssertionInfo的區(qū)別在于,異常崩潰的信息會(huì)被寫入EXCEPTION_POINTERS,非異常崩潰(非法參數(shù)和純虛函數(shù)調(diào)用)的信息會(huì)被寫入MDRawAssertionInfo中。

            dump文件的上傳

            在breakpad的工程中,有一個(gè)工程叫做:crash_report_sender,里面是一個(gè)上傳崩潰文件的類,他的實(shí)現(xiàn)很簡(jiǎn)單,他使用Windows Internet Api來完成dump文件的上傳。
            在使用crash_report_sender時(shí),可以為其指定一個(gè)checkpoint_file。

            1
            explicit CrashReportSender(const wstring &checkpoint_file);

            這個(gè)文件只有一個(gè)作用,就是用來保存上次上傳崩潰的時(shí)間和今天上傳過的崩潰的次數(shù)。通過這個(gè)文件,我們就可以來設(shè)置每日上傳的崩潰的最大數(shù)量。

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            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;
              }
             
              // 上傳文件部分代碼,省略
            }

            調(diào)整每日上傳崩潰的最大數(shù)量的函數(shù)是set_max_reports_per_day。

            需要注意的是:在上傳dump文件的時(shí)候,crash_report_sender并不會(huì)對(duì)dump文件進(jìn)行分析,而是直接上傳整個(gè)dump文件, 如果你需要上傳的dump文件非常大的話,可以考慮把崩潰分析處理的邏輯放入前臺(tái),通過去重或者直接上傳分析結(jié)果,減少上傳的文件大小。

            breakpad存在的問題

            進(jìn)程外生成dump有很多好處,其中最大的好處就是不會(huì)被崩潰進(jìn)程影響,這樣dump的過程就不容易出錯(cuò),但是這樣也有一定的弊端。

            1. 部分崩潰無法抓取
            在一些極端的崩潰,如堆棧溢出之類的崩潰,進(jìn)程外抓取dump有時(shí)候會(huì)失敗。

            2. 無法抓取死鎖或者其他原因?qū)е碌倪M(jìn)程僵死
            breakpad現(xiàn)在沒有檢測(cè)進(jìn)程死鎖的代碼,也沒有在服務(wù)端控制客戶端請(qǐng)求dump的代碼,所以現(xiàn)在breakpad無法抓取死鎖等進(jìn)程僵死的問題。不過因?yàn)閎reakpad的定位是處理崩潰,如果有這種需要的童鞋,可以自行修改breakpad的代碼,添加這些功能。

            3. 對(duì)服務(wù)端有依賴
            如果指定了在使用進(jìn)程外抓取dump,breakpad對(duì)服務(wù)端就有依賴。主要體現(xiàn)在抓取dump時(shí),如果服務(wù)端不存在,客戶端將無法正常抓取dump,甚至有時(shí)會(huì)出現(xiàn)阻塞。

            當(dāng)然對(duì)于這些問題,隨著breakpad的發(fā)展肯定會(huì)越來越完善。如果,你遇到了了這些問題,而又繞過不了,那就改代碼,并且提交給breakpad吧,開源項(xiàng)目就是這么發(fā)展的。

            好,到此breakpad的Windows實(shí)現(xiàn)就已經(jīng)說完了,如果有神馬問題,還請(qǐng)多多指教。謝謝大家。

            北京德勝門中醫(yī)院http://www.0531jsk.com/德勝門中醫(yī)院

            久久亚洲AV无码精品色午夜麻豆 | 99久久国产亚洲综合精品| 精品国产婷婷久久久| 久久久久亚洲AV无码专区桃色| 欧美久久天天综合香蕉伊| 看全色黄大色大片免费久久久| 亚洲综合久久久| 99久久精品免费看国产一区二区三区| 亚洲中文字幕无码一久久区| 久久久国产精品网站| 伊人久久五月天| jizzjizz国产精品久久| 亚洲伊人久久综合中文成人网| 久久精品国产亚洲沈樵| 精品人妻久久久久久888| 久久精品国产只有精品66| 精品综合久久久久久97超人 | 久久无码人妻一区二区三区| 精品乱码久久久久久夜夜嗨| 伊人久久精品无码二区麻豆| 国产精品熟女福利久久AV| 精品一区二区久久久久久久网站| 久久久久综合中文字幕| 国产精品久久久久久福利69堂| 久久久久免费精品国产| 久久性精品| 四虎国产精品免费久久久| 久久亚洲AV成人无码国产| 午夜福利91久久福利| 久久精品国产72国产精福利| 久久91精品国产91久久户| 久久精品一本到99热免费| 久久久久人妻精品一区| 亚洲中文字幕无码久久2017| 热久久视久久精品18| 久久精品国产亚洲5555| 国产精品无码久久四虎| 国产激情久久久久影院| 国产成人精品久久亚洲高清不卡| 成人午夜精品久久久久久久小说| 国产高潮国产高潮久久久|