平臺伺服器控制代碼洩露問題的排查與解決
我們監控平臺有臺報警伺服器,其主要功能是接收前端,TDDC,網管伺服器等傳送的報警,並依據報警聯動配置進行相應的聯動操作,最近發現在該伺服器執行過程中,通過工作管理員檢視其控制代碼數量會不斷增加,以至於影響其他伺服器工作,初步懷疑是控制代碼洩露問題,現對其進行分析排查。
控制代碼是Windows用來標識應用程式所建立或使用的物件的唯一整數,Windows的核心物件包括進執行緒,視窗,點陣圖,GDI物件等等。應用程式通過控制代碼訪問核心物件,當使用完核心物件之後需要釋放資源關閉該核心物件控制代碼,如果未能正確關閉,則會造成控制代碼洩露。
一般而言,如果懷疑發生了控制代碼洩露,最首要任務是查詢洩露的控制代碼型別,這樣有助於後面的排查分析,縮小目標範圍。這時可以通過一些輔助工具來幫助分析,如Process Explorer,PCHunter
開啟Process Explorer,找到執行的報警伺服器程序,該工具的會顯示出報警伺服器當前建立的核心物件控制代碼,如圖,可以看出該程序的控制代碼數已經有上千個,而且還在快速增長中。
該工具很方便的一點是,當有新的核心物件建立時,在下方的列表框中會以綠色標識出來,方便檢視,觀察一段時間發現,不停的有執行緒物件建立,而且不會關閉,初步猜測應該是執行緒物件的控制代碼沒有關閉導致。接下來的工作就是要找出這個執行緒核心物件在哪兒建立的。
查詢控制代碼的建立位置的可以通過windbg來獲取,windbg是windows
開啟windbg,按F6,附加報警伺服器程序。在命令列裡面輸入!htrace–enable開啟htrace功能。輸入!htrace –snapshot做第一個快照,然後輸入g命令,讓程式執行一段時間。
該過程如下圖所示:
程式執行一段時間後,按Ctrl+Break鍵,將程式中斷下來。這時輸入!handle –diff,可以比較新增控制代碼的分配上下文。
通過仔細觀察,可以看出,有建立了多個執行緒核心物件,檢視一個核心物件的資訊可以使用!handle
其起始地址為HPR_GetNetWorkFlowData,可以確定該執行緒是使用HPR庫建立的,可以通過在原始碼中查詢HPR_Thread_Create來縮小範圍。
再圖中還可以看到執行緒ID為7670.fef4,其中7670為程序ID,fef4是執行緒ID,因此可以切換到該執行緒看看,輸入
~~[fef4]s命令,切換執行緒。
出現了一條錯誤資訊,即該執行緒已經不存在了,那其實我們就基本可以確定這裡發生了洩露,執行緒已經不復存在,但該執行緒的核心物件卻未能關閉。
通過仔細觀察,我們可以看到這些執行緒物件都是同一個執行緒建立的,如圖
最終都指向了71a8這個執行緒,這樣我們可以確定是71a8這個執行緒建立了很多執行緒核心物件,卻沒有將核心物件的控制代碼關閉,這樣造成了執行緒核心物件的控制代碼洩露,只要定位到71a8這個執行緒,就能找到洩露的地方。
輸入~~[71a8]s,切換到這個執行緒,並輸入kb列印其呼叫棧,如下圖:
呼叫棧是一堆非常奇怪的資料,其實是因為我的系統是64位的,而報警伺服器這個程序是32位的,windbg使用64位的上下文去解析32位的程序,造成了錯誤的解析。這時可以通過!sw這條命令來切換至32位的上下文。然後再敲入kb命令,就能看到該執行緒的呼叫棧了,如圖:
可以清楚的看到該執行緒的呼叫棧,此時該執行緒正在等待socket連線,使用的是HPR庫的HPR_Accept函式,到這個地步,在原始碼中定位該執行緒函式就非常容易了。
在原始碼中搜索HPR_Accept函式,只有一個地方:
可以清楚的看到,這個執行緒接受到連線之後,就建立了一個執行緒,不過卻未將建立執行緒的控制代碼關閉,從而造成了控制代碼洩露,這與我們之前分析的一致。知道了問題所在,修改也就很方便了。