Basic Windbg - 1. SOSBasics

看代码先。这是一个控制台程序,通过这个程序,我们来初步接触一下windbg如何使用。(代码你可以自己创建一个console application,然后把我下面代码全部覆盖上去即可,用vs2002/03/05/08皆可)请耐着性子,下拉滚动条,看下面的介绍。

  1using System;
  2using System.Collections;
  3using System.Text;
  4
  5namespace SOSBasics
  6{
  7    class Program
  8    {
  9        static void Main(string[] args)
 10        {
 11            MatrixWorld matrix = new MatrixWorld();
 12            Console.WriteLine(matrix);
 13            Console.WriteLine("=========================================================");
 14            Console.WriteLine("到命令行下面,然后切换到windbg目录,执行adplus -hang -pn sosbasics.exe -o c://dumps");
 15            Console.ReadLine();
 16        }

 17    }

 18
 19    public class MatrixWorld
 20    {
 21        private int generation;
 22        private double gold;
 23        private string name;
 24        private DateTime age;
 25        private Hashtable systemKey;
 26        private string[] leaders;
 27        private object previousOne;
 28
 29        private Zion zion;
 30
 31        public MatrixWorld()
 32        {
 33            this.generation = 6;
 34            this.gold = 123456789;
 35            this.name = "The Matrix";
 36            this.age = new DateTime(209911);
 37            this.systemKey = new Hashtable();
 38            
 39            this.systemKey.Add("Oracle""会变脸的老女人");
 40            this.systemKey.Add("Architect""很帅的酷老头儿,应该去演Gandalf");
 41            this.systemKey.Add("Smith""徒为别人做嫁衣");
 42
 43            this.leaders = new string[] "第一代""第二代""第三代""第四代""第五代""NEO""那个印度小女孩" };
 44            this.previousOne = "NEO的前身,不知道是谁";
 45
 46            this.zion = new Zion();
 47            this.zion.One = "NEO";
 48        }

 49
 50        public int Generation get return this.generation; } }
 51        public double Gold get return this.gold; } set this.gold = value; } }
 52        public string Name get return this.name; } }
 53        public DateTime Age get return this.age; } }
 54        public Hashtable SystemKey get return this.systemKey; } }
 55        public string[] Leaders get return this.leaders; } }
 56        public object PreviousOne get return this.previousOne; } }
 57        public Zion Zion get return this.zion; } }
 58
 59        public override string ToString()
 60        {
 61            System.Text.StringBuilder sb = new StringBuilder(1024);
 62            
 63            sb.Append(this.name); sb.Append(" time is "); sb.Append(this.age); sb.Append(System.Environment.NewLine);
 64            sb.Append("Gold: "); sb.Append(this.gold); sb.Append(System.Environment.NewLine);
 65            sb.Append("All key items in "); sb.Append(this.name); sb.Append(" listed here"); sb.Append(System.Environment.NewLine);
 66
 67            IEnumerator ite = this.systemKey.Keys.GetEnumerator();
 68            while (ite.MoveNext())
 69            {
 70                sb.Append("/t");
 71                sb.Append(ite.Current.ToString());
 72                sb.Append("");
 73                sb.Append(this.systemKey[ite.Current]);
 74                sb.Append(System.Environment.NewLine);
 75            }

 76
 77            sb.Append("历代救世主名单");
 78            sb.Append(System.Environment.NewLine);
 79            foreach(string leader in this.leaders){
 80                sb.Append(leader);
 81                sb.Append(System.Environment.NewLine);
 82            }

 83
 84            sb.Append(System.Environment.NewLine);
 85            sb.Append("其中,上一代救世主:");
 86            sb.Append(this.previousOne);
 87
 88            sb.Append(System.Environment.NewLine);
 89            sb.Append("ZION的资料是:");
 90            sb.Append(this.zion);
 91
 92            return sb.ToString();
 93        }

 94    }

 95
 96    public class Zion
 97    {
 98        private string one;
 99
100        public string One
101        {
102            get return this.one; }
103            set this.one = value; }
104        }

105
106        public override string ToString()
107        {
108            return this.one;
109        }

110    }

111}

112


1、按照Main里面说的办法,抓下来一个dump文件
2、用windbg打开,快捷键是Ctrl+D

首先映入眼帘的,是这些东西
eax=00000003 ebx=016a0000 ecx=002df100 edx=00000008 esi=002deee4 edi=00010588
eip=76f60f34 esp=002dee84 ebp=002deea4 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:
76f60f34 c3              ret

什么含义,先不用管它。我们来看第一个命令,~(波浪线,就是你的字母1旁边的那个)
0:000> ~
.  0  Id: 1244.1248 Suspend: 1 Teb: 7ffdf000 Unfrozen
   1  Id: 1244.125c Suspend: 1 Teb: 7ffdd000 Unfrozen
   2  Id: 1244.1268 Suspend: 1 Teb: 7ffdc000 Unfrozen

结果是有三行。这表明,当前程序有三个线程。每一行最左面,是0、1、2,线程的序号。注意上面的第一行,0左面有一个点,表明这是当前线程。如果要切换线程,用波浪线+序号+s来切换,如切换到线程2,那么用~2s即可。

现在只有线程信息,对于每个线程,在被抓的那一刻,在执行什么,我们有命令:kb
K打头的有好多,我们就先看这个kb吧!首先切换到线程2(执行~2s),然后敲kb,回车。
这里要注意,如果你第一次看dump,那么windbg会自动根据前文介绍的那个symbolspath去下载匹配你这个dump文件的symbols,so,时间比较长。你会看到命令窗口最左面,有一个×BUSY×的提示,这表明它在下载东西。好奇一点,你可以打开资源管理器,到c:/symcache(就是前文说的那个symbolspath的本地缓存路径),不停的刷新,你会看到几个目录,及里面的文件在不停的增长。
经过漫长的等待,结果出来了,看一下!
0:002> kb
ChildEBP RetAddr  Args to Child             
0373f650 76f60690 75ee7e09 00000002 0373f6a4 ntdll!KiFastSystemCallRet
0373f654 75ee7e09 00000002 0373f6a4 00000001 ntdll!ZwWaitForMultipleObjects+0xc
0373f6f0 75ee8150 0373f6a4 7a3b84e0 00000000 kernel32!WaitForMultipleObjectsEx+0x11d
0373f70c 79ef224b 00000002 7a3b84e0 00000000 kernel32!WaitForMultipleObjects+0x18
0373f72c 79fb997b 004b99d8 0373f830 004b9a08 mscorwks!WKS::WaitForFinalizerEvent+0x77
0373f740 79ef3207 0373f830 00000000 00000000 mscorwks!WKS::GCHeap::FinalizerThreadWorker+0x79
0373f754 79ef31a3 0373f830 0373f7dc 79f91478 mscorwks!Thread::DoADCallBack+0x32a
0373f7e8 79ef30c3 0373f830 d89770b4 00000000 mscorwks!Thread::ShouldChangeAbortToUnload+0xe3
0373f824 79fb9643 0373f830 00000000 004b54a8 mscorwks!Thread::ShouldChangeAbortToUnload+0x30a
0373f84c 79fb960d 79fb990e 00000008 0373f894 mscorwks!ManagedThreadBase_NoADTransition+0x32
0373f85c 79fba09b 79fb990e d8977004 00000000 mscorwks!ManagedThreadBase::FinalizerBase+0xd
0373f894 79f95a2e 00000000 00000000 00000000 mscorwks!WKS::GCHeap::FinalizerThreadStart+0xbb
0373f930 75ee3833 004b9a08 0373f97c 76f3a9bd mscorwks!Thread::intermediateThreadProc+0x49
0373f93c 76f3a9bd 004b9a08 03739f20 00000000 kernel32!BaseThreadInitThunk+0xe
0373f97c 00000000 79f959e8 004b9a08 00000000 ntdll!_RtlUserThreadStart+0x23


记住一点,这个东西叫做callstack,是需要从下往上看的。即,最下面的是执行的源头,最上面的,是执行的最后一句。(不过,看这个,我们基本看不出啥东西来。)
如果你的线程很多,如一个asp.net程序,那么aspnet_wp或者w3wp里面会有几十号甚至几百号线程,看每个线程的代码,都这么切换,那就要累死了!so,我们看下一个命令,如何查看所有线程的代码。
命令是:~* kb,就是波浪线+星号+kb
等待一会,所有线程信息都会出来。注意一点,可能你会看到这个信息
*** WARNING: Unable to verify checksum for mscorlib.ni.dll
WARNING: Frame IP not in any known module. Following frames may be wrong.

002df0c8 7948d2bb 015765c0 0157651c 002df128 0x40a743
002df110 7948d1ed 002df128 00000000 00000100 mscorlib_ni+0x3cd2bb

这两个,看葡萄那本书去,上面有介绍。
上面看到的线程信息,包括相应的kb命令,都是非托管代码的,即:unmanaged code,那么,怎么看到.net的相关信息呢?这时候就需要SOS了,我们在命令窗口敲:.load clr20/sos.dll,就是"点load clr20/sos.dll"(如果你是framework 1.x,那么就换为.load clr10/sos.dll)
这个时候,如果要看managed code的信息,就可以用!clrstack命令来看(注意前面有一个叹号)。同理,如果看当前线程的,就直接敲!clrstack,如果看其他线程的,就波浪线+线程序号+s切换过去,然后再敲!clrstack。如果看全部线程的managed code信息,就用~* e!clrstack(注意,这里多了一个e叹号)
我们现在在thread 2里面,那么敲!clrstack,看看结果:
0:002> .load clr20/sos.dll
0:002> !clrstack
OS Thread Id: 0x1268 (2)
Failed to start stack walk: 80004005


出错了,什么原因?以后再说。那么切换到1号去看看:
0:002> ~1s
eax=79f4e71f ebx=00000003 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=76f60f34 esp=0150f908 ebp=0150f9a4 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:
76f60f34 c3              ret
0:001> !clrstack
OS Thread Id: 0x125c (1)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process

哦,这不是一个托管线程,so,看不到。继续到0号去看看:
0:001> ~0s
eax=00000003 ebx=016a0000 ecx=002df100 edx=00000008 esi=002deee4 edi=00010588
eip=76f60f34 esp=002dee84 ebp=002deea4 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:
76f60f34 c3              ret
0:000> !clrstack
OS Thread Id: 0x1248 (0)
ESP       EIP    
002df0e0 76f60f34 [NDirectMethodFrameStandaloneCleanup: 002df0e0] System.IO.__ConsoleStream.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
002df0fc 7948d2bb System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)
002df128 7948d1ed System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
002df148 793a3350 System.IO.StreamReader.ReadBuffer()
002df158 793aaa2f System.IO.StreamReader.ReadLine()
002df16c 79497b5a System.IO.TextReader+SyncTextReader.ReadLine()
002df174 793e99f0 System.Console.ReadLine()
002df178 00a400c2 SOSBasics.Program.Main(System.String[])
002df39c 79e7c74b [GCFrame: 002df39c]

很好,很和谐!依据上面的原则,我们从下往上看,倒数第二行,是Main函数,能看出来我的namespace是SOSBasics,然后上一行是Console.ReadLine,然后是System.IO什么的。用脚也能想出来,这应该是.NET内部的实现代码。一直看到最上面一行,我们大致能猜测出来,这个线程信息表明,程序正在等待你敲一个回车。
(这个回车咋来的?我代码里面写的,方便大家来抓dump的)

再来实验一下,查看全部托管线程代码,命令:~* e!clrstack,就是波浪线+星号+空格+e+叹号+clrstack,大家自己实验一下吧!

这个帖子太长了,另开一个,我们开始看另外几个命令)
 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

juqiang

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值