Question

I am new to Windbg and trying to understand a few things about value and refernece types .NET. Here is the code that i am using

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

The only reason i put sleep here is to give me time to attach Windbg with the running process. I know a better way of doing it might be to put a breakpoint but anyways here are my questions.

  1. When Windbg get attached to process, it breaks into this thread #3 but as can I can see there is no thread with managed thead ID 3. Is this thread just used by debugger? Are there any other threads that may not be displayed by !threads command? If so is there any command that can give me all 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 0000000000190c50 200a020 Enabled 00000000027f3ca8:00000000027f3fd0 0000000000187e40 0 MTA
2 2 106c 0000000000198430 b220 Enabled 0000000000000000:0000000000000000 0000000000187e40 0 MTA (Finalizer)

OSID Special thread type
1 e98 DbgHelper
2 106c Finalizer

0:003> !CLRStack
OS Thread Id: 0xe6c (3)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
0:003> kb
RetAddr : Args to Child : Call Site
0000000077978778 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : ntdll!DbgBreakPoint
00000000
776d466d : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : ntdll!DbgUiRemoteBreakin+0x38
00000000778d8791 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : KERNEL32!BaseThreadInitThunk+0xd
00000000
00000000 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : ntdll!RtlUserThreadStart+0x1d

  1. Thread 0 seem like the thread running my Main method. When I get the dump of stack object, its not showing MyStruct and for some reason showing MyClass twice. Any ideas why?

0:000> !CLRStack
OS Thread Id: 0xbbc (0)
Child-SP RetAddr Call Site
000000000031edb0 000007fef6b32012 ConsoleApplication2.Program.Main(System.String[])
0:000> !DumpStackObjects
OS Thread Id: 0xbbc (0)
RSP/REG Object Name
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

Was it helpful?

Solution

  1. The !Threads command displays only managed threads (it will discard purely native threads).
    To see all of your threads (both managed an unmanaged), you could use ~. (you could dump the native stacks using a command such as ~*kb [where * means "for every thread"]).

  2. The !dso command only displays reference types, and sometimes it could display "false positives" or just wrong data (you'll find that SOS have a bit of a buggy nature).

From SOS's help (!help):

!DumpStackObjects [-verify] [top stack [bottom stack]]

This command will display any managed objects it finds within the bounds of the current stack. Combined with the stack tracing commands like K and !CLRStack, it is a good aid to determining the values of locals and parameters.

If you use the -verify option, each non-static CLASS field of an object candidate is validated. This helps to eliminate false positives. It is not on by default because very often in a debugging scenario, you are interested in objects with invalid fields.

The abbreviation !dso can be used for brevity.

OTHER TIPS

When you attach to a running process the debugger injects a thread into the process and that is picked as the current thread. As Liran points out the SOS command !threads only lists managed threads.

For a simple console application you should expect to see two managed threads; the main thread running the application and the finalizer thread, which is started by the CLR. In my experience they are always numbered 0 and 2 respectively.

The !dso command shows references on the stack. It doesn't list value types. For that you can use the !clrstack command with -l for locals. Keep in mind that these will rarely be placed on the stack for optimized code and on x64 the calling convention makes it even harder to track these.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top