Debugging Generic System.Nullable within Windbg
In this post I am going to unravel the mystery of debugging the Nullable<T> within Windbg in .NET 3.5 and also compare it with .NET 4.0. Here is the sample code and it is compiled in .NET 3.5
using System;
namespace ConsoleApplication
{
class Program
{
Int32? test;
int i = 10;
static void Main(string[] args)
{
Nullable<T>
Int32? i = 10;
Object o = 10;
var p = new Program() { test = 20 };
Console.Read();
p.test = (Int32?) o ;
Console.WriteLine(p.test.HasValue);
}
}
}
Attached to the debugger on the Console.Read. FYI I always load sos and sosex extensions to debug managed code. Here is !mdt 0x0253c11c output
0:000> !mdt 0x0253c11c
0253c11c (ConsoleApplication.Program)
test:ERROR (0×80070057).
i:0xa (System.Int32)
Notice that “test” does not have a value and has an error. Next issued !dumpobj
!do 0x0253c11c
0:000> !do 0x0253c11c
Name: ConsoleApplication.Program
MethodTable: 002932f0
EEClass: 00291360
Size: 20(0×14) bytes
(C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe)
Fields:
MT Field Offset Type VT Attr Value Name
00000000 4000001 8 1 instance 0280c124 test
7776ab0c 4000002 4 System.Int32 1 instance 10 i
My fault ,I thought sos should be able to get the MethodTable of Nullable<Int32> for “test” when sosex couldn’t. To my surprise the MT output was 00000000 . To view the contents of the “test” I would have to use the !dumpvc which requires methodtable. I know I could use the dd command. And here is the output from the dd 0280c124
0:000> dd 0280c124
0280c124 00000001 00000014 00000000 77767c70
0280c134 00000000 00000000 00000000 00000000
0280c144 00000000 00000000 777684dc 00000000
0280c154 40010000 7776d7ec 00000003 00000008
0280c164 00000100 00000000 77767cc4 00000000
0280c174 00000000 00000000 00000000 00000001
0280c184 0280c158 00000001 00000000 7776841c
0280c194 00000000 00000000 00000000 00000000
The second field 00000014 is the actual value of test and here is the actual output
0:000> ? poi(0280c124+0×4)
Evaluate expression: 20 = 00000014
But this does not solve the real issue of figuring out the methodtable to use it in !dumpvc. I could have used !mx System.Nullable* to get the MethodTable, because I knew the type is Nullable<Int> ,what if I didn’t know the type information.
To get the mt information I had to disassemble the code. First step is to get the !clrstack
0:000> !CLRStack
OS Thread Id: 0x94c (0)
ESP EIP
0021f1dc 769d73ea [NDirectMethodFrameStandaloneCleanup: 0021f1dc] System.IO.__ConsoleStream.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0021f1f8 77c8ae67 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)
0021f224 77c8ad86 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
0021f244 776f9fbb System.IO.StreamReader.ReadBuffer()
0021f258 77c677fc System.IO.StreamReader.Read()
0021f264 77c8dd81 System.IO.TextReader+SyncTextReader.Read()
0021f270 77bd328b System.Console.Read()
0021f278 00320115 ConsoleApplication.Program.Main(System.String[])
0021f4d0 59781b6c [GCFrame: 0021f4d0]
The next is to !u 00320115 and here is the partial ouput
00320116 8b45d8 mov eax,dword ptr [ebp-28h]
00320119 3a4008 cmp al,byte ptr [eax+8]
0032011c 8d4008 lea eax,[eax+8]
0032011f 8945c8 mov dword ptr [ebp-38h],eax
00320122 ff75dc push dword ptr [ebp-24h]
00320125 8b4dc8 mov ecx,dword ptr [ebp-38h]
00320128 bae8397777 mov edx,offset mscorlib_ni+0x2739e8 (777739e8) (MT: System.Nullable`1[[System.Int32, mscorlib]])
0032012d e8d6a74c59 call mscorwks!JIT_Unbox_Nullable (597ea908)
Notice the Method table 777739e8 for System.Nullable`1[[System.Int32, mscorlib]] and here is the output from !dumpmt -md 777739e8
0:000> !dumpmt -md 777739e8
EEClass: 7752e7c8
Module: 77501000
Name: System.Nullable`1[[System.Int32, mscorlib]]
mdToken: 0200026d (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0×10
ComponentSize: 0×0
Number of IFaces in IFaceMap: 0
Slots in VTable: 14
————————————–
MethodDesc Table
Entry MethodDesc JIT Name
77697028 775eace0 NONE System.Nullable`1[[System.Int32, mscorlib]].ToString()
77697020 775eacc0 NONE System.Nullable`1[[System.Int32, mscorlib]].Equals(System.Object)
77697018 775eacd0 NONE System.Nullable`1[[System.Int32, mscorlib]].GetHashCode()
777374c0 775412a4 PreJIT System.Object.Finalize()
77d010a0 775eac98 PreJIT System.Nullable`1[[System.Int32, mscorlib]]..ctor(Int32)
77d01100 775eaca0 PreJIT System.Nullable`1[[System.Int32, mscorlib]].get_HasValue()
77d01120 775eaca8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].get_Value()
77d01030 775eacb0 PreJIT System.Nullable`1[[System.Int32, mscorlib]].GetValueOrDefault()
77d01140 775eacb8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].GetValueOrDefault(Int32)
77d0100c 775eacf0 PreJIT System.Nullable`1[[System.Int32, mscorlib]].op_Implicit(Int32)
77d00fe8 775eacf8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].op_Explicit(System.Nullable`1<Int32>)
77d01040 775eacc8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].Equals(System.Object)
77d01078 775eacd8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].GetHashCode()
77d010c0 775eace8 PreJIT System.Nullable`1[[System.Int32, mscorlib]].ToString()
Now that I have confirmed the mt and here is the output from !dumpvc 777739e8 0280c124
0:000> !dumpvc 777739e8 0280c124
Name: System.Nullable`1[[System.Int32, mscorlib]]
MethodTable 777739e8
EEClass: 7752e7c8
Size: 16(0×10) bytes
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
7776eadc 40009a8 0 System.Boolean 1 instance 1 hasValue
7776ab0c 40009a9 4 System.Int32 1 instance 20 value
I decided to test this in .NET 4.0 and here is the output of !mdt for the Program object
0:000> !mdt 0x0236bc44
0236bc44 (ConsoleApplication.Program)
test:(System.Nullable`1[[System.Int32, mscorlib]]) VALTYPE (MT=6229f60c, ADDR=0236bc50)
i:0xa (System.Int32)
Notice the “test” which is Nullable<Int32> is now recognized by sosex and it also provides the method table.
Debugging Generic System.Nullable within Windbg « Naveen’s Blog…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
DotNetShoutout
July 8, 2010 at 7:39 am
[...] Debugging Generic System.Nullable within Windbg (blog de Naveen) Ca ne me sert pas heureusement pas tous les jours, du coup je ne m'en souviens pas forcément quand le cas se présente. Le post parle de Nullable<T>, mais ça reste valable avec d'autres cas pour lesquels on se retrouve avec un MT valant 00000000. [...]
Quelques lectures intéressantes , CoqBlog
July 11, 2010 at 6:46 pm