Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

!DumpStackObjects displaying duplicate instances?

Tags:

c#

.net

windbg

sos

I have a small test program. When I look into stack object for main thread, its showing MyClass twice there. Any ideas why there are two object of MyClass on the stack?

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();  
    }  
}  
0:000> !DumpStackObjects  
OS Thread Id: 0xf74 (0)  
RSP/REG          Object           Name  
000000000023e9e8 00000000028f3c90 ConsoleApplication2.Program+MyClass  
000000000023e9f8 00000000028f3c90 ConsoleApplication2.Program+MyClass  
000000000023ea10 00000000028f3c70 System.Object[]    (System.String[])  
000000000023eb98 00000000028f3c70 System.Object[]    (System.String[])  
000000000023ed80 00000000028f3c70 System.Object[]    (System.String[])  
000000000023eda8 00000000028f3c70 System.Object[]    (System.String[])  
like image 533
imak Avatar asked Sep 28 '10 15:09

imak


1 Answers

Actually it shows that the one and only instance is referenced twice. Notice the values in the leftmost column differ. It could be different registers or different parts of the stack frame which point to the same instance.

In my experience this happens quite often (especially with debug builds). !dso is useful for locating objects and in that case the important column is the Object column, which holds the actual references.

E.g. if I run your example above from the debugger and place a break point on Main the output from dso looks like this just before the Main method returns.

0:000> !dso
OS Thread Id: 0x1944 (0)
ESP/REG  Object   Name
eax      0240b2e0 TestApp.Program+MyClass
ecx      0240b2e0 TestApp.Program+MyClass
0014F224 0240b2d0 System.Object[]    (System.String[])
0014F3CC 0240b2d0 System.Object[]    (System.String[])
0014F400 0240b2d0 System.Object[]    (System.String[])

As you can see both the eax and the ecx registers hold a reference to the instance despite the fact that the C# source only has a single reference.

If you look at the JIT compiled code for Main it looks like this

0:000> !u 00200070 
Normal JIT generated code
TestApp.Program.Main(System.String[])
Begin 00200070, size 46

C:\dev2010\TestApp\TestApp\Program.cs @ 33:
>>> 00200070 55              push    ebp
00200071 8bec            mov     ebp,esp
00200073 83ec14          sub     esp,14h
00200076 33c0            xor     eax,eax
00200078 8945f4          mov     dword ptr [ebp-0Ch],eax
0020007b 8945f8          mov     dword ptr [ebp-8],eax
0020007e 894dfc          mov     dword ptr [ebp-4],ecx
00200081 833d3c31150000  cmp     dword ptr ds:[15313Ch],0
00200088 7405            je      0020008f
0020008a e8c05ac268      call    clr!JIT_DbgIsJustMyCode (68e25b4f)
0020008f 33d2            xor     edx,edx
00200091 8955f0          mov     dword ptr [ebp-10h],edx
00200094 90              nop

C:\dev2010\TestApp\TestApp\Program.cs @ 35:
00200095 b960391500      mov     ecx,153960h (MT: TestApp.Program+MyClass)
0020009a e8811ff4ff      call    00142020 (JitHelp: CORINFO_HELP_NEWSFAST)
0020009f 8945ec          mov     dword ptr [ebp-14h],eax
002000a2 8b4dec          mov     ecx,dword ptr [ebp-14h]
002000a5 ff158c391500    call    dword ptr ds:[15398Ch]     (TestApp.Program+MyClass..ctor(), mdToken: 06000003)
002000ab 8b45ec          mov     eax,dword ptr [ebp-14h]
002000ae 8945f0          mov     dword ptr [ebp-10h],eax

C:\dev2010\TestApp\TestApp\Program.cs @ 36:
002000b1 90              nop
002000b2 8be5            mov     esp,ebp
002000b4 5d              pop     ebp
002000b5 c3              ret

Notice the instruction call 00142020. This creates the instance of MyClass and returns the reference in the eax register. Following that the reference is stored at dword ptr [ebp-14h].

The next instruction reads the value stored at dword ptr [ebp-14h] and stores the value in the ecx register, which is then used as input for the call to the constructor call dword ptr ds:[15398Ch] (TestApp.Program+MyClass..ctor(), mdToken: 06000003).

This explains why dso lists the reference twice in this case. However, you rarely need to go into the details about this when debugging.

like image 134
Brian Rasmussen Avatar answered Oct 17 '22 22:10

Brian Rasmussen