如何手工抓取dump檔案

來源:互聯網
上載者:User

在生產環境下進行故障診斷時,為了不終止正在啟動並執行服務或應用程式,有兩種方式可以對正在啟動並執行服務或應用程式的進程進行分析和調試。

首先一種比較直觀簡潔的方式就是用WinDbg等調試器直接attach到需要調試的進程,調試完畢之後再detach即可。但是這種方式有個缺點就是執行debugger命令時必須先break這個進程,執行完debug命令之後又得趕緊F5讓他繼續運行,因為被你break住的時候意味著整個進程也已經被你掛起。另外也經常會由於First Chance Excetpion而自動break,你得時刻留意避免長時間break整個進程。所以這樣的調試方式對時間是個很大的考驗,往往沒有充裕的時間來做仔細分析。

另一種方式則是在出現問題的時候,比如CPU持續長時間100%,記憶體突然暴漲等非正常情況下,通過對服務進程snapshot抓取一個dump檔案,完成dump之後先deatch,讓進程繼續運行。然後用windbg等工具來分析這個抓取到的dump檔案。

那麼如何在不終止進程的情況下抓取dump檔案呢?Debugging Tools for Windows裡提供了一個非常好的工具,adplus.vbs。從名字可以看出,實際上是一個vb指令碼,只是對cdb調試器作的一個封裝指令碼。

其路徑與Debugging Tools for Windows的安裝路徑相同,使用的方法也很簡單,如下所示:

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則是核心模式和使用者模式兩者皆可。

而總所盩厔的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資訊等可以使用以下兩種方式: 
  1、使用VS2005 + sos.dll 
  2、使用Windbg + sos.dll 
  第二種方式功能更加強大,下面我就通過實際操作展示一下怎麼使用這種方法得到運行時ArrayList內部的值。 
  有人可能會說:我直接用Visual Studio的單步調試豈不是更快?當然,這個只是一個示範,通過這個示範是為以後的進階調試打下基礎 
   
  在操作之前,先熟悉一下基本知識: 
  A、使用VS2005 + sos.dll調試 
  1、需要在項目->屬性->調試-〉啟用Unmanaged 程式碼調試 
  2、開啟調試-〉視窗-〉即時 
  3、在即時視窗中輸入 !load sos 載入調試模組 
  4、輸入其它調試語句 
   
  B、使用Windbg + sos.dll 
  1、去微軟的網站下載最新的Windbg 
  2、開啟Windbg在File-〉Symbol File Path ...視窗中輸入 srv*c:/symbols*http://msdl.microsoft.com/download/symbols 
  3、運行需要調試的程式,然後在Windbg中File-〉Attach to Process中選擇剛才啟動並執行程式 
  4、在出現的Command視窗中就可以輸入調試語句 
  5、常用調試語句: 
   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 //查看數組資訊 
   下面就來看看具體的調試步驟: 
  1、我們的測試代碼 
   
   
  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到這個進程 
   
  2、查看所有堆棧資訊 
  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外,還有很多其它的系統資訊,先不用管它 
   
  3、查看我們的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 
   
  4、查看對應地址內部實際的值 
  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[]類型的數組 
   
  5、查看數組資訊 
  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 
   
  6、查看數組內對象的值 
  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】
1、一般抓法
adplus -hang -p 3230 -quiet 抓3230 pid進程,hang模式,相當於把那個進程暫停住,取記憶體快照
adplus -crash -pn w3wp -quiet 抓w3wp進程,crash模式,當那個進程崩潰結束的時候自動抓取當時的記憶體
adplus -hang -iis -quiet 抓IIS相關進程,包括其上host的web應用,以及iis自身
2、抓window服務
http://support.microsoft.com/kb/824344/zh-cn
3、遠程抓
http://blog.joycode.com/tingwang/archive/2006/08/11/79763.aspx
4、抓藍屏和死機的dump
電腦無故重啟或者藍屏會在C:/WINDOWS/Minidump/下儲存一個minidump,但是這個minidump可用的命令很少,一般只打!analyze –v看到是哪個進程引起的,還有相關的驅動模組就基本定位問題了。
5、IIS回收的時候抓
http://blog.yesky.com/blog/omakey/archive/2006/12/17/1618015.html
6、計劃任務抓
比如一個進程起來後不知道它什麼時候會意外崩潰,可以在計劃任務裡用crash裡抓,當那個進程意外終止的時候,cdb可以直接附加上去,抓取當時的dump,如果要抓一些會自動重啟的進程,而且要抓每次重啟前的dump,可以參考附錄裡一節。

【常用命令】
1、先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
2、ld demo,載入你程式的pdb檔案,調試.net程式一般要把kernel32和mscorwks的符號載入上,關於這兩個東西大家可以查資料,尤其是後者有哪些函數可以多瞭解一些。
3、在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和自動從網上下載的偵錯符號,注意根據自己的情況適當修改目錄

【調試死結】
1、!syncblk,查看哪些線程拿到了鎖
2、~67e!clrstack 跳到某個拿到鎖的線程看它正在幹什麼操作,遲遲不肯釋放鎖
3、!runaway 查看這個佔有鎖的線程運行了多長時間。
4、~*e!clrstack查看所有線程的託管堆棧,看看哪些是正在等待鎖的,比如hang在System.Threading.Monitor.Enter(System.Object) 
5、~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號線程佔著不放(格式有點亂,湊合看),
6、有時候通過ecx寄存器找鎖不是很確定,可以用~* kb來把所有線程堆棧打出來,然後根據!syncblk出來的同步快的值去搜尋大概有多少個線程在等那個鎖。因為同樣是等待鎖,可等的狀態不一樣,有的在Q裡,有的鎖已經升級,有的去嘗試去拿鎖了,所以不一定當時ecx寄存器指向那塊記憶體,具體如何找到某個正在等待鎖的線程等待的鎖的記憶體位址,以及它正等待的這個鎖被哪個線程拿著,我還沒琢磨出規律來,但一般情況下,如果有其它同步對象的話,更難查。.net裡用我上面說的幾步就能查出鎖的問題了。

【記憶體流失】
1、!dumpheap -stat看看哪些對象個數最多,占記憶體最大,
2、找到某個格式比較多的對象,可以看它的方法表,然後用!dumpheap -mt 66398fa4去隨機找幾個對象的地址
3、用!do 1e5a22bc命令去查看幾個對象的狀態,屬性的值等,看看正常不正常
4、用!gcroot -nostacks 1e5a22bc去查看幾個對象的根正常不正常,如果有些對象的根不是自己預先設計的那樣,很可能被自己沒想到的對象強引用了,所以GC無法回收它,就泄漏了。
【CPU百分百】
主要用幾個計數器和!runaway命令,具體見以下連結
http://www.cnblogs.com/onlytianc ... 7/06/03/769307.html
【線程池耗盡】
!threadpool 能看到完成連接埠,線程池背景工作執行緒和timer回調各佔線程池的情況。
【其它】
1、!eestack -short -ee查看所有重要(擷取鎖的,託管的,停止並允許回收的)線程的dumpstack,差不多相當於~*e!dumpstack
2、.time 可以看到進程跑了多少時間
3、!dso 查看當前線程裡有哪些對象,分析記憶體流失問題也許會用到
【小結】
要想很好的用windbg排查.net問題,首先要瞭解一些clr宿主的基礎知識,以及IL的一些基礎,還有簡單的寄存器和彙編嘗試,再就是有個好的思路,最後就是經驗和對代碼邏輯的理解。

【附錄:寫了一個自動抓dump的工具,可在程式異常退出的時候抓dump】
【使用方法】
1、先在cmd下運行以下命令確保計劃任務開著
net start "task scheduler"
2、執行以下命令安排自動抓包
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);
        }
    }

===============
補充幾個命令:

1、!analyze -v :用於分析掛掉線程的詳細情形,錯誤原因。

2、!locks :列出全部資源使用方式。

3、!locks -v 0x???????? :特定地址的死結分析。

4、!thread 0x????????:特定線程詳情。

5、.thread 0x????????:轉到某個線程堆棧。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.