屏分析實(shí)戰(zhàn):從DMP文件到錯(cuò)誤碼解讀)
有一次幫朋友排查筆記本藍(lán)屏事件查看器里只有一個(gè)Kernel-Power 41設(shè)備管理器看著一切正常內(nèi)存跑了幾輪也沒報(bào)錯(cuò)。后來我用WinDbg打開崩潰時(shí)生成的DMP文件兩分鐘就定位到了問題某型號(hào)無線網(wǎng)卡的舊驅(qū)動(dòng)在DMA寫入時(shí)訪問了無效地址更新固件之后再?zèng)]出現(xiàn)過。藍(lán)屏分析這件事繞不開WinDbg——它是微軟官方調(diào)試器能把系統(tǒng)崩潰那一刻的現(xiàn)場(chǎng)完整還原出來。這篇是這個(gè)系列的第一篇不談玄學(xué)只講落地的實(shí)用技巧從環(huán)境準(zhǔn)備到第一份DMP的完整分析流程再到高頻錯(cuò)誤碼的解讀套路和常見誤判。如果你是第一次接觸藍(lán)屏分析或者之前只會(huì)用事件查看器和BlueScreenView這類工具這篇正好適合你。后面我會(huì)盡量用“拿到問題→怎么查→怎么確認(rèn)”的順序來寫把我實(shí)際分析過程中的經(jīng)驗(yàn)和踩過的坑都攤開講。1. 為什么事件查看器不夠用藍(lán)屏分析為什么要死磕WinDbg1.1 藍(lán)屏其實(shí)是一次“內(nèi)核級(jí)的停機(jī)報(bào)告”Windows藍(lán)屏的正式名字叫BugCheck本質(zhì)上是內(nèi)核檢測(cè)到系統(tǒng)已經(jīng)無法安全繼續(xù)運(yùn)行調(diào)用了KeBugCheckEx主動(dòng)停機(jī)。停機(jī)之前系統(tǒng)會(huì)把當(dāng)前內(nèi)存中的關(guān)鍵信息寫入轉(zhuǎn)儲(chǔ)文件也就是我們常說的DMP文件。這里要糾正一個(gè)很常見的誤解事件查看器里那條Kernel-Power 41并不是藍(lán)屏的原因它只是“上一次關(guān)機(jī)不正?!钡慕Y(jié)果。真正有價(jià)值的信息在BugCheck事件里也就是Event ID 1001它會(huì)記錄藍(lán)屏的代碼和四個(gè)參數(shù)比如0x000000D1這一類。但光看這串?dāng)?shù)字你很難知道問題出在哪個(gè)驅(qū)動(dòng)、哪個(gè)操作上。這就像一輛車突然熄火儀表盤只告訴你“發(fā)動(dòng)機(jī)故障”但你是想查火花塞、油路還是傳感器必須把行車電腦的數(shù)據(jù)導(dǎo)出來逐條看。DMP文件就是那臺(tái)行車電腦的數(shù)據(jù)WinDbg就是讀取這個(gè)數(shù)據(jù)的設(shè)備。1.2 事件查看器和第三方工具的局限性市面上確實(shí)有一堆號(hào)稱一鍵分析藍(lán)屏的工具比如BlueScreenView、WhoCrashed我也用過它們能幫你快速看到錯(cuò)誤代碼、崩潰模塊、堆棧摘要應(yīng)急夠用。但它們的短板也很明顯只能展示W(wǎng)inDbg分析結(jié)果中很小的一部分尤其在多驅(qū)動(dòng)協(xié)同崩潰、內(nèi)存池?fù)p壞這類復(fù)雜場(chǎng)景下幾乎幫不上忙。對(duì)比一下就知道工具能做什么局限事件查看器顯示BugCheck代碼和四個(gè)參數(shù)不解釋參數(shù)含義看不到調(diào)用棧BlueScreenView讀取DMP的摘要信息顯示崩潰模塊沒有符號(hào)解析能力無法交叉驗(yàn)證WhoCrashed自動(dòng)給出可能原因誤判率高經(jīng)常把系統(tǒng)驅(qū)動(dòng)當(dāng)元兇WinDbg符號(hào)化調(diào)用棧、完整模塊列表、參數(shù)解釋、內(nèi)存池分析有學(xué)習(xí)成本但是可追溯的完整證據(jù)鏈我用WinDbg分析DMP核心是它能給我一條完整的證據(jù)鏈不僅是“哪個(gè)模塊崩潰了”還包括它在什么IRQL下、訪問了什么地址、調(diào)用了什么函數(shù)、當(dāng)前進(jìn)程是什么、驅(qū)動(dòng)文件的版本和時(shí)間戳是多少。把這些拼起來才能判斷是驅(qū)動(dòng)bug、硬件故障還是內(nèi)存被踩踏的連鎖反應(yīng)。2. 第一次動(dòng)手前選對(duì)工具、配好符號(hào)、確認(rèn)轉(zhuǎn)儲(chǔ)文件2.1 WinDbg版本怎么選經(jīng)典版、Preview還是最新版現(xiàn)在WinDbg實(shí)際上有三個(gè)常見形態(tài)很多人一搜“windbg下載”就懵了。簡單說經(jīng)典WinDbg包含在Windows SDK里界面經(jīng)典命令兼容性最好在老舊系統(tǒng)調(diào)試場(chǎng)景下仍然能打。WinDbg Preview微軟商店里上架的新版現(xiàn)在直接叫WinDbg界面現(xiàn)代化了一點(diǎn)支持暗色主題底部的Command窗口仍是主戰(zhàn)場(chǎng)。官方最新WinDbg繼續(xù)以商店方式更新分析性能更好支持更多擴(kuò)展命令。我的建議是直接用新版WinDbg從微軟商店裝一個(gè)就行。命令基本兼容偶爾有擴(kuò)展命令差異網(wǎng)上資料大多數(shù)都能對(duì)上。經(jīng)典版可以留著萬一遇到老系統(tǒng)的轉(zhuǎn)儲(chǔ)兩邊對(duì)照著用。有人會(huì)糾結(jié)漢化包的問題。我個(gè)人建議是不要用漢化——藍(lán)屏分析真正高頻操作的還是那幾十條命令菜單就那么幾個(gè)漢化了反而對(duì)不上社區(qū)資料里的術(shù)語。2.2 符號(hào)路徑?jīng)]有符號(hào)的WinDbg等于廢了一半符號(hào)文件.pdb是WinDbg的靈魂。DMP文件里記錄的是一堆虛擬地址沒有符號(hào)的話WinDbg只能告訴你“某個(gè)模塊調(diào)用了另一個(gè)模塊”卻無法顯示函數(shù)名調(diào)用棧全是問號(hào)。這相當(dāng)于你拿到一份地址列表但沒有門牌簿根本不知道每扇門背后是誰。正確做法是配置微軟公共符號(hào)服務(wù)器讓W(xué)inDbg按需自動(dòng)下載。標(biāo)準(zhǔn)符號(hào)路徑是SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols在WinDbg里按CtrlS打開符號(hào)路徑設(shè)置或者在命令窗口直接輸入.sympath SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols也可以設(shè)置環(huán)境變量_NT_SYMBOL_PATH這樣對(duì)后續(xù)所有調(diào)試會(huì)話都生效。首次分析時(shí)符號(hào)下載會(huì)慢一些尤其碰到系統(tǒng)大模塊耐心等就行。如果你在公司內(nèi)網(wǎng)或離線環(huán)境符號(hào)下載不了就找一個(gè)有網(wǎng)的機(jī)器把符號(hào)緩存整個(gè)C:\Symbols目錄復(fù)制過去也能用。更穩(wěn)妥的方式是用symchk預(yù)先拉取目標(biāo)模塊的符號(hào)。2.3 轉(zhuǎn)儲(chǔ)文件從哪里來Minidump、MEMORY.DMP與轉(zhuǎn)儲(chǔ)參數(shù)DMP文件一般有兩個(gè)位置C:\Windows\Minidump小內(nèi)存轉(zhuǎn)儲(chǔ)通常幾百KB到幾MB包含崩潰時(shí)的關(guān)鍵內(nèi)存和調(diào)用棧日常分析最快。C:\Windows\MEMORY.DMP完整或內(nèi)核內(nèi)存轉(zhuǎn)儲(chǔ)可能有幾個(gè)GB信息最全但分析起來也慢。如果你想系統(tǒng)在藍(lán)屏?xí)r穩(wěn)定生成轉(zhuǎn)儲(chǔ)去“系統(tǒng)屬性→高級(jí)→啟動(dòng)和故障恢復(fù)→設(shè)置”里檢查一下。建議把“寫入調(diào)試信息”留成“自動(dòng)內(nèi)存轉(zhuǎn)儲(chǔ)”或者按需改成“小內(nèi)存轉(zhuǎn)儲(chǔ)(256KB)”。還有兩個(gè)細(xì)節(jié)建議關(guān)掉“自動(dòng)重新啟動(dòng)”否則藍(lán)屏一閃就重啟現(xiàn)場(chǎng)信息丟失來不及拍照記錄錯(cuò)誤碼。轉(zhuǎn)儲(chǔ)寫入依賴頁面文件頁面文件太小會(huì)導(dǎo)致轉(zhuǎn)儲(chǔ)生成失敗。想讓藍(lán)屏分析可靠系統(tǒng)盤頁面文件至少留2GB以上最好是“系統(tǒng)管理的大小”。如果你發(fā)現(xiàn)Minidump目錄是空的先別急著懷疑“沒藍(lán)屏過”。去系統(tǒng)盤根目錄看有沒有MEMORY.DMP那個(gè)同樣能分析。還有可能是之前的轉(zhuǎn)儲(chǔ)被清理工具刪了或者是Windows在快速啟動(dòng)機(jī)制下沒有完成轉(zhuǎn)儲(chǔ)寫入。2.4 先對(duì)齊時(shí)間用事件日志給DMP文件“洗底”很多人拿到DMP就急著拖進(jìn)WinDbg我建議你先做一個(gè)60秒的準(zhǔn)備工作打開事件查看器找到“Windows日志→系統(tǒng)”篩選Event ID 1001可以看到每次BugCheck的時(shí)間和錯(cuò)誤代碼。然后把C:\Windows\Minidump里的文件按修改時(shí)間排序挑出對(duì)應(yīng)時(shí)間的那個(gè)。這一步看著簡單其實(shí)作用很大。它能避免你分析了一份過期的轉(zhuǎn)儲(chǔ)——尤其是那種“一年前藍(lán)屏過一次今天又藍(lán)屏”的情況兩個(gè)DMP可能指向完全不同的原因。先對(duì)齊時(shí)間再?zèng)Q定分析哪份文件分析結(jié)論才靠譜。如果你發(fā)現(xiàn)Minidump里有十幾個(gè)文件而用戶只描述“最近經(jīng)常藍(lán)屏”那就先看最新的一份。但不要只看一份后面我會(huì)講到多份轉(zhuǎn)儲(chǔ)之間的橫向?qū)Ρ韧葐畏萆钔诟菀妆┞缎判摹?. 拿到DMP后的五個(gè)核心步驟從!analyze -v到證據(jù)鏈閉合3.1 第一步打開轉(zhuǎn)儲(chǔ)文件先看系統(tǒng)版本摘要在WinDbg里打開DMP文件菜單路徑是File → Start debugging → Open dump file最新版叫Open Dump File直接選中.dmp文件即可。也可以用命令行windbg -z C:\Windows\Minidump\081318-23451-01.dmp打開之后WinDbg會(huì)自動(dòng)加載符號(hào)輸出窗口會(huì)先顯示目標(biāo)系統(tǒng)的版本、構(gòu)建號(hào)、CPU數(shù)量。別直接跳過這一段因?yàn)槟闶紫纫_認(rèn)的是這份轉(zhuǎn)儲(chǔ)來自哪個(gè)Windows版本是不是這臺(tái)機(jī)器的有些轉(zhuǎn)儲(chǔ)可能是別的機(jī)器拷貝來的構(gòu)建號(hào)對(duì)不上后續(xù)分析會(huì)出現(xiàn)誤導(dǎo)。比如輸出里出現(xiàn)Windows 10 Kernel Version 19041說明這是2004版或之后的系統(tǒng)。構(gòu)建號(hào)對(duì)分析有參考價(jià)值——某些BugCheck只在特定構(gòu)建號(hào)上出現(xiàn)你可以在社區(qū)搜“構(gòu)建號(hào)錯(cuò)誤碼”往往能直接找到已知問題。3.2 第二步執(zhí)行!analyze -v先看它怎么說假設(shè)你已經(jīng)看到了熟悉的BugCheck字樣下一步就是在命令窗口輸入!analyze -v這是WinDbg最核心的自動(dòng)分析命令。它會(huì)解析DMP中的關(guān)鍵信息輸出這樣一段內(nèi)容BugCheck D1, {fffff8800a112340, 0000000000000002, 0000000000000008, fffff8800a10f510} DRIVER_IRQL_NOT_LESS_OR_EQUAL Probably caused by : Netwtw04.sys這里有三塊信息要先看BugCheck行錯(cuò)誤代碼和四個(gè)參數(shù)。參數(shù)的解釋在后面細(xì)講。Probably caused byWinDbg給出的推測(cè)模塊。注意“probably”這個(gè)詞它不是結(jié)論是線索。下面的STACK_TEXT崩潰時(shí)的調(diào)用棧這是后續(xù)人工確認(rèn)的重點(diǎn)。!analyze -v還會(huì)輸出IMAGE_NAME、MODULE_NAME、FAILURE_BUCKET_ID等字段。新手最容易犯的錯(cuò)誤是把MODULE_NAME當(dāng)作最終結(jié)論直接寫在報(bào)告里但我的經(jīng)驗(yàn)是它至少有30%的概率會(huì)把你的注意力帶偏尤其遇到符號(hào)不全或棧回溯被截?cái)嗟那闆r。3.3 第三步參數(shù)和棧回溯怎么交叉驗(yàn)證如果!analyze -v的輸出已經(jīng)很明顯比如?;厮堇镞B續(xù)出現(xiàn)同一個(gè)第三方驅(qū)動(dòng)那基本可以下判斷。但更常見的情況是“似乎指向A驅(qū)動(dòng)又有點(diǎn)牽扯B驅(qū)動(dòng)”這時(shí)候必須做交叉驗(yàn)證。執(zhí)行.ecxr kb.ecxr會(huì)把調(diào)試上下文切換到異常發(fā)生的那個(gè)線程kb顯示它的調(diào)用棧。這一步很關(guān)鍵因?yàn)?analyze -v給出的?;厮萦袝r(shí)是自動(dòng)判斷的不一定是最完整的現(xiàn)場(chǎng)。切換上下文之后再展開往往能多看幾層看到崩潰前到底調(diào)用了什么。如果?;厮堇锍霈F(xiàn)大量系統(tǒng)模塊比如nt!KeBugCheckEx、nt!KiBugCheckDispatch別緊張那是藍(lán)屏的固定路徑真正要盯的是棧里靠下的位置——那個(gè)觸發(fā)異常的具體模塊。再配合lmvm Netwtw04lmvm會(huì)顯示該模塊的詳細(xì)信息包括文件版本、時(shí)間戳、符號(hào)加載狀態(tài)。驅(qū)動(dòng)的時(shí)間戳尤其重要如果崩潰模塊是個(gè)兩年前的舊驅(qū)動(dòng)而它又指向了一個(gè)已知的硬件bug那結(jié)論就非常扎實(shí)。3.4 第四步查錯(cuò)誤碼的參數(shù)含義區(qū)分根因類型BugCheck代碼底下的四個(gè)參數(shù)不是擺設(shè)它們是區(qū)分根因的關(guān)鍵。以0x000000D1為例常見含義是參數(shù)1被訪問的內(nèi)存地址參數(shù)2中斷請(qǐng)求級(jí)別IRQL參數(shù)3操作類型0表示讀1表示寫8表示執(zhí)行參數(shù)4引用該內(nèi)存的指令地址如果你發(fā)現(xiàn)參數(shù)1是個(gè)低地址比如0x00000005之類的大概率是空指針解引用如果參數(shù)3是1寫操作說明驅(qū)動(dòng)在往一個(gè)不該寫的地方寫數(shù)據(jù)如果參數(shù)2非常高比如IRQL等于2以上那就要關(guān)注驅(qū)動(dòng)是否在錯(cuò)誤的IRQL上執(zhí)行了分頁內(nèi)存訪問。這里要提醒一下不同Windows版本的參數(shù)含義可能有細(xì)微差別。拿不準(zhǔn)的時(shí)候去微軟文檔里搜“Bug Check 0xD1”或者“Bug Check 0x50”查對(duì)應(yīng)頁面比盲目猜要靠譜得多。3.5 第五步用模塊列表和驅(qū)動(dòng)時(shí)間戳補(bǔ)全證據(jù)鏈?;厮堇镏傅侥硞€(gè)驅(qū)動(dòng)這只是“最后一步踩空”。真正重要的是搞清楚“為什么它會(huì)踩空”。這時(shí)候要看整個(gè)系統(tǒng)的驅(qū)動(dòng)加載情況lm t n這條命令會(huì)列出所有已加載的內(nèi)核模塊和驅(qū)動(dòng)。重點(diǎn)看第三方驅(qū)動(dòng)比如網(wǎng)卡、顯卡、虛擬化軟件、安全軟件的驅(qū)動(dòng)。很多時(shí)候崩潰棧里看到的驅(qū)動(dòng)并不是問題的源頭——源頭可能是另一個(gè)驅(qū)動(dòng)破壞了內(nèi)存池然后崩潰正好發(fā)生在第三個(gè)驅(qū)動(dòng)里。我的判斷順序是這樣的先用!analyze -v拿到候選模塊再用lmvm確認(rèn)候選模塊的版本和時(shí)間戳?xí)r間戳太舊是重大嫌疑然后用.ecxrkb看完整調(diào)用棧確認(rèn)崩潰路徑最后lm t n檢查全局驅(qū)動(dòng)列表看有沒有其他老版本驅(qū)動(dòng)或已知問題驅(qū)動(dòng)存在。這一套走完結(jié)論通常就比較穩(wěn)了比單看一行Probably caused by可靠得多。4. 高頻藍(lán)屏代碼實(shí)戰(zhàn)五個(gè)錯(cuò)誤碼的分析套路4.1 0x0000000A / 0xD1 這類驅(qū)動(dòng)內(nèi)存訪問違規(guī)怎么處理0x0000000AIRQL_NOT_LESS_OR_EQUAL和0x000000D1DRIVER_IRQL_NOT_LESS_OR_EQUAL本質(zhì)上是一家人都是在過高的IRQL下訪問了可分頁內(nèi)存導(dǎo)致內(nèi)核無法繼續(xù)。區(qū)別是0x0A可以發(fā)生在任意內(nèi)核代碼里0xD1則明確指向驅(qū)動(dòng)。遇到這類錯(cuò)誤碼我的套路是先看參數(shù)3如果操作類型是寫1重點(diǎn)查驅(qū)動(dòng)釋放內(nèi)存后是否仍在寫也就是懸垂指針再看參數(shù)2的IRQL值如果大于PASSIVE_LEVEL0而?;卣{(diào)用到了分頁內(nèi)存那就是驅(qū)動(dòng)在錯(cuò)誤的時(shí)間做了錯(cuò)誤的事最后把崩潰棧里的模塊和時(shí)間戳記錄下來去網(wǎng)上搜“模塊名錯(cuò)誤碼”看是否已知問題。這條排查鏈路特別適合網(wǎng)卡、聲卡驅(qū)動(dòng)的藍(lán)屏。很多人遇到0xD1第一時(shí)間懷疑內(nèi)存硬件但我經(jīng)手的案例里相當(dāng)大比例是驅(qū)動(dòng)bug。4.2 0x00000050 PAGE_FAULT_IN_NONPAGED_AREA的問題別急著換內(nèi)存0x50的意思是系統(tǒng)訪問了一個(gè)無效物理地址比如引用了已被釋放的頁面、訪問了不存在的物理內(nèi)存或者對(duì)不可分頁區(qū)域執(zhí)行了分頁操作。錯(cuò)誤信息里經(jīng)常看到一堆nt!Mi開頭的函數(shù)很容易讓人以為是內(nèi)存條壞了。但這里有個(gè)經(jīng)典誤判0x50也可能是驅(qū)動(dòng)“釋放內(nèi)存后繼續(xù)使用”造成的。判斷方式有兩個(gè)看崩潰棧中是否有第三方驅(qū)動(dòng)在ExFreePool之后又訪問了同一個(gè)地址看參數(shù)1錯(cuò)誤的內(nèi)存地址是否接近驅(qū)動(dòng)常用的池地址范圍。如果棧回溯里只有nt內(nèi)核函數(shù)沒有任何第三方模塊那硬件嫌疑才上升。這種情況下我會(huì)建議先跑MemTest86再檢查CPU的IMC內(nèi)存控制器和BIOS里的XMP設(shè)置最后才是換內(nèi)存。4.3 0x0000003B SYSTEM_SERVICE_EXCEPTION異常代碼是關(guān)鍵0x3B表示在執(zhí)行系統(tǒng)服務(wù)時(shí)發(fā)生了未處理的異常。這類藍(lán)屏經(jīng)常和顯卡驅(qū)動(dòng)、存儲(chǔ)驅(qū)動(dòng)、安全軟件驅(qū)動(dòng)有關(guān)。它的四參數(shù)里第一個(gè)就是異常代碼比如0xc0000005訪問沖突、0xc000000d非法指令后面是異常發(fā)生的指令地址。我的分析重點(diǎn)是看?;厮葜谐霈F(xiàn)的是哪個(gè)模塊。如果指向dxgkrnl.sys、nvlddmkm.sys、athwb.sys這類圖形或網(wǎng)絡(luò)驅(qū)動(dòng)大概率是驅(qū)動(dòng)在處理請(qǐng)求時(shí)越界。如果?;厮堇锶窍到y(tǒng)模塊那可能是系統(tǒng)服務(wù)出現(xiàn)了堆損壞需要進(jìn)一步檢查是否有其他驅(qū)動(dòng)破壞了堆。補(bǔ)充一條經(jīng)驗(yàn)0x3B的崩潰現(xiàn)場(chǎng)經(jīng)常發(fā)生在系統(tǒng)負(fù)載高、USB設(shè)備插拔頻繁的時(shí)間點(diǎn)排查時(shí)不妨問問用戶“藍(lán)屏前在做什么”往往能幫你快速縮小范圍。4.4 0x0000001A MEMORY_MANAGEMENT內(nèi)存池?fù)p壞的排查方向0x1A是內(nèi)存管理器檢查到內(nèi)部狀態(tài)不一致比如頁表項(xiàng)被寫壞、PFN列表被破壞。這類錯(cuò)誤一出很多人直接判定“內(nèi)存條壞了”但我更愿意先把它當(dāng)“內(nèi)存被驅(qū)動(dòng)踩了”來查。!analyze -v輸出里會(huì)有一個(gè)子錯(cuò)誤碼Arg1不同子碼對(duì)應(yīng)不同的內(nèi)存損壞場(chǎng)景??吹?x1A時(shí)我會(huì)額外執(zhí)行!memanalysis或者用!pool這兩個(gè)命令會(huì)掃描內(nèi)存池的標(biāo)記和潛在損壞。如果某個(gè)驅(qū)動(dòng)名的池標(biāo)記反復(fù)出現(xiàn)那基本可以斷定是這個(gè)驅(qū)動(dòng)在池上亂寫導(dǎo)致內(nèi)存管理結(jié)構(gòu)錯(cuò)亂。這時(shí)候換內(nèi)存條解決不了問題該更新驅(qū)動(dòng)、卸載沖突軟件才對(duì)。4.5 0x000000EF CRITICAL_PROCESS_DIED先查系統(tǒng)再查驅(qū)動(dòng)0xEF表示系統(tǒng)關(guān)鍵進(jìn)程意外退出比如wininit.exe、csrss.exe、services.exe。這類問題要分兩步看第一步確認(rèn)是哪個(gè)進(jìn)程死了、退出碼多少第二步看這個(gè)進(jìn)程死前在跑什么是不是某個(gè)驅(qū)動(dòng)引發(fā)的。參數(shù)1通常指向進(jìn)程對(duì)象參數(shù)2是退出碼。比如退出碼是0xc0000409棧緩沖區(qū)溢出那就要重點(diǎn)查是否有安全軟件或反作弊軟件在注入該進(jìn)程。0xEF還有一個(gè)常見背景是系統(tǒng)文件被破壞或系統(tǒng)盤故障我一般會(huì)先建議跑sfc /scannow和chkdsk /f再回來分析驅(qū)動(dòng)因素。0xEF比較麻煩的一點(diǎn)是進(jìn)程死了的現(xiàn)場(chǎng)往往很干凈?;厮堇锊灰欢芸吹皆獌茨K。這時(shí)候我會(huì)直接對(duì)比前后多份轉(zhuǎn)儲(chǔ)看是否有同一個(gè)驅(qū)動(dòng)反復(fù)出現(xiàn)在崩潰線程的模塊列表里。5. 踩坑記錄符號(hào)加載失敗、誤報(bào)驅(qū)動(dòng)與轉(zhuǎn)儲(chǔ)缺失的排查鏈路5.1 符號(hào)加載失敗別再被“問號(hào)堆棧”帶偏我第一次用WinDbg分析DMP時(shí)打開文件后?;厮萑???還以為轉(zhuǎn)儲(chǔ)文件壞了。實(shí)際上就是符號(hào)沒配好。排查鏈路是這樣的先輸入.sympath確認(rèn)當(dāng)前符號(hào)路徑看緩存目錄寫沒寫對(duì)檢查C:\Symbols目錄是否正在增長如果文件數(shù)在漲說明符號(hào)在下載確認(rèn)網(wǎng)絡(luò)能訪問符號(hào)服務(wù)器公司內(nèi)網(wǎng)或防火墻經(jīng)常會(huì)攔如果路徑?jīng)]問題但個(gè)別模塊還是問號(hào)執(zhí)行.reload /f強(qiáng)制重載一次仍不行就人工檢查目標(biāo)模塊的符號(hào)包去微軟符號(hào)服務(wù)器對(duì)應(yīng)頁面手動(dòng)下載。符號(hào)加載失敗時(shí)!analyze -v的輸出質(zhì)量會(huì)直線下降可能連Probably caused by都打不出來。所以遇到問號(hào)堆棧第一件事不是懷疑DMP損壞而是查符號(hào)。5.2 懷疑驅(qū)動(dòng)A卻崩在驅(qū)動(dòng)Bprobable cause的“偽證”時(shí)刻有一次一份0x50轉(zhuǎn)儲(chǔ)!analyze -v明確指向某安全軟件的監(jiān)控驅(qū)動(dòng)時(shí)間戳也合理?xiàng);厮堇镆泊_實(shí)有它。我當(dāng)時(shí)差點(diǎn)直接下結(jié)論讓用戶卸載。后來多看了兩眼發(fā)現(xiàn)真正引發(fā)崩潰的是一個(gè)磁盤過濾驅(qū)動(dòng)它釋放了一塊池內(nèi)存之后沒有把指針置空另一個(gè)模塊在用同一個(gè)指針時(shí)踩到了被釋放的區(qū)域最終崩在了安全軟件驅(qū)動(dòng)的地盤上。這個(gè)案例給我最大的教訓(xùn)是Probably caused by是“崩潰時(shí)正好在這”不代表“根因一定在這”?,F(xiàn)在我的習(xí)慣是每次得出結(jié)論前至少驗(yàn)證三處lmvm的時(shí)間戳、.ecxrkb的完整棧、全局驅(qū)動(dòng)列表里是否有異常驅(qū)動(dòng)。三者指向同一模塊才敢寫最終判斷。5.3 轉(zhuǎn)儲(chǔ)文件缺失或打不開先檢查這兩種常見原因打不開DMP最常見的原因是架構(gòu)不匹配。比如你用64位WinDbg去打開32位系統(tǒng)生成的內(nèi)核轉(zhuǎn)儲(chǔ)會(huì)報(bào)錯(cuò)或者顯示一堆亂碼。解決辦法是明確轉(zhuǎn)儲(chǔ)來源系統(tǒng)的架構(gòu)下載對(duì)應(yīng)x64或x86的調(diào)試器。轉(zhuǎn)儲(chǔ)文件缺失則要回系統(tǒng)設(shè)置里查重點(diǎn)看“啟動(dòng)和故障恢復(fù)”中的“寫入調(diào)試信息”選項(xiàng)。還有一個(gè)容易忽略的點(diǎn)系統(tǒng)盤的頁面文件必須足夠大否則藍(lán)屏瞬間寫轉(zhuǎn)儲(chǔ)失敗。有些優(yōu)化軟件為了省磁盤空間會(huì)把頁面文件設(shè)置得特別小這會(huì)導(dǎo)致藍(lán)屏了卻什么也沒留下。如果你在做批量電腦維護(hù)記得把這個(gè)選項(xiàng)單獨(dú)檢查一遍別等藍(lán)屏了才發(fā)現(xiàn)根本沒轉(zhuǎn)儲(chǔ)文件可分析。5.4 硬件問題濾鏡判斷內(nèi)存故障前先排除驅(qū)動(dòng)踩踏很多藍(lán)屏代碼尤其是0x1A、0x50、0x0A這一類表面看起來都像內(nèi)存問題。我看過太多的排查報(bào)告上來就寫“內(nèi)存故障請(qǐng)更換內(nèi)存條”。但如果你往深挖一層會(huì)發(fā)現(xiàn)不少案子里驅(qū)動(dòng)踩內(nèi)存才是根源。我的個(gè)人判斷順序是這樣先用!analyze -v和棧回溯排除驅(qū)動(dòng)因素如果確認(rèn)棧里全是系統(tǒng)模塊再考慮運(yùn)行!memanalysis看內(nèi)存池標(biāo)記內(nèi)存池也干凈才輪到硬件方向這時(shí)候建議先跑MemTest86和CPU壓力測(cè)試再做替換法。反過來也有一種情況?;厮堇锎_實(shí)指向某個(gè)驅(qū)動(dòng)模塊但這個(gè)模塊的行為異常是硬件錯(cuò)誤引發(fā)的比如CPU緩存或內(nèi)存控制器出錯(cuò)導(dǎo)致驅(qū)動(dòng)讀到錯(cuò)誤數(shù)據(jù)。這種場(chǎng)景l(fā)mvm時(shí)間戳再新也說不清。這時(shí)需要結(jié)合系統(tǒng)事件日志里有沒有WHEA硬件錯(cuò)誤記錄來判斷如果同時(shí)出現(xiàn)Event 18、Event 19這類WHEA事件那硬件因素就得優(yōu)先考慮。5.5 命令拆彈內(nèi)核轉(zhuǎn)儲(chǔ)大、分析慢時(shí)的高效技巧如果只拿到了幾個(gè)GB的MEMORY.DMP打開和波動(dòng)都很痛苦。我的建議是先用小內(nèi)存轉(zhuǎn)儲(chǔ)Minidump做快速定位絕大多數(shù)場(chǎng)景小轉(zhuǎn)儲(chǔ)的信息足夠如果一定要用完整轉(zhuǎn)儲(chǔ)打開后先輸!analyze -v別急著展開其他命令讓它慢慢跑完分析過程中可以用.logopen把輸出重定向到文件避免窗口滾動(dòng)丟失內(nèi)容遇到反復(fù)崩潰的機(jī)器別滿足于分析最新的那份把最近三五個(gè)DMP都過一遍看看崩潰模塊是否一致。如果每次都指向同一個(gè)驅(qū)動(dòng)這個(gè)證據(jù)比任何單次現(xiàn)場(chǎng)都更有說服力。另外微軟文檔里很多BugCheck頁面下面有!analyze -show的用法它可以重新顯示自動(dòng)分析結(jié)果。如果你在分析過程中把輸出刷掉了不用重新跑一遍自動(dòng)分析直接執(zhí)行這個(gè)命令就能把結(jié)論調(diào)回來。我自己還有一個(gè)習(xí)慣分析完成之后把!analyze -v、lmvm和相關(guān)?;厮莸奈谋径急4娴饺罩纠镂募伞叭掌赺BugCheck代碼_核心模塊.txt”。這樣后續(xù)再藍(lán)屏直接翻舊檔對(duì)比能省不少時(shí)間。這個(gè)系列下一篇我打算重點(diǎn)講怎么在一堆DMP里做橫向?qū)Ρ纫约叭绾螐谋罎⒓?xì)節(jié)反推“是驅(qū)動(dòng)更新問題還是硬件退化問題”。先把這一篇里的流程用熟練遇到藍(lán)屏就不會(huì)再像個(gè)無頭蒼蠅一樣亂猜了。