你需要了解的 JIT Debugging
如果你還不清楚什么是轉儲文件,不知道什么時候需要轉儲文件,請參考轉儲文件系列文章的第一篇 —— 轉儲文件知多少。
前言
我在 你需要知道的 N 種抓取 dump 的工具?的工具 這篇文章里,向大家介紹了幾款可以抓取轉儲文件的工具及其簡單用法。不知道大家是否還記得,以管理員權限運行 procdump -i 可以注冊 procdump 為事后調試器。大家是否了解其實現原理?今天讓我們一起揭開其神秘面紗。
約定
JIT Debugger, Just In Time Debugger,JIT 調試器, Postmortem Debugger,事后調試器,指的是同一個概念 —— 事后調試器。如果把 Debugger 換成 Debugging,表示事后調試。我有時候會說 JIT 調試器,有時候會說事后調試器,希望大家不要被我混亂的用詞搞暈。
原理探究
運行 process monitor,開啟監視。然后以管理員權限執行 procdump.exe -i,成功后,停止監視。為了方便大家,我特意錄制了整個過程,感興趣的小伙伴可以點開看看,不過我建議你親自動手實戰一番,畢竟 紙上來的終覺淺, 絕知此事要躬行。
探究 procdump 安裝為 JIT 調試器的過程
如果你沒看視頻,可以直接參考我過濾后的結果截圖(保留Result是 Success 的 注冊表 寫 事件,排除非注冊表相關事件):
我用黃色和紅色高亮了 procdump 操作的注冊表項。你能從圖中得出什么結論呢?
procdump 會同時寫 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug 和 HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug 注冊表項。
相信有開發經驗的小伙伴兒知道,在64 位系統下,部分注冊表項有兩套:一套是供 64 位進程使用的(黃色高亮部分),一套是供 32 位進程使用的(紅色高亮部分,帶 Wow6432Node)。
如果 AeDebug下的 Auto 子項和 Debugger 子項有值,procdump 會先備份,再修改。(執行 procdump -u 的時候會恢復系統原有設置)
Auto 和 Debugger 的數據類型都是 REG_SZ。(雖然我們看到 Auto 的值是 1)
我猜,32 位進程崩潰的時候,會使用帶 Wow6432Node 的注冊表項,64 位進程崩潰的時候,會使用不帶 Wow6432Node 的注冊表項。真的是這樣嗎?你知道怎么驗證嗎?相信聰明的你一定能想出驗證辦法。
其實,以上結論在 procdump -i 的輸出結果中已經給出提示了(除了備份操作)。注意看下圖中的黃色和紅色高亮的部分。
procdump-i溫馨提示:
某些殺毒軟件可能會對此注冊表項有保護,如果設置失敗,請檢查是否是殺毒軟件導致的。
至此,我們知道 procdump 是通過設置 AeDebug下的 Auto 和 Debugger 子項實現的 JIT Debugging。那么這兩項都有什么用呢?
AeDebug 探究
使用 google 搜索 AeDebug,搜到了微軟的官方說明[1] ,有興趣的小伙伴一定要讀一讀,有很多有價值的信息。
Auto 項:指定是否向用戶顯示錯誤提示框,如果值為 "0",則顯示提示框。為 "1" 則不顯示提示框,直接附加注冊的事后調試器到目標進程中。
Debugger 項:指定事后調試器的路徑,及傳遞給事后調試器的參數。我們發現 procdump -i 設置的參數是 -accepteula -j "E:\dumps" %ld %ld %p。其中:
-accepteula 表示接受用戶協議。
-j 表示參數中有指向 JIT_DEBUG_INFO 的指針(父進程傳遞了 %p 對應的內容)。
"E:\dumps" 表示轉儲文件保存的路徑(如果運行 procdump -i 的時候,沒有指定轉儲文件的保存路徑,默認會取當前路徑 )。
第一個 %ld 表示目標進程的進程 ID。
第二個 %ld 表示事件句柄。這個事件句柄是 WER 復制到事后調試器中的。如果事后調試器激活該事件(通過 SetEvent())后,WER 將繼續目標進程的執行,而無需等待事后調試器終止。如果事后調試器在沒有激活該事件的情況下終止,WER 將繼續收集關于目標進程的信息。
%p 指向目標進程空間中的 JIT_DEBUG_INFO 結構指針。包含了異常的來源和與異常相關的上下文信息。
如果轉儲文件中保存了 JIT_DEBUG_INFO,使用 windbg 調試時,可以通過 .jdinfo address 來查看異常發生時的信息。例如,使用 windbg 打開 procdump 保存的轉儲文件的時候,應該可以看到如下提示。
procdump 在轉儲文件中添加的注釋我們可以根據提示,輸入.jdinfo 0x1afd59e0000 來查看異常來源及上下文信息。
jdinfo 結果說明:
在運行 procdump -i 的時候,如果沒有指定轉儲選項,會默認使用 -mm 選項。該選項只包含 Process, Thread, Module, Handle and Address Space info. 信息,不會包含 %p 對應的內存數據。如果我們在調試 使用 -mm 選項保存的轉儲文件的時候執行 .jdinfo address,會得到如下錯誤:Unable to process JIT_DEBUG_INFO, Win32 error 0n30
我們可以簡單的通過指定 -ma 或 -mp來生成包含內存數據的轉儲文件,這樣我們在調試器里執行 .jdinfo address的時候就不會報錯了。
據我觀察,對于 procdump 來說 -j和 %p 選項需要同時傳遞,缺一不可。
排除進程
如果我們真的不想讓某些進程出現未處理異常的時候中斷到 JIT 調試器中,有沒有辦法呢?從 vista 開始,我們可以顯示排除某些進程,不讓這些進程在出現未處理異常的時候中斷到 JIT 調試器中。對應的注冊表項如下:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusionList
下面是我機器上的該注冊表項的值:
Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusionList] "DWM.exe"=dword:00000001 "demo.exe"=dword:00000001上面的 demo.exe 是我為了測試手動添加的,而 DWM.exe 是系統添加的。windows 為什么要默認把 DWM.exe 添加到排除列表呢?我也不太清楚,不過我在 Excluding an Application from Automatic Debugging[2] 看到這樣一句話:
By default, the Desktop Window Manager (Dwm.exe) is excluded from automatic debugging because otherwise a system deadlock can occur if Dwm.exe stops responding (the user cannot see the interface displayed by the debugger because Dwm.exe isn't responding, and Dwm.exe cannot terminate because it is held by the debugger).
我想這就是 DWM.exe 會被排除的原因吧。
如果想通過代碼的形式實現,除了直接操作注冊表外,還可以通過 WerAddExcludedApplication() 來實現,對應的,可以通過 WerRemoveExcludedApplication() 來刪除 。這兩個函數的原型摘錄如下:
HRESULT WerAddExcludedApplication(PCWSTR pwzExeName,BOOL bAllUsers );HRESULT WerRemoveExcludedApplication(PCWSTR pwzExeName,BOOL bAllUsers );第一個參數 pwzExeName 表示要排除的程序,不要帶路徑,只傳遞程序名稱即可。比如,demo.exe。
第二個參數 bAllUsers 如果是 FALSE 的話,表示僅對當前用戶有效,其它用戶不受影響,修改的是 HKCU (HKEY_CURRENT_USER)下對應的注冊表項。如果為 TRUE 的話,表示對所有用戶都生效,修改的是 HKLM (HKEY_LOCAL_MACHINE)下對應的注冊表項,為 TRUE 的時候,需要有管理員權限。
注意:
如果你手動調用代碼操作注冊表的話,務必注意 64 位系統下的注冊表重定向問題。相信一定有小伙伴兒和我一樣踩過這個坑。
JIT 調試的運作機制
整個運作機制,在張銀奎張老師的《軟件調試》(第一版)第 12 章:未處理異常和 JIT 調試 中做了非常非常詳細的介紹。我就不摘錄了,感興趣的小伙伴一定要好好多讀幾遍。
AeDebug 中的 Ae 是什么意思?
AeDebug中的 Debug 很好理解,就是調試的意思。那 Ae 代表什么意義呢?有人說 AeDebug 是 Auto Exception Debug 的縮寫,聽上去挺有道理的。偶然的機會,google 到了 Ramond Chen寫的一篇文章 —— What does the “Ae” stand for in AeDebug?[3]。根據他的說法,Ae 表示 Application Error 的意思。我把原文截取如下,方便大家閱讀。
Raymond-Chen-explain-AE知道 AeDebug 是什么單詞的縮寫有助于幫助大家記憶,但沒必要糾結。
總結
一般情況下,修改 HKLM 下的注冊表項需要管理員權限。
注冊為 JIT 調試器,需要管理員權限,因為需要寫 HKLM 下的子鍵。
procdump 可以通過 -i 選項注冊為事后調試器,另外 windbg也可以通過 -I 選項注冊為事后調試器。
AeDebug 注冊表項是 JIT 調試的關鍵,該注冊項在 64 位系統下有對 32 位進程和 64 位進程分別有對應的注冊表項。其中,帶 Wow6432Node 的注冊表項是給 32 位目標進程使用的。
64位系統下,除了AeDebug有兩套,還有很多其它注冊表項也有兩套。
如果確實不希望自己的進程在出現未處理異常時中斷到 JIT 調試器中,可以設置注冊表進行排除(Vista 及之后的操作系統才支持)。
參考資料
《windows sysinternals 實戰指南》
《軟件調試》(第一版)
Microsoft Document : Enabling Postmortem Debugging[4]
Raymond-Chen : What does the “Ae” stand for in AeDebug?[5]
Configuring Automatic Debugging[6]
WerAddExcludedApplication[7]
WerRemoveExcludedApplication[8]
References:
[1]
微軟的官方說明: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/enabling-postmortem-debugging
[2]Excluding an Application from Automatic Debugging: https://docs.microsoft.com/en-us/windows/win32/debug/configuring-automatic-debugging#excluding-an-application-from-automatic-debugging
[3]What does the “Ae” stand for in AeDebug?: https://devblogs.microsoft.com/oldnewthing/20181017-00/?p=99995
[4]Microsoft Document : Enabling Postmortem Debugging: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/enabling-postmortem-debugging
[5]Raymond-Chen : What does the “Ae” stand for in AeDebug?: https://devblogs.microsoft.com/oldnewthing/20181017-00/?p=99995
[6]Configuring Automatic Debugging: https://docs.microsoft.com/en-us/windows/win32/debug/configuring-automatic-debugging#configuring-automatic-debugging-for-application-crashes
[7]WerAddExcludedApplication: https://docs.microsoft.com/en-us/windows/win32/api/werapi/nf-werapi-weraddexcludedapplication
[8]WerRemoveExcludedApplication: https://docs.microsoft.com/en-us/windows/win32/api/werapi/nf-werapi-werremoveexcludedapplication
猜你喜歡:
轉儲文件系列:
轉儲文件知多少
你需要知道的 N 種抓取 dump 的工具
你生成的轉儲文件有問題嗎?
調試系列:
調試實戰——你知道怎么使用DebugView查看調試信息嗎?
調試實戰——程序CPU占用率飆升,你知道如何快速定位嗎?
調試實戰——崩潰在ComFriendlyWaitMtaThreadProc
調試實戰——使用windbg調試崩潰在ole32!CStdMarshal::DisconnectSrvIPIDs
調試實戰——調試PInvoke導致的內存破壞
調試實戰——調試excel啟動時死鎖
調試實戰——調試DLL卸載時的死鎖
調試實戰——調試TerminateThread導致的死鎖
排錯系列:
排錯實戰——VS清空最近打開的工程記錄
排錯實戰——拯救加載調試符號失敗的IDA
排錯實戰——你知道拖動窗口時只顯示虛框怎么設置嗎?
排錯實戰——解決Tekla通過.tsep安裝插件失敗的問題
排錯實戰——使用process explorer替換任務管理器
排錯實戰——通過對比分析sysinternals事件修復程序功能異常
歡迎留言交流
總結
以上是生活随笔為你收集整理的你需要了解的 JIT Debugging的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为容器提供持久存储,这个方法试试看
- 下一篇: 超越“迁移”的思考:应用程序该如何被Ku