tags:

views:

57

answers:

2

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

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

Liran
Thanks Liran for your answer.So what is the best way of getting value types on stack? I guess I can run !clrstack -a, but not sure what else can I do to get to MyStruct in this case, coz I cannot use !do on value types.
imak
Oh yes one more thing, the code below is when I am running this app on 64 bit system and as you can see Myclass size is 24 bytes but when I run it on a 32-bit machine the size is 16 bytes. Not sure what causing this difference???
imak
I tried copy + paste code from windbg output but for some reason its not letting me copy paste that many character in the comment. May be I am doing something wrong as i am new to this forum
imak
@user459876, 1) You can dump the content of a ValueType using the !DumpVC command. 2) The size difference can be explained by padding and pointer size differences. Remember that the Size SOS show's includes the size of the ObjHeader (that lays before the instance's data).
Liran
When looking at the output of !ClrStack -a command, is there anyway to determine which is ref type or which is value type, so that I can know if I need to run !DumpVC or !do on that pointer
imak
Also can you please elaborate what you mean by padding here. I undersand that object will include a synblock part but that should account for 4 bytes only as on 32-bit this will be 4 bytes whereas on 64-bit this will be 8 byte.
imak
@user459876, 1) I recommend that you'll try to divide your sub-question into new questions in StackOverflow (as you can see, the comments aren't really meant for that). 2) Issuing the !do command on a value type will usually fail (except in some rare cases). Also, you can use SOS to dump the variable's structure and see if its a reference or a value type (when issuing the !dumpVc command, you will need to have the type's MethodTable address anyway).
Liran
Yeah comments are not working well for asking new questions; Thanks for your help. I am going to post a new question about it.
imak
@user459876, sorry for the delay, but the size differences reflect the size difference in the header. The change in the bittness cause the extra 8 bytes to be allocated (before the Type pointer, there's a signed integer that represents SyncBlock index. When moving to x64, that index is padded with additional 4 bytes).
Liran
A: 

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.

Brian Rasmussen