如何手工抓取dump文件
在生產環境下進行故障診斷時,爲了避免終止正在運行的服務或應用程序,有兩種方式能夠對正在運行的服務或應用程序的進程進行分析和調試。 html
首先一種比較直觀簡潔的方式就是用WinDbg等調試器直接attach到須要調試的進程,調試完畢以後再detach便可。可是這種方式有個缺點就是執行debugger命令時必須先break這個進程,執行完debug命令以後又得趕忙F5讓他繼續運行,由於被你break住的時候意味着整個進程也已經被你掛起。另外也常常會因爲First Chance Excetpion而自動break,你得時刻留意避免長時間break整個進程。因此這樣的調試方式對時間是個很大的考驗,每每沒有充裕的時間來作仔細分析。 web
另外一種方式則是在出現問題的時候,好比CPU持續長時間100%,內存忽然暴漲等非正常狀況下,經過對服務進程snapshot抓取一個dump文件,完成dump以後先deatch,讓進程繼續運行。而後用windbg等工具來分析這個抓取到的dump文件。 windows
那麼如何在不終止進程的狀況下抓取dump文件呢?Debugging Tools for Windows裏提供了一個很是好的工具,adplus.vbs。從名字能夠看出,其實是一個vb腳本,只是對cdb調試器做的一個包裝腳本。 數組
其路徑與Debugging Tools for Windows的安裝路徑相同,使用的方法也很簡單,以下所示: app
adplus.vbs -hang -p 1234 -o d:/dump 函數
其中-hang指明使用hang模式,亦即在進程運行過程當中附加上去snapshot抓取一個dump文件,完成以後detach。與之對應的是-crash崩潰模式,用戶先啓動adplus,而後由它啓動要監控的程序,在出現異常崩潰時自動生成dump文件,或者經過Ctrl-C人爲發出抓取指令。可是-crash模式在抓取完成以後,被監控的進程就必須終止。所以咱們在這裏只選用-hang模式。 工具
-p是要調試的進程ID,好比ASP.NET應用線程池,在Win2003下就是w3wp.exe 測試
-o 指定要output的dump文件路徑。 網站
另外,與adplus相似的,有個UserDump工具,可是抓取用戶模式的進程,而adplus則是內核模式和用戶模式二者皆可。 ui
而總所周至的Dr. Waston,則會在進程崩潰以後的自動時候抓取dump文件,同樣能夠用於windbg等調試器來過後分析程序崩潰時的狀態。
====================
0:000> !dumpheap -stat
No export dumpheap found
======解決方法:
.load clr20/sos.dll,你要先執行的。sos.dll在默認的c:/windows/microsoft.net/framework/v2.....下面,你複製到c:/program files/debugging tools for windows下面的clr20目錄下面(clr20是你手工建立的)
=======================
在.NET下開發時,最基本的調試方法就是使用Visual Studio的單步調試。可是對於一些特殊狀況,特別是涉及到CLR內部的時候使用這種方式就達不到目的了。
若是要查看運行時內存使用狀況,IL代碼,CLR信息等可使用如下兩種方式:
一、使用VS2005 + sos.dll
二、使用Windbg + sos.dll
第二種方式功能更增強大,下面我就經過實際操做展現一下怎麼使用這種方法獲得運行時ArrayList內部的值。
有人可能會說:我直接用Visual Studio的單步調試豈不是更快?固然,這個只是一個演示,經過這個演示是爲之後的高級調試打下基礎
在操做以前,先熟悉一下基本知識:
A、使用VS2005 + sos.dll調試
一、須要在項目->屬性->調試-〉啓用非託管代碼調試
二、打開調試-〉窗口-〉即時
三、在即時窗口中輸入 !load sos 加載調試模塊
四、輸入其它調試語句
B、使用Windbg + sos.dll
一、去微軟的網站下載最新的Windbg
二、打開Windbg在File-〉Symbol File Path ...窗口中輸入 srv*c:/symbols*http://msdl.microsoft.com/download/symbols
三、運行須要調試的程序,而後在Windbg中File-〉Attach to Process中選擇剛纔運行的程序
四、在出現的Command窗口中就能夠輸入調試語句
五、經常使用調試語句:
lm //查看加載了哪些模塊
.load C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/sos.dll //加載調試模塊
ld TestClass //加載調試符號
!name2ee TestClass.exe TestClass.Program.test //顯示test方法相關的地址
!dumpmt -md 00976d48 //獲得類的成員函數詳細信息
!dumpil 00973028 // 顯示這個方法被編譯器編譯以後的IL代碼
!dumpheap -stat //該命令顯示程序中全部對象的統計信息,顯示的大小是對象自己的大小,不包括對象裏面值的大小
!dumpheap -mt 790fcb30 //該命令顯示MethodTable 790fcb30的詳細信息
!gcroot 012919b8 //來顯示一個實例的所屬關係
!dumpobj(do) 012a3904 //顯示一個對象的具體內容,看對象裏面有什麼,值是什麼
!ObjSize 012a1ba4 //對象實際在內存中的大小
!eeheap -gc //查看託管堆的狀況(包括大小)
!DumpArray //查看數組信息
下面就來看看具體的調試步驟:
一、咱們的測試代碼
namespace TestClass
{
class Program
{
[STAThread]
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.Add("aaaa");
list.Add("bbbb");
Console.ReadLine();
}
}
}很簡單,就是一個ArrayList
運行這個程序(開始執行,不調試),而後進入Windbg,Attach到這個進程
二、查看全部堆棧信息
0:004> !dumpheap -stat
MT Count TotalSize Class Name
7910062c 1 12 System.Security.Permissions.SecurityPermission
7918e284 1 16 System.IO.TextReader+SyncTextReader
79102d10 1 20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
79102cb4 1 20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
79101d30 1 20 System.Text.InternalEncoderBestFitFallback
79100a7c 1 20 Microsoft.Win32.SafeHandles.SafeFileHandle
79105cd4 1 24 System.Collections.ArrayList
......
7912ad90 11 9036 System.Object[]
790fcb30 2083 131492 System.String
Total 2202 objects
除了咱們的ArrayList外,還有不少其它的系統信息,先不用管它
三、查看咱們的ArrayList的信息
0:004> !dumpheap -mt 79105cd4
Address MT Size
012a1b88 79105cd4 24
total 1 objects
Statistics:
MT Count TotalSize Class Name
79105cd4 1 24 System.Collections.ArrayList
Total 1 objects
四、查看對應地址內部實際的值
0:004> !do 012a1b88
Name: System.Collections.ArrayList
MethodTable: 79105cd4
EEClass: 79105c28
Size: 24(0x18) bytes
(C:/WINDOWS/assembly/GAC_32/mscorlib/2.0.0.0__b77a5c561934e089/mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
7912ad90 40008df 4 System.Object[] 0 instance 012a1bb0 _items
791018e0 40008e0 c System.Int32 1 instance 2 _size
791018e0 40008e1 10 System.Int32 1 instance 2 _version
790fc35c 40008e2 8 System.Object 0 instance 00000000 _syncRoot
7912ad90 40008e3 1c0 System.Object[] 0 shared static emptyArray
>> Domain:Value 00149c58:012a1ba0 <<
能夠看到ArrayList的大小爲2,具體的值保存在地址012a1bb0中,是一個System.Object[]類型的數組
五、查看數組信息
0:004> !DumpArray 012a1bb0
Name: System.Object[]
MethodTable: 7912ad90
EEClass: 7912b304
Size: 32(0x20) bytes
Array: Rank 1, Number of elements 4, Type CLASS
Element Methodtable: 790fc35c
[0] 012a1b50
[1] 012a1b6c
[2] null
[3] null
六、查看數組內對象的值
0:004> !do 012a1b50
Name: System.String
MethodTable: 790fcb30
EEClass: 790fca90
Size: 26(0x1a) bytes
(C:/WINDOWS/assembly/GAC_32/mscorlib/2.0.0.0__b77a5c561934e089/mscorlib.dll)
String: aaaa
Fields:
MT Field Offset Type VT Attr Value Name
791018e0 4000096 4 System.Int32 1 instance 5 m_arrayLength
791018e0 4000097 8 System.Int32 1 instance 4 m_stringLength
790fe534 4000098 c System.Char 1 instance 61 m_firstChar
790fcb30 4000099 10 System.String 0 shared static Empty
>> Domain:Value 00149c58:790d81bc <<
7912b1d8 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 00149c58:012a16f0 <<
====================
windbg使用小總結
【抓dump】
一、通常抓法
adplus -hang -p 3230 -quiet 抓3230 pid進程,hang模式,至關於把那個進程暫停住,取內存快照
adplus -crash -pn w3wp -quiet 抓w3wp進程,crash模式,當那個進程崩潰結束的時候自動抓取當時的內存
adplus -hang -iis -quiet 抓IIS相關進程,包括其上host的web應用,以及iis自身
二、抓window服務
http://support.microsoft.com/kb/824344/zh-cn
三、遠程抓
http://blog.joycode.com/tingwang/archive/2006/08/11/79763.aspx
四、抓藍屏和死機的dump
電腦無端重啓或者藍屏會在C:/WINDOWS/Minidump/下保存一個minidump,可是這個minidump可用的命令不多,通常只打!analyze –v看到是哪一個進程引發的,還有相關的驅動模塊就基本定位問題了。
五、IIS回收的時候抓
http://blog.yesky.com/blog/omakey/archive/2006/12/17/1618015.html
六、計劃任務抓
好比一個進程起來後不知道它何時會意外崩潰,能夠在計劃任務裏用crash裏抓,當那個進程意外終止的時候,cdb能夠直接附加上去,抓取當時的dump,若是要抓一些會自動重啓的進程,並且要抓每次重啓前的dump,能夠參考附錄裏一節。
【經常使用命令】
一、先path C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727,把.net路徑設置爲path環境變量,一遍在windbg裏能夠直接.load sos,而沒必要.load C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/sos.dll
二、ld demo,加載你程序的pdb文件,調試.net程序通常要把kernel32和mscorwks的符號加載上,關於這兩個東西你們能夠查資料,尤爲是後者有哪些函數能夠多瞭解一些。
三、在windbg的file/symbol file path對話框裏輸入如下文字,以便自動加載和下載符號
C:/WINDOWS/Symbols;d:/Program Files/Microsoft Visual Studio 8/SDK/v2.0/symbols;.sympath SRV*d:/localsymbols*http://msdl.microsoft.com/download/symbols
其中有windows、.net2.0和自動從網上下載的調試符號,注意根據本身的狀況適當修改目錄
【調試死鎖】
一、!syncblk,查看哪些線程拿到了鎖
二、~67e!clrstack 跳到某個拿到鎖的線程看它正在幹什麼操做,遲遲不願釋放鎖
三、!runaway 查看這個佔有鎖的線程運行了多長時間。
四、~*e!clrstack查看全部線程的託管堆棧,看看哪些是正在等待鎖的,好比hang在System.Threading.Monitor.Enter(System.Object)
五、~136s選擇該線程,顯示以下
0:000> ~136s eax=00005763 ebx=08deeb5c ecx=03eff0d4 edx=5570ab69 esi=08deeb5c edi=7ffd6000 eip=7c95ed54 esp=08deeb10 ebp=08deebb8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCallRet: 7c95ed54 c3 ret
找到ecx寄存器的值,複製後ctrl+f,向上查找,會找到!syncblk的地方,以下
0:000> !syncblk Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner 1906 03ee4be4 5 1 03ee8f88 22c8 67 185e2ef0 System.Object 5390 052ca39c 3 1 05292b30 1dd4 49 1060d3ac System.Object 9372 0530702c 15 1 0012d3a8 1aa8 80 185e7704 System.Object 11428 03eff0d4 35 1 053b8fa8 169c 120 166acd98 System.Object 15278 0531c6b4 61 1 06bc1430 26d8 86 1a5bea88 System.Object
能夠看到136線程等待的鎖被120號線程佔着不放(格式有點亂,湊合看),
六、有時候經過ecx寄存器找鎖不是很肯定,能夠用~* kb來把全部線程堆棧打出來,而後根據!syncblk出來的同步快的值去搜索大概有多少個線程在等那個鎖。由於一樣是等待鎖,可等的狀態不同,有的在Q裏,有的鎖已經升級,有的去嘗試去拿鎖了,因此不必定當時ecx寄存器指向那塊內存,具體如何找到某個正在等待鎖的線程等待的鎖的內存地址,以及它正等待的這個鎖被哪一個線程拿着,我還沒琢磨出規律來,但通常狀況下,若是有其它同步對象的話,更難查。.net裏用我上面說的幾步就能查出鎖的問題了。
【內存泄漏】
一、!dumpheap -stat看看哪些對象個數最多,佔內存最大,
二、找到某個格式比較多的對象,能夠看它的方法表,而後用!dumpheap -mt 66398fa4去隨機找幾個對象的地址
三、用!do 1e5a22bc命令去查看幾個對象的狀態,屬性的值等,看看正常不正常
四、用!gcroot -nostacks 1e5a22bc去查看幾個對象的根正常不正常,若是有些對象的根不是本身預先設計的那樣,極可能被本身沒想到的對象強引用了,因此GC沒法回收它,就泄漏了。
【CPU百分百】
主要用幾個計數器和!runaway命令,具體見如下連接
http://www.cnblogs.com/onlytianc ... 7/06/03/769307.html
【線程池耗盡】
!threadpool 能看到完成端口,線程池工做線程和timer回調各佔線程池的狀況。
【其它】
一、!eestack -short -ee查看全部重要(獲取鎖的,託管的,中止並容許回收的)線程的dumpstack,差很少至關於~*e!dumpstack
二、.time 能夠看到進程跑了多少時間
三、!dso 查看當前線程裏有哪些對象,分析內存泄漏問題也許會用到
【小結】
要想很好的用windbg排查.net問題,首先要了解一些clr宿主的基礎知識,以及IL的一些基礎,還有簡單的寄存器和彙編嘗試,再就是有個好的思路,最後就是經驗和對代碼邏輯的理解。
【附錄:寫了一個自動抓dump的工具,可在程序異常退出的時候抓dump】
【使用方法】
一、先在cmd下運行如下命令確保計劃任務開着
net start "task scheduler"
二、執行如下命令安排自動抓包
at 13:27 d:/myapp/autodump/processmon.exe
其中計劃啓動的時間和自動抓包的程序路徑要根據狀況設置,計劃啓動以前當前用戶必定要註銷。
【相關配置】
<appSettings>
<add key="adplusPath" value="D:/MyApp/Debugging/adplus.vbs"/><!--adplus的路徑-->
<add key="ProcessName" value="w3wp"/><!--要抓dump的進程的名字,可用部分名字,不用完整的-->
</appSettings>
【源碼】
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace ProcessMon
{
class Program
{
static readonly List<int> _dumpPIDs = new List<int>();
private static readonly string _processName = System.Configuration.ConfigurationManager.AppSettings["ProcessName"];
private static readonly string _adplusPath = System.Configuration.ConfigurationManager.AppSettings["adplusPath"];
static void Main(string[] args)
{
while(true)
{
Console.WriteLine("..");
ThreadProc();
Thread.Sleep(10000);
}
}
private static void ThreadProc()
{
foreach(Process vProcess in Process.GetProcesses())
{
try
{
string processName = vProcess.ProcessName.ToLower();
if (processName.IndexOf(_processName) >= 0)
{
Console.WriteLine("{0}-{1}", vProcess.ProcessName, vProcess.Id);
if (_dumpPIDs.Contains(vProcess.Id))
continue;
_dumpPIDs.Add(vProcess.Id);
DumpProcessDeg d = DumpProcess;
d.BeginInvoke(vProcess.Id, null, null);
DumpProcess(vProcess.Id);
}
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
private delegate void DumpProcessDeg(int pid);
static void DumpProcess(int pid)
{
ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo();
Info.FileName = _adplusPath;
Info.Arguments = string.Format("-crash -p {0} -quiet",pid);
Info.WorkingDirectory = "C://";
Process Proc ;
try
{
Proc = Process.Start(Info);
}
catch(System.ComponentModel.Win32Exception e)
{
Console.WriteLine("系統找不到指定的程序文件。/r{0}", e);
return;
}
Proc.EnableRaisingEvents = true;
Console.WriteLine("外部程序的開始執行時間:{0}", Proc.StartTime);
Proc.WaitForExit(60000);
if(Proc.HasExited == false){
Console.WriteLine("由主程序強行終止外部程序的運行!");
Proc.Kill();
}
else{
Console.WriteLine("由外部程序正常退出!");
}
Console.WriteLine("外部程序的結束運行時間:{0}", Proc.ExitTime);
Console.WriteLine("外部程序在結束運行時的返回值:{0}", Proc.ExitCode);
}
}
}
===============
補充幾個命令:
一、!analyze -v :用於分析掛掉線程的詳細情形,錯誤緣由。 二、!locks :列出所有資源使用狀況。 三、!locks -v 0x???????? :特定地址的死鎖分析。 四、!thread 0x????????:特定線程詳情。 五、.thread 0x????????:轉到某個線程堆棧。 |