Windows 調試工具課程,在軟件崩潰中調試出原因
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
本文是我在集團內部上的課程記錄而成的博客內容。在本次課程里面將和大家介紹一些在 Windows 上常用的調試工具,以及調查問題的常見套路。適合于伙伴們入門 Windows 調試 本文以下內容是采用原本課程課件里面的一頁頁的內容組裝而來,過程中補充一些講課時的內容 本次課程里面核心的內容是調試工具,調試工具是我們在調試軟件的時候的利器,通過調試工具我們可以找到軟件的問題,解決軟件的問題 本次的課程的開始我來和大家講一個調試故事,這個故事是從用戶反饋軟件用不了的問題開始的 用戶說軟件用不了,那可能會是什么問題呢?用戶不是專業的開發人員,他們不知道如何準確的表述問題 學過軟件工程的同學應該有不少,軟件工程里面應該會有提到,開發的第一步也是非常關鍵的一步就是需求分析。當收到用戶反饋說軟件用不了時,用戶在說什么呢?是不是可能是軟件崩潰了?還是軟件無法啟動?還是其他的問題 遇到用戶說軟件用不了的時候,咱可以有哪些入手點呢?我的調查思路是分為兩個大的方向。第一個方向是從當下的情況入手。如果當下已經沒有了現場了,則可以考慮第二個方向,復現(重現)問題 從第一個方向入手時,可以先考慮從用戶的設備上尋找痕跡。接下來我將和大家聊聊如何開始從用戶的設備上尋找痕跡。當然了,如果這個用戶是咱的測試人員或者是咱的同事,那尋找痕跡這一步就更有價值了 在用戶設備上尋找痕跡時,別忘了 Windows 是咱的好朋友。Windows 提供了很多工具,可以幫助我們找到問題的原因。接下來我將和大家介紹一些 Windows 上自帶的常用的調試工具 第一站就是事件查看器。可以先假設咱可能遇到的是軟件啟動即崩潰的問題。在不遠程用戶的情況下,可以先請用戶發送系統事件日志或截圖過來看看。事件查看器作為第一站的原因是可不發起遠程,直接請用戶截圖或發送日志過來。相對來說對開發者的工作成本較低 通過事件查看器可以進行快速的分析,如看到軟件崩的日志,那就可以證明確實是軟件崩潰了。后續咱的調查方向就可以向著軟件崩掉的方向進行 也有可能通過事件查看器直接看到非常有效的信息,直接就結束戰斗,定位到了問題 舉個栗子 有一次我在調試一個軟件的時候,用戶反饋說軟件無法啟動。我讓用戶發送了事件查看器的日志過來,通過日志可以看到如下內容 錯誤應用程序名稱: lindexi.exe,版本: 5.1.12.63002,時間戳: 0xedd2d687錯誤模塊名稱: MSVCR100.dll,版本: 10.0.40219.325,時間戳: 0x4df2be1e異常代碼: 0x40000015錯誤偏移量: 0x0008d6fd錯誤進程 ID: 0x994錯誤應用程序啟動時間: 0x01d50ac3bd970061錯誤應用程序路徑: C:\Program Files\lindexi\lindexi.exe錯誤模塊路徑: C:\Program Files\PowerShadow\App\MSVCR100.dll報告 ID: a0c5c0b1-76b7-11e9-9d20-94c69123de40 細心的伙伴也許一眼就看出來問題了,出現問題的是 MSVCR100.dll 模塊,然而這個模塊路徑居然是在一個不認識的,名為 PowerShadow 的軟件的目錄下。這時候就可以大概確定問題了,這是被投毒了 試試用谷歌好幫手,搜搜這個軟件是什么軟件。剛好搜到了這篇博客: 影子系統讓 C++ 程序無法運行 于是這就結束戰斗了,調查到了問題的原因,軟件無法啟動是因為被投毒了,被影子系統投毒了。解決方法就是請用戶卸載影子系統,因為影子系統也不維護了,咱軟件層沒啥好掙扎的 可惜的是在很多用戶的設備上,事件查看器日常不工作。沒關系,能從事件查看器找到額外信息,就是賺到了 如果事件查看器找不到或不能用?咱還有其他很多工具可以用 尋找痕跡的時候,另一個常用的好工具就是任務管理器。任務管理器是 Windows 自帶的一個工具,可以幫助我們了解到非常多的信息 通過任務管理器尋找痕跡時,可以按照如上圖所示的決策樹了解一下情況。如果不能在任務管理器里面看到進程,那很可能就是進程已經崩掉了。如果能夠看到進程,那可能就是進程卡了。此時關注點可以是 CPU 使用率。如果 CPU 使用率不動,那可以猜猜可能是死鎖問題,如果 CPU 使用率爆高,那可能是死循環等問題。同步也看一下內存使用率,雖然在任務管理器里面看內存使用率不能真實反映內存使用情況,但是可以作為一個參考。詳細關于如何正確查看程序的內存使用情況,后面會有專門的內容介紹 無論是何種情況,都可以試試撈一個 DUMP 回來調試看看。當然了,對于軟件崩掉的情況,先嘗試一下是不是能啟動起來,拼手的速度快速撈一個 DUMP 回來,如果不能,那后文還會和大家介紹其他工具來輔助撈 DUMP 文件 先回顧一下,咱的調查思路一開始就是嘗試尋找痕跡。尋找痕跡的時候借助 Windows 里面提供的好用的工具,這里重點介紹的是事件查看器和任務管理器。通過事件查看器可以快速的了解到軟件崩潰的原因,通過任務管理器可以了解到軟件的運行情況 在通過自帶的工具沒有明確收獲的情況下,則嘗試撈一個 DUMP 回來開發機器上進行進一步分析 本課程這里提到的 DUMP 文件是指 Windows 下的內存轉儲文件,是一個二進制文件,簡單用人話說就是將進程的內存內容保存到文件里面。通過 DUMP 文件可以有效還原出此時的進程的內存狀態和內存里面的內容,可以用于進一步的分析。當用戶環境里面沒有帶開發工具時,撈一個 DUMP 文件回來,可以幫助我們在開發機器上進行進一步的分析。撈 DUMP 分析的過程,相當于給進程做了一個快照,然后將其放在開發機器上進行進一步的分析 假設進程還在的話,那最簡單的撈 DUMP 方式就是通過任務管理器右鍵選擇創建內存轉儲文件了。對應的英文系統是 Create memory dump file 菜單項 這里需要額外說明的是,如果當前系統是 x64 系統,但是自己的進程是 x86 進程,那此時不建議使用默認打開的任務管理器撈 DUMP 文件。因為默認打開的任務管理器是 x64 的,打出來的是 x64 轉儲文件,包含 WoW64 子系統的信息。詳細請看 你生成的轉儲文件有問題嗎? - 知乎 正確的做法應該是使用 現在假定撈到了 DUMP 文件了,那接下來的步驟就是如何分析 DUMP 文件了。當然了,前置步驟就是如何將 DUMP 文件傳回到自己的開發機器上,這里有一個小妙招就是將這個 DUMP 壓縮一下。由于 DUMP 文件是內存轉儲文件,大部分都是全零的內容,壓縮率非常高。如果需要通過網絡等方式傳輸,那壓縮一下再傳輸會快很多 分析 DUMP 的工具有很多,我著重要和大家介紹的是太陽系最強 IDE —— VisualStudio。VisualStudio 已經是一個成熟的 IDE 了,只需將 DUMP 拖進去就可以了,聰明的 VisualStudio 可以自動幫咱進行分析 一般而言,將 DUMP 拖入到 Visual Studio 里面,接著點擊混合調試按鈕即可。混合調試是使用 托管 調試和 本機 調試的組合。托管調試是指調試 .NET 程序,本機調試是指調試其他非 .NET 系的程序。混合調試是指同時調試托管和本機代碼,因為一般而言 .NET 系的應用要在托管層崩潰是有點難度的,除非開發者自己比較缺乏處理。然而本機代碼,如某些使用 C 、匯編、C++ 編寫的程序,那就容易崩潰了。混合調試可以同時調試這兩種代碼。即使進程完全不是 .NET 程序,也可以使用混合調試來調試 進入混合調試之后,需要等待 Visual Studio 自動分析。如果是第一次調試 DUMP 文件的,可能會在下載符號這一步卡住一會。大家可以出去喝個茶,等待一下,再回來看看。實在等不急了,那就點擊取消符號加載再繼續吧 好的,現在咱的進度就是在用戶側發現了問題,且不能通過事件查看器等結束戰斗。將用戶的 DUMP 文件撈回來,通過 Visual Studio 進行分析。分析的方法就是將 DUMP 文件拖入 Visual Studio 里面,然后點擊混合調試按鈕。等待 Visual Studio 自動分析,即可看到分析結果 那聰明的 Visual Studio 會幫咱分析出什么內容呢?如何看 Visual Studio 的分析結果呢?常見的套路就是關注 Visual Studio 以下三個方面內容
先來和大家介紹一下調用堆棧。調用堆棧是個好東西,調用堆棧是一個非常重要的內容,可以幫助我們了解到程序是如何運行的。通過調用堆棧可以看到程序是如何運行的,是從哪個函數開始的,是如何調用的,是如何返回的。默認的 Visual Studio 調試布局里面,可以快速看到調用堆棧窗格 調用堆棧可以如何看?調用堆棧可以和著之前在用戶端任務管理器所見內容進行一起分析。如在任務管理器看不見進程,即對應進程崩了的問題,可以通過調用堆棧嘗試看到是誰帶崩的,崩之前調用的是哪個函數。如果是在任務管理器能看到進程,但是 CPU 使用率不動,那可能是死鎖問題,可以通過調用堆棧看到是哪個函數卡住了主線程或進入鎖。如果是 CPU 使用率爆高,那可能是死循環問題,可以通過調用堆棧看到是哪個函數跑滿了線程 舉個真實的例子,以下就是我從用戶端撈回來的一個 DUMP 文件。通過 Visual Studio 分析,崩潰之前的調用堆棧如下 > 00000000() Unknown [Frames below may be incorrect and/or missing] Unknown nvumdshim.dll!710d0745() Unknown nvd3dum.dll!5989f2e1() Unknown nvd3dum.dll!595f1716() Unknown nvd3dum.dll!596b7827() Unknown nvd3dum.dll!598a6233() Unknown nvd3dum.dll!5989b95c() Unknown nvd3dum.dll!5989c33b() Unknown nvd3dum.dll!598816bc() Unknown nvumdshim.dll!710ca40e() Unknown nvumdshim.dll!710cbb78() Unknown nvumdshim.dll!710ca17f() Unknown nvumdshim.dll!710ca0d3() Unknown d3d9.dll!5ab86f81() Unknown ntdll.dll!_NtWaitForMultipleObjects@20 () Unknown KERNELBASE.dll!76f69723() Unknown 通過調用堆棧可以看到是 nvumdshim.dll 模塊帶崩的。這個模塊是 NVIDIA 顯卡驅動的模塊。通過這個調用堆棧可以看到是 NVIDIA 顯卡驅動帶崩的。這個問題的解決方法就是更新 NVIDIA 顯卡驅動。此問題詳細請看 記因為 NVIDIA 顯驅錯誤而讓 WPF 應用啟動閃退問題 驅動問題是客戶端崩的常見問題,表現就是在很多用戶電腦工作好好的,在某些用戶就起不來 修復 DirectX 時,我常用的就是 DirectX 修復工具,此工具下載地址是: https://blog.csdn.net/VBcom/article/details/6962388 講完了誰帶崩的問題,接下來再看另一個案例。對應 CPU 不動的問題,如下圖所示的調用堆棧 大家猜猜上面堆棧告訴咱什么問題 通過以上的堆棧可以知道進入了鎖。此時的常見套路就是從上到下找找,找第一個咱自己程序集的調用函數,如這里就找到了是在 lindexi.dll 里面的方法。可以知道的是這個方法有邏輯在等待鎖,且這個鎖就不返回。此時配合代碼食用更佳。咱這里能夠知道進程卡住的原因是因為等待鎖,且這個鎖不返回,而至于這個鎖在業務上是什么作用就需要咱進一步配合代碼進行分析了 再來看看對應 CPU 爆高的一個案例,此時堆棧里面的信息可以告訴咱,現在正在跑的方法是哪些。有可能就是當前的調用堆棧的頂部的幾個方法有邏輯跑滿了線程了。同樣,此時配合代碼食用更佳 但有可能此時面對的情況是沒有代碼。如使用的是第三方庫等,此時靠堆棧信息是不夠的。先讓大家思考這個問題,如果此時沒有代碼還可以如何進一步分析?我將在后文和大家介紹如何通過三板斧來進一步分析 回顧一下,這就是咱拖入 DUMP 文件之后,依靠 Visual Studio 里面的調用堆棧進行問題分析的常見三個案例。對應軟件崩潰的問題,可以通過調用堆棧看到是誰帶崩的。對應 CPU 不動的問題,可以通過調用堆棧看到是誰卡住了主線程。對應 CPU 爆高的問題,可以通過調用堆棧看到是誰跑滿了線程 但是僅靠調用堆棧可能還是不夠的,有時候需要更多的信息。接下來我將和大家介紹如何通過“三板斧”來進一步分析 這里介紹的“三板斧”分別是寄存器、反匯編、內存這三個方面的工具。通過這三個方面的工具可以幫助我們進一步的分析問題 需要說明的是用到這三個工具時僅僅只是在咱有需要了解更多狀態信息的時候。而且通過這三個工具也不一定能夠準確了解到問題的原因。這三個工具的使用本身不難,但是其難點確是這幾個工具所見內容的背后大家關于程序本身的理解以及軟件運行機制的了解。如果對于軟件運行機制不了解,那這三個工具所見內容可能會讓人難以理解,或者是調查方向跑偏 依然使用剛才的例子,當看到 CPU 爆高的時候,通過調用堆棧可以看到是哪個方法跑滿了線程。但是這個方法邏輯跑滿了,其原因是什么呢?調用堆棧可無法回答此問題 試試先在 Visual Studio 里面打開內存、寄存器、反匯編窗格。這三個工具可以幫助我們進一步分析問題 打開之后的 Visual Studio 的界面布局大概如上圖所示 拿本課程的 CPU 爆高的例子,先通過反匯編發現了可能存在的問題,如想看看 rcx 寄存器里面存放了什么。通過寄存器窗格可以看到 rcx 寄存器里面存放了什么內容。通過內存窗格可以看到這個地址里面存放了什么內容。剛好就看到了對應的內存里面存放了一段逗比代碼 使用 “三板斧” 本身的難度不大,但是其難點在于其背后的知識。如匯編知識,寄存器的機制,以及軟件本身的運行機制。這部分知識遠遠超過了本課程能介紹的范圍,需要大家自行學習,但由于這部分知識的學習成本較高,所以在實際工作中,這部分知識可能并不是必須的。我只敢推薦大家在有余力的情況下進行學習,如果平時工作已經很忙了學不過來了,那這部分知識還可以先放著。但是如果能夠掌握這部分知識,那在調試問題時會有所幫助 繼續和大家介紹 Visual Studio 的另一個調試工具——局部變量。局部變量也是個好東西,可以幫助我們了解到程序運行時的狀態。通過局部變量可以看到程序運行時的變量的值,可以幫助我們了解到程序運行時的狀態 如看到了錯誤之前的局部變量有一個名為 工具下載地址: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-code-lookup-tool 如在這里咱可以看到的錯誤信息是文件或文件夾名錯誤,根據咱的業務邏輯,可能是文件名錯誤導致的問題。那接下來的調查方向就是看看為什么出現錯誤的文件名了,這時候也許一看代碼就理解了 再舉另一個真實的例子,如看到的是如上圖的異常導致的崩潰。根據咱通過搜索引擎了解到的知識,這個 WindowsCodecs.dll 是 Windows 系統的 WIC 多媒體解碼層。可能此時遇到的問題和圖片等多媒體的編解碼有關 剛好在本例子里面,通過局部變量看到了出問題的圖片的文件地址,此時的調查就更加有方向了。除了可能存在的 WIC 層的問題外,還可以是圖片文件本身的問題。如圖片文件投毒等問題 延伸一下,如何了解圖片、音視頻等文件是否被投毒了?這里推薦一個工具,通過 MediaInfo 工具可以幫助咱看到文件的許多信息 如這個文件就是一個假裝是 png 的 WebP 文件,然后投毒將 WIC 層搞崩了 MediaInfo 工具下載地址: https://mediaarea.net/en/MediaInfo/Download 好像… 還是有些問題調試不出來 太陽系最強 IDE 也頂不住呀 那就試試上接近能調試一切的 WinDbg 吧 這個工具非常強大,只是有一個問題。那就是有億點點上手門檻 在這里我告訴大家一個非常簡單的方法,讓大家瞬間就能學會上手使用 WinDbg 工具調試問題。方法就是請一個熟悉 WinDbg 的伙伴,讓他幫你調試,找到一個工具人幫你使用 WinDbg 調試問題是最快能學會使用 WinDbg 的方法 回顧一下,以上咱就聊了在用戶端發現問題,先嘗試使用 Windows 自帶工具快速進行定位問題。以及撈到 DUMP 文件之后,如何在開發機器上通過 Visual Studio 進行進一步分析。分析的方法就是將 DUMP 文件拖入 Visual Studio 里面,然后點擊混合調試按鈕。等待 Visual Studio 自動分析,即可看到分析結果。分析的重點是調用堆棧、三板斧、局部變量。通過這三個方面的工具可以幫助我們進一步的分析問題 如果 Visual Studio 還不能解決問題,那就找個工具人來幫忙使用 WinDbg 繼續調查問題 這就是第一個大方向的內容 第二個大方向就是事后現場的復現問題。什么時候需要復現問題?比如最簡單來說就是軟件啟動即崩潰,完全來不及打開任務管理器撈 DUMP 文件。這時候就需要復現問題了,通過復現問題可以幫助我們更好的定位問題 復現問題時也不是只是簡單重復跑程序,而是可以通過更多的工具輔助來在復現問題時更好的定位問題 首要介紹的就是 ProcDump 工具 當使用任務管理器撈不到 DUMP 或不好撈 DUMP 時,使用 ProcDump 工具能夠更好的幫助我們撈 DUMP 文件。ProcDump 工具是 Sysinternals 的工具,下載地址是: https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump 為什么說有時候不好使用任務管理器撈 DUMP 呢?因為現實往往很復雜。除了閃崩,軟件啟動即崩潰導致的手速不夠快,撈不到 DUMP 文件之外,還有其他很多問題。比如軟件就是處于似崩未崩的狀態,期望抓到某個時機的狀態,如軟件一定會在某次 CPU 爆高之后不能符合預期工作,然而 CPU 爆高的時間非常短,靠人類去看去抓是有些廢程序猿的。比如軟件半夜崩潰,只有在午夜12點才會崩潰,這時候人類可能已經睡著了,即使沒睡著,可能錯過了這個時間點就要等明天的午夜12點了。再比如是非必現的問題,需要壓測才能復現,期望自動化收集,否則可能要跑幾千次才能復現一次,靠人類的工作量有些大 通過 ProcDump 可以在程序萬種死法中有效的生成 Dump 文件,只需使用好 ProcDump 的參數。具體參數作用可以參考 微軟官方文檔 和 如何在 NET 程序萬種死法中有效的生成 Dump (上) - 一線碼農 - 博客園 這是一個小游戲,讓大家連連線,看看在什么情況下應該使用什么方法 在調查思路這里,復現問題時經常伴隨使用 ProcDump 工具,因為 ProcDump 工具可以在非常多的情況下幫助我們撈 DUMP 文件 復現問題時,不僅只有 ProcDump 工具。還有可能面對的是事后現場的情況,此時需要使用更多的工具來輔助定位問題。以及當沒有調查思路時,可以試試常見的問題的探索幫助尋找思路 來和大家講講事后現場的調查 什么是事后現場?事后現場問題在這里一般說的是當前的現場或能復現所抓取到的現場已經不是問題發生的現場,而是發生問題之后的現場了 比如找到問題了,但問題非本質問題。常見的就是通過 DUMP 分析是如 空 異常的情況,導致崩潰的原因是因為空指針異常。但是空指針異常是如何產生的呢?這時候就需要通過事后現場分析思路調查來進一步分析問題 比如發生問題的地方不是產生問題的地方。如本課程的例子里面,崩潰原因是一張假裝 png 的 WebP 圖片,那這張圖片是哪里來的,為什么會使用這張圖片。如果此時代碼邏輯沒有幫助的話,那就需要進一步通過復現問題調查事前現場來進一步分析問題 比如系統性的問題。常見的就是團伙作案,不是單個應用導致的問題。這類問題的難度在于其復雜度,可能難以抓到正確的現場。此時也需要通過多次復現問題,抓取更多的信息,通過事前和事后現場的分析來進一步分析問題 面對事后現場和團伙作案等問題,采用微軟極品工具箱的 Process Monitor 工具,配合 DebugView 工具通常都能有不錯的收獲 Process Monitor 工具下載地址: https://learn.microsoft.com/zh-cn/sysinternals/downloads/portmon Debugview++ 工具開源地址: https://github.com/CobaltFusion/DebugViewPP DebugView 工具下載地址: https://learn.microsoft.com/en-us/sysinternals/downloads/debugview 從界面和交互上,DebugView++ 比 DebugView 更好用一些 舉個真實栗子來和大家演示多個工具之間的配合使用來調用一個有趣且復雜的問題 這個問題的開始是測試同學和我報告了觸摸失效問題,后來經過進一步調查發現其實是 explorer 未響應問題,表現就是 explorer 迷之閃黑 這個問題復雜之處在于 explorer 不是咱的,咱也不熟悉,也不知道是什么導致的。而且 explorer 太龐大了,撈到 DUMP 分析壓力過大,耗時耗力。需要使用更多的工具輔助進一步分析問題 此時通過 Process Monitor 工具抓取 explorer 進程信息,發現了如上圖的有趣的內容。里面很受我關注的就是存在了進程退出 通過網上四處搜發現 explorer 是一個多進程軟件,進程的退出和迷之閃黑可能有所影響。既然進程退出了,那就試試上 ProcDump 工具在進程之前之后抓一個 DUMP 文件回來分析 由于 explorer 十分龐大,且咱也不熟悉 explorer 的代碼,來回抓了幾次 DUMP 分析都沒有什么收獲。直到某次抓取到了一個有趣的 DUMP 文件,通過這個 DUMP 文件發現了在進程退出之前的調用堆棧里面包含了 Shell32 的一些調用 再根據前面的 Process Monitor 工具抓到的在進程退出之前碰的是 Realtek Bluetooth 藍牙模塊,于是重心就在 Shell32 和藍牙一起組合上面 既然大概定位到這里,那就繼續上 ShellView 工具。通過 ShellView 工具進行大量的 Shell32 組件的禁用,我的做法大概就是看哪個不開森就禁用哪個,進行二分法的禁用,最終發現了是 Realtek Bluetooth 藍牙模塊導致的問題 二分法的禁用就是先一口氣禁用一半的組件,看看問題是否解決。如果解決了,那就說明問題在這一半里面。如果沒有解決,那就說明問題在另一半里面。然后再在這一半里面繼續二分禁用,直到找到問題所在 經過以上的調查工具可以了解到是藍牙相關模塊的問題,集中火力找到明確的調試方向,很快就找到是藍牙驅動的問題 詳細的調試內容可比這里介紹的有趣的很,請看 記一次調試資源管理器未響應經驗 博客園博客只做備份,博客發布就不再更新,如果想看最新博客,請訪問 https://blog.lindexi.com/ 如圖片看不見,請在瀏覽器開啟不安全http內容兼容 本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含鏈接:https://www.cnblogs.com/lindexi ),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我[聯系](mailto:lindexi_gd@163.com)。 該文章在 2024/9/20 9:13:18 編輯過 |
關鍵字查詢
相關文章
正在查詢... |