Windbg+SOS 分析托管程序
时间:2010-09-16 来源:DylanWind
转载Juqiang的Basic Windbg系列
代码:
代码 using System;using System.Collections;
using System.Text;
namespace SOSBasics
{
class Program
{
static void Main(string[] args)
{
MatrixWorld matrix = new MatrixWorld();
Console.WriteLine(matrix);
Console.WriteLine("=========================================================");
Console.WriteLine("到命令行下面,然后切换到windbg目录,执行adplus -hang -pn cosoleTry.exe -o c:\\dumps");
Console.ReadLine();
}
}
public class MatrixWorld
{
private int generation;
private double gold;
private string name;
private DateTime age;
private Hashtable systemKey;
private string[] leaders;
private object previousOne;
private Zion zion;
public MatrixWorld()
{
this.generation = 6;
this.gold = 123456789;
this.name = "The Matrix";
this.age = new DateTime(2099, 1, 1);
this.systemKey = new Hashtable();
this.systemKey.Add("Oracle", "会变脸的老女人");
this.systemKey.Add("Architect", "很帅的酷老头儿,应该去演Gandalf");
this.systemKey.Add("Smith", "徒为别人做嫁衣");
this.leaders = new string[] { "第一代", "第二代", "第三代", "第四代", "第五代", "NEO", "那个印度小女孩" };
this.previousOne = "NEO的前身,不知道是谁";
this.zion = new Zion();
this.zion.One = "NEO";
}
public int Generation { get { return this.generation; } }
public double Gold { get { return this.gold; } set { this.gold = value; } }
public string Name { get { return this.name; } }
public DateTime Age { get { return this.age; } }
public Hashtable SystemKey { get { return this.systemKey; } }
public string[] Leaders { get { return this.leaders; } }
public object PreviousOne { get { return this.previousOne; } }
public Zion Zion { get { return this.zion; } }
public override string ToString()
{
System.Text.StringBuilder sb = new StringBuilder(1024);
sb.Append(this.name); sb.Append(" time is "); sb.Append(this.age); sb.Append(System.Environment.NewLine);
sb.Append("Gold: "); sb.Append(this.gold); sb.Append(System.Environment.NewLine);
sb.Append("All key items in "); sb.Append(this.name); sb.Append(" listed here"); sb.Append(System.Environment.NewLine);
IEnumerator ite = this.systemKey.Keys.GetEnumerator();
while (ite.MoveNext())
{
sb.Append("\t");
sb.Append(ite.Current.ToString());
sb.Append(": ");
sb.Append(this.systemKey[ite.Current]);
sb.Append(System.Environment.NewLine);
}
sb.Append("历代救世主名单");
sb.Append(System.Environment.NewLine);
foreach (string leader in this.leaders)
{
sb.Append(leader);
sb.Append(System.Environment.NewLine);
}
sb.Append(System.Environment.NewLine);
sb.Append("其中,上一代救世主:");
sb.Append(this.previousOne);
sb.Append(System.Environment.NewLine);
sb.Append("ZION的资料是:");
sb.Append(this.zion);
return sb.ToString();
}
}
public class Zion
{
private string one;
public string One
{
get { return this.one; }
set { this.one = value; }
}
public override string ToString()
{
return this.one;
}
}
}
1. 打开Dump文件, windbg打开,Ctrl+D
2. ~ 列出所有线程 ~2s 进入第二个线程
3. kb 列出当前线程CallStack (非托管)
4. ~*kb 列出所有线程的callstack (非托管)
5. 装载SOS 命令 .load C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll
6. !clrstack 列出托管代码CallStack; 同样~*e!clrstack列出所有线程的callstack
章节1 栈分析
7. !dso(Dump Stack Objects的缩写),这个命令可以把当前栈上所有的变量都搞出来
8. !do (Dump Object), 查看变量信息 !do + 地址 如果需要查看帮助:!help dumpobj
下面列出的是变量MatrixWorld的所有Field信息
!do 02747724 |
The column VT contains the value 1 if the field is a valuetype structure, and 0 if the field contains a pointer to another object. For valuetypes, you can
take the MethodTable pointer in the MT column, and the Value and pass them to the command !DumpVC
可以看出VT列是1的int和double的值直接显示,
9. 特殊的是DateTime类型,需要 !DumpVC + MT + Value
!DumpVC 61f18148 02747748
Name: System.DateTime
MethodTable 61f18148
EEClass: 61cf0564
Size: 16(0x10) bytes
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
61f193ec 40000f4 0 System.UInt64 1 instance 662065056000000000 dateData
61f42c84 40000f0 30 System.Int32[] 0 shared static DaysToMonth365
>> Domain:Value 007567f0:02747950 <<
61f42c84 40000f1 34 System.Int32[] 0 shared static DaysToMonth366
>> Domain:Value 007567f0:02747990 <<
61f18148 40000f2 28 System.DateTime 1 shared static MinValue
>> Domain:Value 007567f0:02747930 <<
61f18148 40000f3 2c System.DateTime 1 shared static MaxValue
>> Domain:Value 007567f0:02747940 <<
10. 引用类型,!do+地址
11. 殊的对HashTable使用!do 027479d0 得到
!do 027479d0
Name: System.Collections.Hashtable
MethodTable: 61f4313c
EEClass: 61cfe93c
Size: 56(0x38) bytes
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
61f43238 400093c 4 ...ashtable+bucket[] 0 instance 02747a08 buckets
61f42d34 400093d 1c System.Int32 1 instance 3 count
61f42d34 400093e 20 System.Int32 1 instance 1 occupancy
61f42d34 400093f 24 System.Int32 1 instance 7 loadsize
61f3a32c 4000940 28 System.Single 1 instance 0.720000 loadFactor
61f42d34 4000941 2c System.Int32 1 instance 3 version
61f145b4 4000942 30 System.Boolean 1 instance 0 isWriterInProgress
61f42fb8 4000943 8 ...tions.ICollection 0 instance 0274a87c keys
61f42fb8 4000944 c ...tions.ICollection 0 instance 00000000 values
61f3c8c0 4000945 10 ...IEqualityComparer 0 instance 00000000 _keycomparer
61f40704 4000946 14 System.Object 0 instance 00000000 _syncRoot
61f2eee0 4000947 18 ...SerializationInfo 0 instance 00000000 m_siInfo
Count = 3 表示我们的HashTable有3个元素,
继续对buckets 运行 dd 02747a08 得到
dd 02747a08
02747a08 61f43238 0000000b 00000000 00000000
02747a18 00000000 00000000 00000000 00000000
02747a28 00000000 00000000 00000000 0274777c
02747a38 0274779c e047abd0 00000000 00000000
02747a48 00000000 00000000 00000000 00000000
02747a58 00000000 00000000 00000000 00000000
02747a68 00000000 00000000 00000000 00000000
02747a78 00000000 027477bc 027477e0 62e0f252
第一个0000000b,表明这个hashtable有11个对象。
12. !do -nofields 0274777c 得到HashTable第一个元素的Key,(-nofields不输出fields信息)
13. !do -nofields 0274779c 得到HashTable第一个元素的Value,
14. 对数组,用 !da 02747a98
0:000> !da 02747a98
Name: System.String[]
MethodTable: 61f142b8
EEClass: 61cfda64
Size: 44(0x2c) bytes
Array: Rank 1, Number of elements 7, Type CLASS
Element Methodtable: 61f40ae8
[0] 02747854
[1] 0274786c
[2] 02747884
[3] 0274789c
[4] 027478b4
[5] 027478cc
[6] 027478e4
0:000> !do 02747854
然后对其中任意一个!do 或者直接
15. !da -details 02747a98 列出数组中所有元素的详细信息
16 对自定义类型也是使用!do !do 层层剥皮
章节2 堆分析
17. !eeheap –gc
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x027107b4
generation 1 starts at 0x0270100c
generation 2 starts at 0x02701000
ephemeral segment allocation context: none
segment begin allocated size
02700000 02701000 0274b4e0 0x0004a4e0(304352)
Large object heap starts at 0x03701000
segment begin allocated size
03700000 03701000 03703250 0x00002250(8784)
Total Size 0x4c730(313136)
------------------------------
GC Heap Size 0x4c730(313136)
GC有三个generation,分别是0、1、2
整个gc heap的size是:313136,大概三百多K
查看抓到的Dump文件,有50M左右,说明大部分都是非托管资源占用的内存。
18. !dumpheap –stat 把托管堆上所有的东西都扒出来, -stat 按照托管资源的类型group by; 取得帮助:
!help dumpheap
19. !dumpheap -mt <methodtable>
20. !dumpheap –min 8000 查看所有8000个以上字节的对象
!dumpheap -min 8000
Address MT Size
02704b34 61f142b8 8208
027107c0 61f40ae8 45832
02720458 61f43558 39684
027354a8 61f40ae8 9280
027385b0 61f43558 9012
0273fb58 61f43558 22920
total 6 objects
Statistics:
MT Count TotalSize Class Name
61f142b8 1 8208 System.Object[]
61f40ae8 2 55112 System.String
61f43558 3 71616 System.Byte[]
Total 6 objects
一共有6个对象,其中1个object数组,2个字符串,3个byte数组, 其实我们得到的61f40ae8是所有string的table地址,对数组进行查看:
21. !dumpheap -mt 61f40ae8 -min 800 查看字节数超过800的string对象
Address MT Size
02701fc4 61f40ae8 2216
0270286c 61f40ae8 3384
027107c0 61f40ae8 45832
027354a8 61f40ae8 9280
02749478 61f40ae8 2068
total 5 objects
Statistics:
MT Count TotalSize Class Name
61f40ae8 5 62780 System.String
可以看到一共有5个符合我们要求的string,
运行!do -nofields 02749478可以看到这个string就是我们控制台输出的string,就是我们代码里的stringbuilder变量。
运行!do –nofields 027107c0是一个xml文件,是CLR自己维护的
22. !dumpheap -type String(Hashtable)根据类型查看