很早看到windbg+sos方面的知識(shí),一直沒(méi)仔細(xì)學(xué)習(xí),也許因?yàn)樽约鹤龅南到y(tǒng)還不夠復(fù)雜,也沒(méi)線上真實(shí)環(huán)境查看的權(quán)限,一直沒(méi)學(xué)習(xí)這方面的知識(shí),最近幾天仔細(xì)找了這方面的資料,自己也寫了個(gè)可能造成高CPU高內(nèi)存的測(cè)試web頁(yè)面,發(fā)現(xiàn)確實(shí)不錯(cuò),即使一個(gè)生手,也可以用工具連蒙帶騙的猜出哪里出了問(wèn)題,當(dāng)然對(duì)一些命令和內(nèi)部標(biāo)示更熟悉了后,可以更好的找出問(wèn)題所在,非常值得學(xué)習(xí)。我在使用過(guò)程中,也遇到大量問(wèn)題,比如系統(tǒng)兼容,軟件版本,.net版本等,部分我也沒(méi)能理解清楚,但現(xiàn)在確實(shí)用它找到了程序的問(wèn)題,因此作個(gè)記錄,為以后自己或大家查閱資料提供一定的方便。
基本工作:
我用的windbg是6.11.1.404的32位版本,微軟官網(wǎng)下載地址:http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#b,SOS是每個(gè).net框架都自帶的,我用的.net4.0的sos.dll,我的在C:\Windows\Microsoft.NET\Framework\v4.0.30319里面,另外我還用了調(diào)式的pdb符號(hào)文件,如果不下載,可以配置需要的時(shí)候自動(dòng)下載,但下載需要選擇操作系統(tǒng)版本。windbg老版本使用過(guò)程中報(bào)過(guò)mscorlib的什么錯(cuò)誤,具體記不起了,但以前別人用起過(guò),我確實(shí)用不起,換新版本就好了。
配置工作及常用命令:
在windbg主窗口點(diǎn)擊File下面的Symbol Search Path,設(shè)置符號(hào)文件路徑,比如我的:C:\Windows\symbols\dll;srv*C:\symbols*http://msdl.microsoft.com/download/symbols,其實(shí)C:\symbols才是我下載的符號(hào)文件安裝位置,http://msdl.microsoft.com/download/symbols這個(gè)是必須的,因?yàn)檎也坏椒?hào)文件,它會(huì)自動(dòng)從這里下載。也可以從注冊(cè)表設(shè)置,但我沒(méi)在注冊(cè)表設(shè)置它。
我在調(diào)試過(guò)程中windbg界面如下:
一般我們找到程序出問(wèn)題大體的地方,可以直接查看對(duì)應(yīng)代碼,如果沒(méi)源代碼,我們同樣可以用windbg導(dǎo)出指定程序集地址的代碼,然后反編譯看到源代碼,尤其比如部分dll文件是其他團(tuán)隊(duì)的代碼,我們看不到的時(shí)候。
常用命令比如:
第一步一般是.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll,加載sos,這樣才能調(diào)試托管代碼
查看引起CPU過(guò)高命令比如:
!threadpool,查看線程池CPU使用量,我認(rèn)為WEB的比如iis應(yīng)用程序池進(jìn)程w3wp如果CPU使用過(guò)高,那查看線程池命令肯定看的出來(lái)過(guò)高,這個(gè)是我自己的理解,c/s的就不一定了。
!runaway,查看線程占用CPU時(shí)間,可以從中找到哪個(gè)線程占用時(shí)間更高。
~number s,number為具體哪個(gè)線程的ID。
!clrstack,到具體某個(gè)線程后,查看當(dāng)前線程的托管代碼
!name2ee ,找到哪個(gè)托管代碼模塊后,查看MethodTable,EEClass等信息。
!dumpmt,找到相關(guān)MethodTable處的相關(guān)信息。
!dumpmd,根據(jù)MethodDesc找到相關(guān)模塊信息,比如MethodTable.
!dumpdomain,顯示所有域里的程序集,或者根據(jù)參數(shù)獲取指定域。
!savemodule,根據(jù)具體程序集地址,把當(dāng)前程序集的代碼生成到指定文件
查看占用內(nèi)存過(guò)高的命令比如:
!eeheap,查看堆中信息,可以查看到大對(duì)象。
!dumpheap,查看堆中信息,一般帶-min,-stat,-type等參數(shù)。
!gcroot,根據(jù)堆地址,查看相關(guān)模塊引用代碼信息。
其他命令當(dāng)然還非常多,也非常有用,需要的時(shí)候再翻資料,如果需要很精通windbg+sos,還是老老實(shí)實(shí)仔細(xì)看吧。
開始調(diào)試:
一般分析dump文件方式,可以直接附加進(jìn)程分析,也可以保存dump文件,再單獨(dú)線下分析。 在線調(diào)試,直接Attach to a process就可以了,一般線下調(diào)試,可以用windbg的
adplus.vbs生成dump文件,命令如下:adplus -hang -o d:\dump -p 1234,其中hang表示附加到進(jìn)程,如果是—crash,則為目標(biāo)進(jìn)程崩潰的時(shí)候抓取,-o后面的參數(shù)表示dump文件放到說(shuō)明位置,-p后面的數(shù)字為進(jìn)程的PID,也可以是-pn后面跟進(jìn)程名稱。在我的使用過(guò)程中,win7的系統(tǒng),用adplus抓取w3wp進(jìn)程老失敗,抓其他普通進(jìn)程沒(méi)問(wèn)題,最終我用windows任務(wù)管理器查看到的進(jìn)程界面,點(diǎn)擊w3wp進(jìn)程,右鍵創(chuàng)建轉(zhuǎn)儲(chǔ)文件,它自動(dòng)生成的dmp的dump文件到臨時(shí)目錄,這里它創(chuàng)建的dump文件大小將遠(yuǎn)遠(yuǎn)大于當(dāng)前進(jìn)程的實(shí)際大小。
我的測(cè)試程序在VS2010下的MVC2代碼如下:模擬常見的程序造成循環(huán)過(guò)大,靜態(tài)事件綁定到實(shí)例對(duì)象,造成不釋放內(nèi)存等會(huì)引起CPU過(guò)大,內(nèi)存過(guò)高的問(wèn)題。 訪問(wèn)/home/index后,造成我CPU馬上升到50%左右,內(nèi)存倒看不出來(lái),但多個(gè)訪問(wèn)后會(huì)有無(wú)法釋放的內(nèi)存越來(lái)越大。 首先載入sos后,下面是我的命令記錄:(.........為省略更多內(nèi)容,由于我記錄的時(shí)候線程已經(jīng)切換到25了,顯示0:025>了) 0:025> !threadpool ................... 查看什么線程占用CPU多一點(diǎn) 0:025> !runaway 切換到25線程: 0:025> ~25s 查看托管代碼: 0:025> !clrstack ............................... 0:025> !name2ee * DumpWebTests.Controllers.HomeController ................ Module: 008b689c 查看模塊信息: 0:025> !dumpmt 0130002c 查看所有程序集: 0:025> !dumpdomain 查看到下面的信息: Module Name Assembly: 00e4b2c8 [C:\Windows\assembly\GAC_MSIL\System.Web.Mvc\2.0.0.0__31bf3856ad364e35\System.Web.Mvc.dll] 保存程序集: 0:025> !savemodule 008b689c d:\dump\out.dll 已經(jīng)把分析出的有問(wèn)題的程序dll保存到d盤dump目錄的Out.dll文件了,如果有源代碼,當(dāng)然可以直接查看對(duì)應(yīng)方法的代碼,這個(gè)就把CPU過(guò)高的程序找到了,具體原因當(dāng)然還要自己分析代碼了, 分析內(nèi)存過(guò)高的方法,前面的常用方法里按照步驟就能找到了,這里就不寫到博文里了。 其實(shí)我也還對(duì)windbg+sos調(diào)試代碼的方式比較陌生,還不夠熟悉,但是還是像最開始說(shuō)的,只要稍微比較熟悉了,連蒙帶差還是能大體找到處問(wèn)題的地方,精確找到問(wèn)題,還是需要進(jìn)一步的學(xué)習(xí)。 本篇文章中,也許有不對(duì)的地方,如果發(fā)現(xiàn),請(qǐng)指正,防止自己和大家以后出現(xiàn)同樣的問(wèn)題:)
2 {
3 public static event EventHandler MyEvent;
4 List<byte[]> list = new List<byte[]>();
5 public ActionResult Index()
6 {
7 ViewData["Message"] = "歡迎使用 ASP.NET MVC!";
8
9 MyEvent += new EventHandler(TestEvent);
10 MyEvent(null, new EventArgs());
11 MyMethod();
12
13 return View();
14 }
15
16 public void MyMethod()
17 {
18 long i = 0;
19 while (i < 999000000)
20 {
21 i++;
22 }
23 }
24
25 public void TestEvent(object obj, EventArgs args)
26 {
27 for (int i = 0; i < 20; i++)
28 {
29 list.Add(new byte[1024 * 1024 * 10]);
30 }
31 }
32 }
CPU utilization: 51%
User Mode Time
Thread Time
25:920 0 days 0:00:03.042
0:150c 0 days 0:00:00.046
9:6c 0 days 0:00:00.015
27:1598 0 days 0:00:00.000
26:15cc 0 days 0:00:00.000
24:1084 0 days 0:00:00.000
eax=2362d4fc ebx=00000000 ecx=00000001 edx=00000000 esi=01c9e838 edi=01ca4934
eip=00d70746 esp=0e2dee44 ebp=0e2dee58 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
00d70746 8bd9 mov ebx,ecx
OS Thread Id: 0x920 (25)
Child SP IP Call Site
0e2dee44 00d70746 DumpWebTests.Controllers.HomeController.MyMethod()
0e2dee60 00d70580 DumpWebTests.Controllers.HomeController.Index()
Module: 64bd1000
Assembly: SMDiagnostics.dll
Assembly: DumpWebTests.dll
Token: 02000004
MethodTable: 0130002c
EEClass: 00c84348
Name: DumpWebTests.Controllers.HomeController
EEClass: 00c84348
Module: 008b689c
Name: DumpWebTests.Controllers.HomeController
mdToken: 02000004
File: C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\9f8ccc72\b9d96a8\assembly\dl3\9b2cfeec\916cd56e_eebacb01\DumpWebTests.dll
BaseSize: 0x38
ComponentSize: 0x0
Slots in VTable: 45
Number of IFaces in IFaceMap: 6
008b689c C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\9f8ccc72\b9d96a8\assembly\dl3\9b2cfeec\916cd56e_eebacb01\DumpWebTests.dll
ClassLoader: 00e234f8
SecurityDescriptor: 00e82280
3 sections in file
section 0 - VA=2000, VASize=39c4, FileAddr=200, FileSize=3a00
section 1 - VA=6000, VASize=320, FileAddr=3c00, FileSize=400
section 2 - VA=8000, VASize=c, FileAddr=4000, FileSize=200