2

我是 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);
    }
}

我把 sleep 放在这里的唯一原因是让我有时间将 Windbg 与正在运行的进程连接起来。我知道一个更好的方法可能是设置一个断点,但无论如何这是我的问题。

  1. 当 Windbg 附加到进程时,它会进入此线程 #3,但我可以看到没有托管 thead ID 为 3 的线程。该线程仅由调试器使用吗?是否有任何其他线程可能无法通过 !threads 命令显示?如果是这样,是否有任何命令可以给我所有线程?

0:003> !threads -special
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive Lock ID OSID ThreadOBJ State GC GC Alloc Context Domain Count APT Exception

0 1 BBC 000000000000190C50 200A
00000000000000/8020 0 MTA(终结器)启用00000000027F3CA8:00000000000000000000/20>>>>>>>>>>>>>>>>>>

OSID 特殊线程类型
1 e98 DbgHelper
2 106c 终结 器

0:003> !CLRStack
OS 线程 ID:0xe6c (3)
无法遍历托管堆栈。当前线程可能不是
托管线程。您可以运行的线程来获得在管理线程的列表!
进程
0:003> KB
RetAddr:参数以子:呼叫站点
00000000 77978778 : 0000000000000000 00000000 00000000 0000000000000000 00000000 776d466d:00000000 00000000 00000000 00000000:NTDLL DbgUiRemoteBreakin + 0x38 00000000 00000000 00000000 00000000 00000000 00000000:00000000 00000000 00000000 00000000:ntdll!RtlUserThreadStart+0x1d 00000000 : ntdll!DbgBreakPoint
00000000
00000000 0000000000000000 00000000
778d8791 : 0000000000000000 0000000000000000 : KERNEL32!BaseThreadInitThunk+0xd
00000000
00000000 0000000000000000 00000000

  1. 线程 0 看起来像是运行我的 Main 方法的线程。当我得到堆栈对象的转储时,它没有显示 MyStruct 并且由于某种原因显示 MyClass 两次。任何想法为什么?

0:000> !CLRStack
OS 线程 ID: 0xbbc (0)
Child-SP RetAddr 调用站点
000000000031edb0 000007fef6b32012 ConsoleApplication2.Program.Main(System.String[])
0:000> !DumpStackObjects
OS 线程 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

4

2 回答 2

3

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

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

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

于 2010-09-29T04:50:51.343 回答
1
  1. !Threads命令仅显示托管线程(它将丢弃纯本机线程)。
    要查看所有线程(均管理非托管),您可以使用~. ~*kb(您可以使用[where*表示“针对每个线程”] 之类的命令转储本机堆栈)。

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

来自 SOS 的帮助 ( !help):

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

此命令将显示它在当前堆栈范围内找到的任何托管对象。与 K 和 !CLRStack 等堆栈跟踪命令结合使用,可以很好地帮助确定局部变量和参数的值。

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

缩写 !dso 可用于简洁。

于 2010-09-27T21:09:05.300 回答