1.崩潰分析過程
1.1 確認錯誤碼
不論是用windbg仍是用vs。首先應該注意的是錯誤碼,而90%以上的崩潰都是非法訪問。chrome
在非法訪問時。可以看一下訪問的目標地址。windows
地址是0,或者離0很是近(0x00000008或0xfffffffc)。
通常和空指針相關。假設是一個貌似正常的地址,一般是對象已析構後訪問其數據,或者堆破壞。
1.2確認崩潰相應的C++操做
什麼是確認崩潰相應的C++操做:
比方非法訪問,一般得有個mov指令纔會觸發內存訪問,而後致使崩潰。而mov指針相應於C++的哪一步呢?
比方a->b->c->foo();
在看到源代碼時,會定位於這一行,但是,並不清楚是哪一步訪問失敗。安全
因此這個時候要查看相應彙編代碼。
大概會有好幾個mov,簡單的分析就知道是哪一步時訪問失敗。函數
對編碼的影響:
這就要求,不要在單個語句中寫太複雜的東西比方
x ? b[i] : y > 0 ? c->member[8] : *ptr;
這種代碼崩潰。要還原到錯誤的地方很是難。工具
虛函數調用:
一般
mov edx, dword ptr [ecx]
mov edx, dword ptr [edx+0x??佈局
]
call edx
意味着虛函數調用。每一行均可能是崩潰位置(在call內崩潰時,vs會標註出下一條語句的位置)。
在第一行崩潰意味着拿到一個非法指針,多是空,也可以指向非法地址。post
在第二行崩潰意味着對象已經析構,ecx指向可以訪問,但是值不正確。因此拿到的虛函數表不正確。優化
在最後一行崩潰通常另外一個崩潰棧,但是看不到棧幀,在vs中相應的棧楨顯示一個地址。沒其餘內容。
也通常意味着對象析構。
對象及析構函數:
析構函數是經常發生崩潰的地方,假設沒實用戶提供的析構函數,會定位到幾行彙編。因此沒事,就寫個
析構函數吧,至少能定位到是析構函數。
在析構函數外部還會有一大堆彙編代碼,裏面是對象成員析構的代碼。崩潰在裏面的時候,難以確認
是哪一個對象析構。
假設用指針,在析構函數中主動調用Release或delete,這樣可以顯示調用,不用去猜是誰在析構,固然
用指針或值對象。在邏輯上各有其優勢。在此不表。
假設崩潰位置是call或jmp到某個A::~A()的位置。可以推測到析構的對象的類型是A。
對象析構順序是從後往前,從子類到基類,依據這點,結合崩潰位置。可以推測誰析構。
利用對象佈局,比方成員在對象中的偏移,可能有得於推測誰析構出問題。
可以人爲地在對象佈局中引入一些填充的字節,使得能看到this對象(線上崩潰沒有堆上的數據,因爲
截取fulldump並上報有操做上的難度。因此this指向堆上時可能看不到,而在棧上則有可能),有利於分析。
還原上下文:
線上拿到的dump的信息少,不能調試。因此可以依據崩潰所在模塊。崩潰在模塊中的偏移量,在本地
調試相應的bin,找到相應的模塊。偏移量。打上斷點,可以在本地還原出崩潰時的運行環境。this
固然,
在本地運行到相應位置時不必定崩潰,但是,有了不少其它上下文信息。可以比較easy肯定相應的C++操做。編碼
使用IDA:
可以使用IDA讓彙編代碼更好看,較easy分析流程。
關閉alsr。指定建議模塊載入地址:
這樣可能使得更easy分析。
但是會使得安全性減小,可以用於小流量版本號。
ln指令:
windbg中ln指令可以依據地址。還原出相應的信息,比方該地址是在某個類的某個方法中。
有時可能會
還原出幾個信息:A::foo() + 0x?
?, B::foo1() + 0x??。這就需要本身依據上下文推斷了。
代碼優化:
代碼優化使得分析更難,可以嘗試改變一些編譯選項,減小優化級別,保留棧楨,關閉應用程序全局優化,
使得在release下的分析easy些。
所見並非真實:
windbg和vs看到的棧幀多是假的:可能在中間某一些可能已經亂了,可能棧楨省略使得vs分析的結果不正確。
(一般是vs分析得不正確。另外也有windbg杯具的時候)
對於在中間已經亂掉的棧。可以依據返回地址。棧參數,棧楨省略數據等,又一次還原出棧。只是在90%的狀況
下,即便還原出來,也不知道下一步怎麼辦。
對象還原:
線上崩潰沒有堆,可以將感興趣的對象拷貝複製到棧上(本身得控制深拷貝)。而後崩潰上報中就可以看到
對象的狀態了。
(注意代碼優化可能使得拷貝無效)
1.3C++上的邏輯
在肯定崩潰和C++操做的關係後,就是本身邏輯上的問題了,基本上能遇到的問題都是對象生命週期管理
不當,進而形成非法訪問。
指針的判空能規避一處的非法訪問,但是可以把錯誤進一步擴散。指針判空。且用且珍惜。
在設計或編碼時,應當考慮代碼的可調試性。比方chromium中的線程池中,加入任務時,會生成當前調用
信息。和task綁定,以使於定位錯誤。
1.4堆破壞
基本無解,崩潰現場和引入錯誤的點相差太遠。
僅僅能盡人事,聽天命了。
比方,開一下頁堆。存在必定機率使得崩潰出現,看人品。
比方。換一個CRT堆。或者本身寫個,加強錯誤檢測。
比方。CRT自己。尤爲是調試的堆,堆上有些填充信息。使得在看到的時候或多或少嘆口氣:大概認識這些
填充信息,想要不少其它的信息,難啊。。
。
比方。可以本身寫個調試器,本身插入頁堆,或者使用系統的頁堆。使得檢測本身主動化,而後經過大規模數據
使之重現。
2.其餘
多進程調試:
可以經過在
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
創建關心的進程名的項。填上debugger鍵值,值爲調試器路徑,使得進程建立時就attach。(gflags也是
改這裏)。
但是問題是,有的模塊是按需載入的,在這個時候還不能在相應模塊中下斷點。
另外可以本身在關心的位置加上MessageBox或ATLASSERT之類的代碼,等彈出對話框時再attach到相應進程。
activex:
attach方法同理。
但是,IE的多進程模型會使得attach不方便。
在IE9及以上,其進程模型是一個主進程,控制多個tab進程,按必定規則建立tab進程,將任務分派到
tab上。同一個網頁打開兩次,可能分配到不一樣的進程上,也多是一樣的進程。在同一個進程中,同一
個activex可能有多個實例,而且每個實例相應的主線程還不必定是同一個線程。
一般會控制僅僅開一個tab,使得調試更加easy。另外可以開多tab。而後關閉,而後再開,來測試同一個進程
有多外activex實例的狀況。更進一步,可以本身調用IWebBrowser2,來模擬不少其它的狀況。
np插件:
chrome中這個簡單些。一個插件一個進程,多個實例。共享主線程。
還有些開源工具將activex適配爲np插件,使得可以在chrome中調用ax。調試。
多機調試:
前面說過,windbg,vs都支持。
死鎖:
死鎖現場不會是線上問題(可以經過必定手段,使得在線上發生死鎖時報告。但是基本上沒用過,相應的
手段在線下有玩過)。線下問題一般會有現場,或者能拿到full dump。
通常使用windbg來看,用~*kb或者
這系列的命令看看線程都在幹什麼。而重點關注的則是WaitForSingleObjectEx之類的調用。
經過分析調用
相應的參數。進一步能還原出,拿到 分發器 對象後不歸還的線程是誰。
或者也可以!runaway找到佔用CPU 高的線程,而後看看該線程在幹什麼。線程循環等待,則死鎖了。線程一直在那裏跑,多是死循環了。 線下的死鎖檢測,通常可以向主線程發一個消息來實現。 warning:有可能某個線程拿到分發器對象,但是該線程已經掛掉了。