对于 .NET 线程,以下内容适用于“正常”Thread
s (手动创建线程,因为我不知道命名线程池线程的方法):
A Thread
是一个类,因此可以在 .NET 托管堆中找到:
0:000>.loadby sos clr
0:000> !dumpheap -stat -type Thread
MT Count TotalSize Class Name
...
725e4960 11 572 System.Threading.Thread
请注意,还有其他输出,因为!dumpheap
查找类名的一部分。然而,方法表(MT)唯一地标识了一个类,所以这就是我们从现在开始使用的:
0:000> !dumpheap -short -mt 725e4960
023123d0
02312464
02313c80
...
这些是地址Thread
对象。由于它是干净的输出,我们可以在循环中使用它:
0:000> .foreach (address {!dumpheap -short -mt 725e4960}) {.echo ${address} }
023123d0
02312464
02313c80
...
在循环内部,我们可以使用地址来获取有关线程的更多信息。首先,让我们了解一下线程的内部结构:
0:000> !do 023123d0
Name: System.Threading.Thread
...
Fields:
MT Field Offset Type VT Attr Value Name
...
725e3e18 400076e c System.String 0 instance 02313c0c m_Name
...
偏移处+0xC
(取决于位数!),有m_Name
成员。那是一个字符串。让我们看看字符串是什么样子的:
0:000> !do poi(023123d0+c)
Name: System.String
...
Fields:
MT Field Offset Type VT Attr Value Name
...
725e4810 40000ac 8 System.Char 1 instance 4d m_firstChar
所以,字符串的第一个字符位于偏移量处+0x08
。 .NET 中的字符串是 Unicode,因此我们可以使用以下命令查看它du
:
0:000> du poi(023123d0+c)+8
02313c14 "My named thread 0"
将所有这些知识合并到一个命令中:
.foreach (address {!dumpheap -short -mt 725e4960})
{
du poi(${address}+c)+8
}
(为了便于阅读而格式化,将其全部放在一行中)
如果你尝试这样做,你会发现它可能会输出类似的内容
00000008 "????????????????????????????????"
发生这种情况时m_Name
is null
。如果您关心这一点,可以添加对 null 的检查:
.foreach (address {!dumpheap -short -mt 725e4960})
{
.if (poi(${address}+c) != 0) {
du poi(${address}+c)+8
}
}
(为了便于阅读而格式化,将其全部放在一行中)
其他改进:
- 对线程 ID 执行相同操作
- 美化输出(使用
.printf
代替dd
and du
)
最后结果:
.foreach (address {!dumpheap -short -mt 725e4960})
{
.if (poi(${address}+c) != 0)
{
.printf "%d ",poi(${address}+28);
.printf "%mu\r\n", poi(${address}+c)+8
}
}