天天看點

【調試】Windows夯機Memory Dump案例分析

我們已經看了不少Linux的core dump分析案例了,這次我們來看一個案例,其中利用到了Windows memory dump的分析技巧。Windows的memory dump基本原理幾乎和Linux并無太大差別,如果是Crash - 核心崩潰類型的dump,分析思路幾乎是完全一緻的,當然難度主要在于Windows系統封閉性,即無法提供私有符号和源碼,是以多需要一些彙編層面的了解。但是對于夯機的dump分析就需要一些對系統的了解了,這一點Windows和Linux系統的差別就展現出來了。與之前一樣,我們還是先來看一下具體的現象:

問題現象:

問題現象相對之前要複雜一些,使用者反應業務應用經常會有響應慢的現象。此外看到的一個現象是RDP登入會比較慢,有時會停滞在如下界面5分鐘以上:

【調試】Windows夯機Memory Dump案例分析

前期分析

第一步我們還是做一些前期的分析,因為我們必須要找到分析的切入點,這決定了我們之後排查的方向是否正确。這裡需要多提一句,在分析一些疑難問題時,做一些前期分析,確定排查方向正确是非常必要的,否則一旦方向錯誤,會耗費大量的時間和精力。

這裡我們前期分析的一個目标就是确認問題原因确實是在這台機器上,而不是因為網絡或者RDP用戶端的問題。那麼一個簡單的區分方法就是做一些本地RDP,即在這台機器對127.0.0.1發起RDP連接配接,确認确實也有以上類似現象,那麼我們基本就可以放心繼續我們在這台機器上的排查了。

資訊抓取

夯機Memory Dump的抓取步驟其實相對比較簡單,直接在NC上運作virsh dump即可。但是注意一點,我們經常會犯的一個錯誤是,使用者報告機器響應慢,于是我們就直接抓取dump了。這裡的一個問題在于,當我們分析memory dump時,我們需要一個切入點,比如需要知道大概是哪個程序出現了夯住的問題,那麼我們就可以從這個程序的線程堆棧出發進行分析。如果沒有這個切入點,我們會陷入一種不知從何處開始的窘境,當然如果你對系統足夠了解,可以做一些猜測,甚至整個系統的所有關鍵程序進行逐一掃描。但是這将耗費大量的時間,排查效率也大大下降了。

是以在這個實際案例中,我們要求使用者在本機上進行RDP,并確定停滞在以上畫面5分鐘以上,開始抓取dump。這樣的好處在于,當分析dump時,我們便可以直接從登入的相關程序開始分析了。

Memory Dump分析

下面就到了真正的分析環節了,面對夯機的memory dump分析個人會有自己思路,對Windows系統一個簡單的想法是首先看一下核心鎖的情況,而Linux往往更關注CPU上運作的程序。Windows系統中executive resource是一種非常常見的核心鎖,關于eresource的具體描述可以參考:

https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-eresource-routines

。簡而言之他是Windows核心層面比較常用的鎖形式,并且是Windows特有,在核心層面,一些全局的大鎖往往是由eresource的形式實作的。我們首先來看一下他的情況:

【調試】Windows夯機Memory Dump案例分析

我們可以看到有大量的核心取鎖的線程,而一般我們首先看一下第一個共享鎖的所有者:

【調試】Windows夯機Memory Dump案例分析

我們可以看到executive resource的所有者正在擷取另外一個鎖,是以我們要做的是把他正在嘗試擷取的鎖的所有者線程堆棧列印出來。首先取得鎖的位址:

【調試】Windows夯機Memory Dump案例分析

使用!locks指令列印出鎖的競争情況:

【調試】Windows夯機Memory Dump案例分析

我們可以從輸出中看到對應鎖的所有者為線程 fffffa800b61bb50。是以我們可以再次應用!thread指令列印出該線程的堆棧:

【調試】Windows夯機Memory Dump案例分析

我們可以看到該線程目前正在CPU上運作,但是不幸的是我們針對Windows虛拟機的core dump抓取和轉換是有些問題的,對于正在CPU上運作的堆棧無法輸出。但是我們可以上面等待的線程中擷取一些資訊,注意看Ticks一項:

【調試】Windows夯機Memory Dump案例分析

Ticks代表該線程處于等待未運作狀态的時間,可以看到該等待線程已經等待鎖僅有1秒鐘左右,是以可以判斷該問題并不是一個鎖死的問題,這意味着對應在CPU上運作的這個線程并不會一直占據CPU,而是過一會兒會釋放CPU。是以這僅是一個比較忙的問題,并不是整個核心都被鎖死而無法運作的問題。那麼下個問題就是,這些線程在忙什麼?

我們來看看正在運作的這個線程的IRP吧。IRP即是Windows是分層IO的結構,Windows是通過IRP來實作IO的層層傳遞的,大家可以參考:

https://en.wikipedia.org/wiki/I/O_request_packet

來具體了解一下。我們可以使用windbg的!irp指令來列印出IO結構,其位址來自于!thread輸出的"IRP List"部分:

【調試】Windows夯機Memory Dump案例分析
【調試】Windows夯機Memory Dump案例分析

可以看到IRP的分層結構,IO是有輸出的底層向上層傳遞,目前已經傳遞到檔案系統驅動層面Ntfs。注意我們在這裡要始終記住一點,就是線程等待的時間并不長,說明他是忙着做一些事情,并不是停滞不前。是以面對這個IRP,我們會更關注Ntfs在忙着做什麼。是以我們可以挑選IRP中的操作檔案對象的位址來看看這個IO是發向那個檔案的,或者說線程是在操作哪個檔案:

【調試】Windows夯機Memory Dump案例分析

我們發現了該線程是在操作c:\Windows\Temp目錄下的一些臨時檔案,如果下一步驟自然是我們會去看看這個目錄裡就是有些什麼樣的問題。

驗證階段

當我們去通路c:\Windows\Temp該目錄是,真相就大白了,其中包含了大量的sess_xxx的檔案,導緻explorer直接通路都會夯住很長時間,如果采用指令行dir的方式需要等待很長時間才能列印完所有檔案。

當然使用者會關注是哪些程序産生了這些檔案,利用我們之前文章裡提到的process monitor可以輕松地确認檔案是由業務程序php-cgi建立的。解決方法當然是删除這些臨時檔案,這事實上需要一個晚上的時間!

繼續閱讀