.netwindowsdebuggingwindbg

Can Windbg display thread names?


Windbg should understand the MS exception protocol used to pass thread names to a debugger.

I can't get this to work. Looking on the net there are many examples showing "~" thread lists with no thread names, and that's what I see. I'm debugging a .NET x86 process, and I've tried the WDK 8.1 x86 and x64 versions of Windbg.

Does anyone know if this feature is still available? What am I missing?


Solution

  • For .NET threads, the following works for "normal" Threads (manually created threads, since I don't know a way to name threadpool threads):

    A Thread is a class and thus can be found in the .NET managed heap:

    0:000>.loadby sos clr
    0:000> !dumpheap -stat -type Thread
          MT    Count    TotalSize Class Name
    ...
    725e4960       11          572 System.Threading.Thread
    

    Note that there is other output as well, since !dumpheap looks for parts of class names. The Method Table (MT) however, identifies a class uniquely, so that's what we use from now on:

    0:000> !dumpheap -short -mt 725e4960
    023123d0
    02312464
    02313c80
    ...
    

    These are the addresses of Thread objects. Since it is clean output, we can use it in a loop:

    0:000> .foreach (address {!dumpheap -short -mt 725e4960}) {.echo ${address} }
    023123d0
    02312464
    02313c80    
    ...
    

    Inside the loop, we can use the address to get more information about the thread. First, let's find out how a Thread looks like internally:

    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
    ...
    

    At offset +0xC (depending on the bitness!), there's the m_Name member. That's a string. Let's find out how a string looks like:

    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
    

    So, the first character of the string is at offset +0x08. Strings in .NET are Unicode, so we can view it with du:

    0:000> du poi(023123d0+c)+8
    02313c14  "My named thread 0"
    

    Combine all this knowledge into a single command:

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

    (formatted for readability, put it all in one line)

    If you try that, you'll find that it may output something like

    00000008  "????????????????????????????????"
    

    This happens when m_Name is null. If you care about that, you can add a check for null:

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

    (formatted for readability, put it all in one line)

    Other improvements:

    Final result:

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