標籤:
寫一個Windows上的守護進程(7)捕獲異常並產生dump
誰都不能保證自己的代碼不出bug。一旦出了bug,最好是崩潰掉,這樣很快就能被發現,若是不崩潰,只是業務處理錯了,就麻煩了,可能很長時間之後才能被發現。
那麼如果崩潰掉,怎麼查錯呢?
寫過Windows驅動的同學應該知道,一旦崩潰,系統會產生dump檔案,然後就可以根據dump檔案、pdb檔案、源碼用windbg分析了。應用程式層的程式同樣可以在崩潰的時候產生dump檔案,只是沒人幫你完成這個步驟,得自己動手。
1. API
這裡涉及到的主要API就一個:MiniDumpWriteDump——產生dump檔案,我說再多也沒有官方權威,所以要看詳情,還是請移步MSDN。其它輔助API就是設定異常處理器的函數,此處不介紹。
2. 異常類型
讀過《Windows核心編程》的同學應該知道SEH——Windows結構化異常處理,當時我讀了之後,就以為所有的異常都是在SEH體系內的,還接著“以為”了很多年。後來發現並不是:因為我寫的捕獲異常並產生dump檔案的庫並沒有發揮應有的作用,該崩潰的地方還是崩潰了並且沒有產生dump檔案!
google後發現了這麼一篇文章:http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus。
我總結一下:Windows下並非所有的異常都走SEH體系,還有CRT錯誤處理體系;也並不是只有異常才會導致程式退出,接收到Windows支援的幾個訊號也會導致程式退出,比如SIGABRT等等;有的“異常”處理器不是針對進程的,而只對當前線程有效,必須每個線程都設定處理器。
所以,要想捕獲所有的“異常”,就得:
l 調用SetUnhandledExceptionFilter設定SEH異常處理函數
l 調用_set_purecall_handler設定處理純虛函數調用導致的錯誤的處理函數
l 調用_set_new_handler設定new失敗導致的錯誤的處理函數
l 調用_set_invalid_parameter_handler設定無效參數導致的錯誤的處理函數
l 調用signal設定訊號SIGABRT、SIGINT、SIGTERM的處理函數
l 調用set_terminate設定terminate處理函數。這個只對當前線程有效
l 調用set_unexpected設定unexpected處理函數。這個也是只對當前線程有效
l 調用signal設定訊號SIGFPE、SIGILL、SIGSEGV的處理函數。這個也是對當前線程有效
3. 異常資訊擷取
MiniDumpWriteDump 的ExceptionParam需要填充上異常資訊,那麼如何填充呢?
上面那篇文章已經指出了方法:用vc源碼檔案invarg.c中函數_invoke_watson的代碼。
他代碼中用的是vc8.0的源碼,我是用vc2008編譯的,所以就取的vc2008的源碼,但是好像不用在意取哪個vc版本的源碼。
4. 調試dump檔案
我把產生的dump檔案放在了應用程式所在目錄,這樣一眼就可以看出程式有沒有崩潰過。一旦產生了dump檔案,我們只要把二進位、pdb檔案、dump檔案放在一起,然後用vs開啟dump檔案,直接運行就可以調試了。(所以我們提交二進位的時候要把pdb也一同提交。)
我修改了下上面文章中的源碼,封裝成了兩個函數,把我用它的測試代碼測試的結果寫在了代碼中。
我的使用:main函數起來後,設定所有處理函數;在每個任務線程設定線程相關的處理函數。
源碼:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。
2015年11月10日星期二
寫一個Windows上的守護進程(7)捕獲異常並產生dump