【问题标题】:Can Windbg display thread names?Windbg 可以显示线程名称吗?
【发布时间】:2015-07-22 06:15:28
【问题描述】:

Windbg should understand MS exception protocol 用于将线程名称传递给调试器。

我无法让它工作。在网上看有很多例子显示没有线程名称的“~”线程列表,这就是我所看到的。我正在调试一个 .NET x86 进程,我已经尝试过 Windbg 的 WDK 8.1 x86 和 x64 版本。

有谁知道这个功能是否仍然可用?我错过了什么?

【问题讨论】:

标签: .net windows debugging windbg


【解决方案1】:

对于 .NET 线程,以下适用于“正常”Threads(手动创建的线程,因为我不知道如何命名线程池线程):

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_Namenull 时会发生这种情况。如果你关心这一点,你可以添加一个空检查:

.foreach (address {!dumpheap -short -mt 725e4960})
{
    .if (poi(${address}+c) != 0) {
        du poi(${address}+c)+8
    }
}

(为便于阅读而格式化,全部放在一行中)

其他改进:

  • 对线程 ID 执行相同操作
  • 美化输出(使用.printf 而不是dddu

最终结果:

.foreach (address {!dumpheap -short -mt 725e4960}) 
{
    .if (poi(${address}+c) != 0) 
    {
        .printf "%d ",poi(${address}+28);
        .printf "%mu\r\n", poi(${address}+c)+8
    }
}

【讨论】:

  • 线程池线程和普通线程没有什么不同,但是命名它们通常是没有意义的,因为它们是重复使用的。
  • 似乎 .NET 没有对线程名称使用第一次机会异常协议,尽管我没有时间验证这一点。感谢您的教程。我已将您的脚本添加到我很少使用的 WinDbg 备忘单中!
  • 我现在在一家从事 .Net 开发的公司工作。我刚刚使用了.foreach 循环,它工作正常。我只有一个问题:725e4960 的号码显然会因每个应用程序而改变。有没有办法在函数中得到这个,比如.foreach ... (!dumpheap -sort -mt $((get_ID(System.Threading.Thread)))? (我知道,我将 Windbg 与 bash 计算符号混合在一起,看起来很难看)
  • @Dominique:我认为这将构成它自己的好问题。你可以分配一个伪寄存器.foreach /pS 7 /ps 4 (token {!name2ee mscorlib.dll System.Threading.Thread}) { r $t0 = ${token}},然后你可以像!dumpheap -short -mt $t0一样使用它
【解决方案2】:

有谁知道这个功能是否仍然可用?

是的,此功能仍然可用。至少对于本机应用程序而言。

对于 .NET 应用程序,来自“How to: Set a Thread Name in Managed Code”的方法可能比引发异常更受欢迎。

【讨论】:

  • 你在说哪个功能?在 WinDbg 中命名线程或显示命名线程?请扩展一下如何在 WinDbg 中显示线程名称。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2021-03-16
  • 2021-06-07
相关资源
最近更新 更多