值类型vs引用类型通过WinDbg

时间:2010-09-27 20:20:39

标签: windbg sos

我是Windbg的新手,并尝试了解有关值和引用类型.NET的一些内容。这是我正在使用的代码

class Program
{
    struct MyStruct
    {
        int x;
        int y;
    }

    class MyClass 
    {
        int x;
        int y;
    }


    static void Main(string[] args)
    {
        MyStruct s ;
        MyClass c = new MyClass();
        Thread.Sleep(5 * 60 * 1000);
    }
}

我在这里睡觉的唯一原因是让我有时间将Windbg附加到正在运行的进程中。我知道一个更好的方法可能是设置一个断点,但无论如何这里都是我的问题。

  1. 当Windbg附加到进程时,它会进入这个线程#3但是我可以看到没有带有托管thead ID 3的线程。这个线程是否仅由调试器使用? 请问!threads命令可能无法显示任何其他线程吗?如果有,那么有什么命令可以给我所有线程吗?
  2. 0:003> !threads -special
    ThreadCount:2
    UnstartedThread:0
    BackgroundThread:1
    PendingThread:0
    DeadThread:0
    托管运行时:没有                                                 PreEmptive Lock        ID OSID ThreadOBJ状态GC GC分配上下文域计数APT异常

    0 1 bbc 0000000000190c50 200a020已启用00000000027f3ca8:00000000027f3fd0 0000000000187e40 0 MTA
       2 2 106c 0000000000198430 b220已启用0000000000000000:0000000000000000 0000000000187e40 0 MTA(终结器)

    OSID特殊螺纹类型
        1 e98 DbgHelper
        2 106c Finalizer

    0:003> !CLRStack
    操作系统线程ID:0xe6c(3)
    无法遍历托管堆栈。目前的线程可能不是一个 托管线程。您可以运行!threads来获取中的托管线程列表 过程中 0:003> KB
    RetAddr:Args to Child:呼叫站点
    00000000 77978778 : 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 : ntdll!DbgBreakPoint
    00000000
    776d466d:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000:ntdll!DbgUiRemoteBreakin + 0x38
    00000000 778d8791 : 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 : KERNEL32!BaseThreadInitThunk+0xd
    00000000
    00000000:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000:ntdll!RtlUserThreadStart + 0x1d

    1. 线程0看起来像运行我的Main方法的线程。当我获得堆栈对象的转储时,它没有显示MyStruct,并且出于某种原因显示MyClass两次。有什么想法吗?
    2. 0:000> !CLRStack
      操作系统线程ID:0xbbc(0)
      Child-SP RetAddr呼叫站点
      000000000031edb0 000007fef6b32012 ConsoleApplication2.Program.Main(System.String [])
      0:000> !DumpStackObjects
      操作系统线程ID:0xbbc(0)
      RSP / REG对象名称
      000000000031edd8 00000000027f3c90 ConsoleApplication2.Program + MyClass
      000000000031ede8 00000000027f3c90 ConsoleApplication2.Program + MyClass
      000000000031ee00 00000000027f3c70 System.Object [](System.String [])
      000000000031ef88 00000000027f3c70 System.Object [](System.String [])
      000000000031f170 00000000027f3c70 System.Object [](System.String [])
      000000000031f198 00000000027f3c70 System.Object [](System.String [])

      TIA

2 个答案:

答案 0 :(得分:3)

当您附加到正在运行的进程时,调试器会将一个线程注入进程,并将其作为当前线程选择。正如Liran所指出的那样,SOS命令!threads仅列出了托管线程。

对于一个简单的控制台应用程序,您应该会看到两个托管线程;运行应用程序的主线程和由CLR启动的终结器线程。根据我的经验,他们总是分别编号为0和2。

!dso命令显示堆栈上的引用。它不列出值类型。为此,您可以将!clrstack命令与-l一起用于本地人。请记住,这些很少会被放置在堆栈上以获得优化的代码,而在x64上,调用约定使得跟踪它们变得更加困难。

答案 1 :(得分:1)

  1. !Threads命令仅显示托管线程(它将丢弃纯粹的本机线程) 要查看所有线程(都是托管非托管),您可以使用~。 (您可以使用诸如~*kb之类的命令转储本机堆栈[其中*表示“对于每个线程”]。)

  2. !dso命令仅显示引用类型,有时它可能显示“误报”或只是错误的数据(您会发现SOS有点buggy nature)。

  3. 从SOS的帮助(!help):

      

    !DumpStackObjects [-verify] [顶部堆栈   [底部堆栈]]

         

    此命令将显示任何托管   它在范围内找到的对象   当前的堆栈。结合   堆栈跟踪命令,如K和   !CLRStack,这是一个很好的帮助   确定当地人的价值观   参数。

         

    如果您使用-verify选项,则每个   对象的非静态CLASS字段   候选人经过验证。这有助于   消除误报。它不是   在默认情况下,因为经常在一个   调试方案,你是   对无效的对象感兴趣   字段。

         

    缩写!dso可用于   简洁。