Script to load sos within Windbg based on .NET Framework version


I often debug  .NET Framework v 2.0 / v 4.0 code within windbg. In v 2.0 the main clr dll was called “mscorwks.dll” and in v 4.0 it is called “clr.dll”.  As many of you are aware , to load sos in v 2.0 we would have to enter “.loadby sos mscorwks” and in v 4.0 it would be “.loadby sos clr” . This was a pain for me. Came up with a script to automate loading sos based on clr version

!for_each_module .if(($sicmp( "@#ModuleName" , "mscorwks") = 0) ) {.loadby sos mscorwks} .elsif ($sicmp( "@#ModuleName" , "clr") = 0) {.loadby sos clr}

You can take it up a notch by setting a break-point within clr based on the .NET Framework version

!for_each_module .if(($sicmp( "@#ModuleName" , "mscorwks") = 0) ) {bp mscorwks!WKS::GCHeap::SuspendEE ".if (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) {.echo start of gen 2}"} .elsif ($sicmp( "@#ModuleName" , "clr") = 0) {bp clr!WKS::GCHeap::SuspendEE ".if (dwo(clr!WKS::GCHeap::GcCondemnedGeneration)==2) {.echo start of gen 2}"}

Debugging .NET – mystery between DEBUG versus RELEASE within windbg


I am sure most of us have debugged applications that are build with debug turned on, which is obviously much easier compared to debugging release build (optimized code). In this post I am going to share one of my experiences of debugging release build code. I will demonstrate this with a simple Console Application.

Here is the code


using System;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var x = 10;
var name = "naveen";
Console.WriteLine(name);
Console.Read();
}
}
}

I compiled it under release mode and launched it within the debugger. The goal is to have a break-point on Console.WriteLine(name). First thing was to set up a sx notification on load for mscorlib.


sxe ld:mscorlib

And when the break-point hits for the above event, then issued the following command to load sosex , sos and set an bp on Console.WriteLine which is nothing fancy


.load sosex;.loadby sos clr;!mbm *System.Console.WriteLine* "!mk";g

I would imagine that the break-point would hit and I would get a call-stack, but to my surprise this was the output

0:000> .load sosex;.loadby sos clr;!mbm *System.Console.WriteLine* “!mk”;g
The breakpoint could not be resolved immediately.
Further attempts will be made as modules are loaded.
(1090.11fc): CLR notification exception – code e0444143 (first chance)
Breakpoint set at System.Console.WriteLine().
Breakpoint set at System.Console.WriteLine(Boolean).
Breakpoint set at System.Console.WriteLine(Char).
Breakpoint set at System.Console.WriteLine(Char[]).
Breakpoint set at System.Console.WriteLine(Char[], Int32, Int32).
Breakpoint set at System.Console.WriteLine(System.Decimal).
Breakpoint set at System.Console.WriteLine(Double).
Breakpoint set at System.Console.WriteLine(Single).
Breakpoint set at System.Console.WriteLine(Int32).
Breakpoint set at System.Console.WriteLine(UInt32).
Breakpoint set at System.Console.WriteLine(Int64).
Breakpoint set at System.Console.WriteLine(UInt64).
Breakpoint set at System.Console.WriteLine(System.Object).
Breakpoint set at System.Console.WriteLine(System.String).
Breakpoint set at System.Console.WriteLine(System.String, System.Object).
Breakpoint set at System.Console.WriteLine(System.String, System.Object, System.Object).
Breakpoint set at System.Console.WriteLine(System.String, System.Object, System.Object, System.Object).
Breakpoint set at System.Console.WriteLine(System.String, System.Object, System.Object, System.Object, System.Object, …).
Breakpoint set at System.Console.WriteLine(System.String, System.Object[]).
(1090.11fc): CLR notification exception – code e0444143 (first chance)

That’s it. And I never got an hit for the break-point. I checked to make sure there was an actual breakpoint set by issuing a “bl” command. I could see there were break-points for Console.WriteLine. The next step was to  disassemble the code. So got the instruction pointer from the !mk call-stack. Here is the output of !mk. FYI this is when the code is blocked on Console.Read

00:U 003def90 75d273ea KERNEL32!ReadConsoleInternal+0x15
01:U 003def98 75d27041 KERNEL32!ReadConsoleA+0x40
02:U 003df020 75caf489 KERNEL32!ReadFileImplementation+0x75
03:M 003df068 65651c8b DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)(+0x0 IL)(+0x0 Native)
04:M 003df0e8 65cbf7e8 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)(+0x53 IL)(+0x8c Native) [f:\dd\ndp\clr\src\BCL\System\IO\__ConsoleStream.cs, @ 16707566,0]
05:M 003df110 65cbf6d0 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)(+0x5d IL)(+0x9c Native) [f:\dd\ndp\clr\src\BCL\System\IO\__ConsoleStream.cs, @ 131,13]
06:M 003df138 65608bfb System.IO.StreamReader.ReadBuffer()(+0xa0 IL)(+0x3b Native) [f:\dd\ndp\clr\src\BCL\System\IO\StreamReader.cs, @ 488,21]
07:M 003df154 65bcacc3 System.IO.StreamReader.Read()(+0x1b IL)(+0x23 Native) [f:\dd\ndp\clr\src\BCL\System\IO\StreamReader.cs, @ 302,17]
08:M 003df160 65cc5e9d System.IO.TextReader+SyncTextReader.Read()(+0x0 IL)(+0x19 Native) [f:\dd\ndp\clr\src\BCL\System\IO\TextReader.cs, @ 244,17]
09:M 003df170 0066009a *** WARNING: Unable to verify checksum for ConsoleApplication.exe
ConsoleApplication.Program.Main(System.String[])(+0x0 IL)(+0x2a Native) [c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs, @ 10,13]
0a:U 003df17c 661621db clr!CallDescrWorker+0x33

Next disassemble Main Method using the ip which is 0066009a


!u 0066009a

Here is the output

0:000> !u 0066009a
Normal JIT generated code
ConsoleApplication.Program.Main(System.String[])
Begin 00660070, size 2d
c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs @ 10:
00660070 55 push ebp
00660071 8bec mov ebp,esp
00660073 56 push esi
00660074 8b3530206b03 mov esi,dword ptr ds:[36B2030h] (“naveen”)
c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs @ 11:
0066007a e85170f864 call mscorlib_ni+0x2570d0 (655e70d0) (System.Console.get_Out(), mdToken: 060008cd)
0066007f 8bc8 mov ecx,eax
00660081 8bd6 mov edx,esi
00660083 8b01 mov eax,dword ptr [ecx]
00660085 8b403c mov eax,dword ptr [eax+3Ch]
00660088 ff5010 call dword ptr [eax+10h]
0066008b e8f0a55565 call mscorlib_ni+0x82a680 (65bba680) (System.Console.get_In(), mdToken: 060008cc)
00660090 8bc8 mov ecx,eax
00660092 8b01 mov eax,dword ptr [ecx]
00660094 8b402c mov eax,dword ptr [eax+2Ch]
00660097 ff500c call dword ptr [eax+0Ch]
c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs @ 13:
>>> 0066009a 5e pop esi
0066009b 5d pop ebp
0066009c c3 ret

And I see System.Console.get_Out instead of System.Console.WriteLine which I was totally surprised. This was  the reason the break-point never hit. Next I wanted check the IL which was compiled , what we see above is jitted x86 mixed with IL. Here is the command to check the compiled IL. First I had to get the methodesc from the ip using !ip2md


!ip2md 0066009a

0:000> !ip2md 0066009a
MethodDesc: 002237f0
Method Name: ConsoleApplication.Program.Main(System.String[])
Class: 002213f8
MethodTable: 00223804
mdToken: 06000001
Module: 00222e9c
IsJitted: yes
CodeAddr: 00660070
Transparency: Critical
Source file: c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs @ 13

Here is from the methoddesc to IL


!dumpil 002237f0

0:000> !dumpil 002237f0
ilAddr = 012a2050
IL_0000: ldstr “naveen”
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call System.Console::WriteLine
IL_000c: call System.Console::Read
IL_0011: pop
IL_0012: ret

Which looks very similar to my C# code. So looks like CLR optimized the code ,converted the Console.WriteLine to Console.get_Out. To validate it restarted the app with this as the command for break-point

.load sosex;.loadby sos clr;!mbm *System.Console.get_Out* "!mk";g

And here is the output

00:M 0032ed58 655e70d1 System.Console.get_Out()(+0x0 IL)(+0x1 Native) [f:\dd\ndp\clr\src\BCL\System\Console.cs, @ 193,17]
01:M 0032ed60 003a007f *** WARNING: Unable to verify checksum for ConsoleApplication.exe
ConsoleApplication.Program.Main(System.String[])(+0x6 IL)(+0xf Native) [c:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication11\Program.cs, @ 11,13]
02:U 0032ed6c 661621db clr!CallDescrWorker+0x33

Now that I have solved this I wanted to check the same on the debug build (optimized -) . To validate if it was Console.get_Out or Console.WriteLine. So when mscorlib loaded here was my command to check this

.load sosex;.loadby sos clr;!mbm *Program.Main* "!u @eip";g

In the above command I am setting a break-point on Main method and when the break-point hits “!u @eip” will disassemble the ip, the @eip register will have the address of the current function. Here is the output from !u @eip

*** WARNING: Unable to verify checksum for ConsoleApplication11.exe

c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 11:
01f10070 55 push ebp
01f10071 8bec mov ebp,esp
01f10073 83ec0c sub esp,0Ch
01f10076 894dfc mov dword ptr [ebp-4],ecx
01f10079 833d3c31360000 cmp dword ptr ds:[36313Ch],0
01f10080 7405 je 01f10087
01f10082 e8c85a5064 call clr!JIT_DbgIsJustMyCode (66415b4f)
01f10087 33d2 xor edx,edx
01f10089 8955f4 mov dword ptr [ebp-0Ch],edx
01f1008c 33d2 xor edx,edx
01f1008e 8955f8 mov dword ptr [ebp-8],edx
>>> 01f10091 90 nop
c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 12:
01f10092 c745f80a000000 mov dword ptr [ebp-8],0Ah
c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 13:
01f10099 8b0530200f03 mov eax,dword ptr ds:[30F2030h] (“naveen”)
01f1009f 8945f4 mov dword ptr [ebp-0Ch],eax
c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 14:
01f100a2 8b4df4 mov ecx,dword ptr [ebp-0Ch]
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\246f1a5abb686b9dcdf22d3505b08cea\mscorlib.ni.dll
01f100a5 e802706d63 call mscorlib_ni+0x2570ac (655e70ac) (System.Console.WriteLine(System.String) , mdToken: 06000919)
01f100aa 90 nop
c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 15:
01f100ab e8b4c1ca63 call mscorlib_ni+0x82c264 (65bbc264) (System.Console.Read(), mdToken: 0600090a)
01f100b0 90 nop
c:\users\naveen\documents\visual studio 2010\Projects\ConsoleApplication11\Program.cs @ 17:
01f100b1 90 nop
01f100b2 8be5 mov esp,ebp
01f100b4 5d pop ebp
01f100b5 c3 ret
eax=003637f0 ebx=00000000 ecx=020fbc7c edx=00000000 esi=004bb2d0 edi=0016f400
eip=01f10091 esp=0016f3c8 ebp=0016f3d4 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
01f10091 90 nop

Notice in the above code it is Console.WriteLine and not Console.get_Out.

Here is one of gotchas of debugging optimized code.

Using F# to Automate Reading–The Morning Brew


I guess most of the .NET Devs read The Morning Brew, if not you should.  It is a morning newspaper for the dev, so I end up reading it first thing when I go to work. I like to  try and automate most of the stuff . So I thought why not write a script that reads the Morning Brew feed, filter the excluded content that I am not interested in and open the urls before I come in. The reason behind using F# is I don’t have to compile the code. I could use it with FSI.exe. Incase if the extraction logic changes and I don’t have to recompile , it is just fixing the script. The best part of writing in F# is I avoid all the ceremony. Here is the F# code, it is nothing fancy. And this code can easily be modified for other link collection sites.


#r @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.ServiceModel.dll"
#r @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Xml.Linq.dll"

open System.IO
open System.ServiceModel.Syndication
open System.Web
open System.Xml
open System.Xml.Linq
open System.Diagnostics

let formatter = new Rss20FeedFormatter()
let sw = new StringWriter();
// Excluded Keywords
let exists (item:string) = File.ReadAllLines @"c:\Users\naveen\exclude.txt" |>
                             Seq.exists(fun e -> item.ToLower().Contains(e.ToLower()))

formatter.ReadFrom(XmlReader.Create "http://feeds.feedburner.com/ReflectivePerspective")
formatter.Feed.Items |> Seq.nth 0 |> fun i -> i.SaveAsRss20(new XmlTextWriter(sw))

let data = HttpUtility.HtmlDecode(sw.ToString())
XDocument.Parse(data).Descendants(XName.Get("li")) |>
           Seq.filter(fun i -> not (exists i.Value)) |>
           Seq.map(fun i -> i.Descendants(XName.Get("a"))) |>
           Seq.iter(fun x -> x |> Seq.iter(fun y ->
            try
               System.Diagnostics.Process.Start (y.Attribute(XName.Get("href")).Value) |> ignore
           with
           |ex -> printfn "Error : %s" ex.Message))

My excluded file at the moment contains javascript and jquery. I am not much into these. Here is the command line

"C:\Program Files (x86)\Microsoft F#\v4.0\Fsi.exe" --quiet --exec "C:\Users\naveen\morningbrew.fsx"

Combining Stack Overflow RSS, OData and API to query


In my opinion Stack Overflow has a ton of knowledge to learn new tricks. And there are some really smart people in the SO community. I try and learn new things when I find time.

I subscribe to RSS feeds for new questions on a particular topic. Example, here is one for F# from Stack Overflow http://stackoverflow.com/feeds/tag/f%23. The advantage of the RSS feed is I get to see new questions, but the drawback is I would have to navigate to the site to look for answers. AFAIK the stacky (stack overflow API) does not provide a mechanism for querying new questions based on a tag.

It was easy for me to combine both of them to solve my problem. With RSS feed I could discover new questions and with the stacky I could get answers . And I use Linqpad as a scratchpad so it was easy to write-up something quick.


void Main()
{
 var reader = XmlReader.Create("http://stackoverflow.com/feeds/tag/f%23");
 var feed = SyndicationFeed.Load<SyndicationFeed>(reader);
 var length = "http://stackoverflow.com/questions/".Length;

 var client = new StackyClient("1.0", File.ReadAllText(@"c:\temp\so.txt"),HostSite.StackOverflow,new UrlClient(), new JsonProtocol());

 var feedItems = from item in feed.Items
                 let nextOccurence = item.Id.ToString().IndexOf("/",length)
                 let getId = new Func<int>(() => Convert.ToInt32( item.Id.Substring(length,nextOccurence - length)))
                 select new {Id = getId(), Title = item.Title.Text, Body = item.Summary.Text.StripHTML()};

 var answers = client.GetQuestionAnswers(feedItems.Select (y => y.Id),new AnswerOptions() { IncludeBody = true});

 // The latest F# feed questions and answers
 var qa = from question in feedItems
          join answer in answers on question.Id equals answer.QuestionId
          where answer.Accepted == true
          select new { Title = question.Title, Question = question.Body.StripHTML(), Answer = answer.Body.StripHTML()};
 qa.Dump();
}
public static class Extensions
{
      public static string StripHTML(this string s)
      {
         return Regex.Replace(s, @"<(.|\n)*?>", string.Empty);
      }
}

And if you have been following F# and functional programming then you would probably know Tomas. I would also like to read what he has been answering. Again stacky does not provide an API to query user by name. This is where the SO OData comes in handy and LinqPad handles OData very well. Here is the code to get Tomas user id via OData and query for questions and answers which he has answered using stacky .

var tomas = Users.Where(u => u.DisplayName.StartsWith("Tomas Pet")).First().Id;
var tomasQA = from ans in  client.GetUsersAnswers(tomas,new AnswerOptions() { IncludeBody = true })
              select new { Title = ans.Title, Question = client.GetQuestion(ans.QuestionId,true,false).Body.StripHTML(),
              Answer = ans.Body.StripHTML()};
tomasQA.Dump();

Load the same Assembly from GAC and private bin path in .NET


This post is all about exploring the CLR loader to load an assembly from GAC and from private bin path. This is not possible because, if an assembly is loaded from GAC and if the code uses Assembly.LoadFrom to load the same assembly, then the loader would return the assembly that has already been loaded from the GAC.

Here is the ClassLibrary2 code


using System;

namespace ClassLibrary2
{
 public class Class1
 {
 public string Bar()
 {
 return "bar";
 }

 }
}

Here is the Winforms application to load the Class Library Code


using System;
using System.Reflection;
using System.Windows.Forms;
using ClassLibrary2;
namespace WindowsFormsApplication4
{
 public partial class Form1 : Form
 {
 public Form1()
 {
 InitializeComponent();
 var c = new Class1();
 Console.WriteLine(c.Bar());
 }

 private void button1_Click(object sender, EventArgs e)
 {
 var  ad = AppDomain.CreateDomain("Test");
 ad.AssemblyResolve += ((a1,e1) => Assembly.LoadFile(@"C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\ClassLibrary2\bin\Debug\ClassLibrary2.dll"));
 var a = ad.Load("ClassLibrary");
 var c1 = a.CreateInstance("ClassLibrary2.Class1") as Class1;
 c1.Bar();
 }
 }
}

If you notice the code creates an instance of the “Class1” in the Form1 constructor, this will essentially load the ClassLibrary2 in to memory. FYI the ClassLibrary2 is GACed when the application launches. Here is the fusglovwr output , which shows the assembly being loaded from GAC.

LOG: This bind starts in default load context.

LOG: Using application configuration file: C:\Users\naveen\Documents\Visual Studio 2010\Projects\WindowsFormsApplication4\bin\Debug\WindowsFormsApplication4.exe.Config

LOG: Using host configuration file:

LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.

LOG: Post-policy reference: ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3a0a06f4596f3e16

LOG: Found assembly by looking in the GAC.

LOG: Binding succeeds. Returns assembly from C:\Windows\Microsoft.Net\assembly\GAC_MSIL\ClassLibrary2\v4.0_1.0.0.0__3a0a06f4596f3e16\ClassLibrary2.dll.

LOG: Assembly is loaded in default load context.

After verifying fuslogvwr output , I uninstall the assembly from gac using gacutil /u (without closing the app). After which I click the button1 ,which will call the Assembly.LoadFrom to load the same assembly from private bin path. And here fuslogvwr output of loading the assembly using the load from, notice GAC lookup was unsuccessful.

LOG: This bind starts in default load context.

LOG: Using application configuration file: C:\Users\naveen\Documents\Visual Studio 2010\Projects\WindowsFormsApplication4\bin\Debug\WindowsFormsApplication4.exe.Config

LOG: Using host configuration file:

LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.

LOG: Post-policy reference: ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3a0a06f4596f3e16

LOG: GAC Lookup was unsuccessful.

LOG: Attempting download of new URL file:///C:/Users/naveen/Documents/Visual Studio 2010/Projects/WindowsFormsApplication4/bin/Debug/ClassLibrary2.DLL.

LOG: Assembly download was successful. Attempting setup of file: C:\Users\naveen\Documents\Visual Studio 2010\Projects\WindowsFormsApplication4\bin\Debug\ClassLibrary2.dll

LOG: Entering run-from-source setup phase.

LOG: Assembly Name is: ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3a0a06f4596f3e16

LOG: Binding succeeds. Returns assembly from C:\Users\naveen\Documents\Visual Studio 2010\Projects\WindowsFormsApplication4\bin\Debug\ClassLibrary2.dll.

LOG: Assembly is loaded in default load context.

And here is the !dumpdomain output from windbg for this process

Assembly:           003ce1e0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\ClassLibrary2\v4.0_1.0.0.0__3a0a06f4596f3e16\ClassLibrary2.dll]
ClassLoader:        003ee5b8
SecurityDescriptor: 003d2838
Module Name
00306b28            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\ClassLibrary2\v4.0_1.0.0.0__3a0a06f4596f3e16\ClassLibrary2.dll

Assembly:           003ceae0 [C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\ClassLibrary2\bin\Debug\ClassLibrary2.dll]
ClassLoader:        0044f418
SecurityDescriptor: 00445ea8
Module Name
011ee6b4            C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\ClassLibrary2\bin\Debug\ClassLibrary2.dll

Notice the first assembly is loaded from the GAC and the second one is loaded from the bin path.

This is not something that I would ever do. This was merely an exercise to bypass the loader.

FYI when the assembly is uninstalled from GAC, the gacutil.exe creates a temp copy and stores the assembly in the temp directory because it is being used by a process. And the temp copy of the assembly is deleted when the process ends. Here is the screen shot of Procmon creating an temp copy of the GACed assembly which I uninstalled.

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 (0x80070057).
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(0x14) 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+0x4)
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: 0x10
ComponentSize: 0x0
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(0x10) 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.

Recursive !dumpmt – Windbg


In this post I will be demonstrating how we could use CLR internal data-structures to recursively get the methodtable’s of an object and its base classes. The idea behind this is to understand the CLR data structure.

Here is the sample code


using System;
namespace ConsoleApplication
{
 class Program : B
 {
 string test = "cw";
 static void Main(string[] args)
 {
 var p = new Program();
 Console.Read();
 }
 }
 class B : A
 {
 public void TestB()
 {
 }
 }
 class A
 {
 public void TestA()
 {
 }
 }
}

The “Program” object address  is 0x0254bc38 and here  is the output of !dumpobj

0:000> !do 0x0254bc38
Name:        ConsoleApplication.Program
MethodTable: 001c3904
EEClass:     001c1508
Size:        12(0xc) bytes
File:        C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
6335f9ac  4000001        4        System.String  0 instance 0254bc44 test

The method table pointer for the Program class is 001c3904 . Let’s dump the raw memory instead of using !dumpobj

dd 0x0254bc38

0:000> dd 0x0254bc38
0254bc38  001c3904 0254bc44 80000000 6335f9ac
0254bc48  00000002 00770063 00000000 00000000
0254bc58  63367490 00000000 00000000 00000000
0254bc68  00000000 00000000 00000000 6335f5e8
0254bc78  00000000 40010000 63366034 00000003
0254bc88  00000008 00000100 00000000 63366f40
0254bc98  00000000 00000000 00000000 00000000
0254bca8  00000001 0254bc80 00000001 00000000

Now that we can see the MethodTable pointer is the first field we can get the Methods by using


!dumpmt -md poi(0x0254bc38)

0:000> !dumpmt -md poi(0x0254bc38)
EEClass:      001c1508
Module:       001c2e9c
Name:         ConsoleApplication.Program
mdToken:      02000004
File:         C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
————————————–
MethodDesc Table
Entry MethodDesc      JIT Name
6326a7e0   63044934   PreJIT System.Object.ToString()
6326e2e0   6304493c   PreJIT System.Object.Equals(System.Object)
6326e1f0   6304495c   PreJIT System.Object.GetHashCode()
632f1600   63044970   PreJIT System.Object.Finalize()
002200d0   001c38f0      JIT ConsoleApplication.Program..ctor()
00220070   001c38e4      JIT ConsoleApplication.Program.Main(System.String[])

So next time when we have an object we don’t have to go look for method table pointer address.

The goal is to get every method of class  Program, B, A and System.Object automatically. To get this, lets dump the raw memory of the method table

dd poi(0x0254bc38)

0:000> dd poi(0x0254bc38)
001c3904  00080000 0000000c 00050011 00000004
001c3914  001c3890 001c2e9c 001c3934 001c1508
001c3924  00000000 00000000 001c3854 002200d0
001c3934  00000080 00000000 00000000 00000000
001c3944  00000000 00000000 00000000 00000000
001c3954  00000000 00000000 00000000 00000000
001c3964  00000000 00000000 00000000 00000000
001c3974  00000000 00000000 00000000 00000000

The 10th offset contains the address of its base class method table pointer, So in the above output it is 001c3890 .

Now that we know it is the 10th offset, here is the script to get every method table for a class and its parents. FYI if the 10th offset is 00000000 then it means it is the super class which is System.Object.

r$t0 =poi(0x0254bc38);.while(@$t0) {!dumpmt -md @$t0;.echo *****************;r$t0=poi(@$t0+10)}

And here is the explanation for the above script

  1. r$t0 =poi(0x0254bc38) – Using a pseudo register $t0 to assign the value of mt of the 0x0254bc38
  2. The .while loop will terminate when the value is 0. The  “!dumpmt -md @$t0″ will dump the Method Table of the $t0 and the  “r$t0=poi(@$t0+10)” will reset $t0 its parent object method table.

Here is the partial output from the above script with method tables from Program and B

0:000> r$t0 =poi(0x0254bc38);.while(@$t0) {!dumpmt -md @$t0;.echo *****************;r$t0=poi(@$t0+10)}
EEClass:      001c1508
Module:       001c2e9c
Name:         ConsoleApplication.Program
mdToken:      02000004
File:         C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
————————————–
MethodDesc Table
Entry MethodDesc      JIT Name
6326a7e0   63044934   PreJIT System.Object.ToString()
6326e2e0   6304493c   PreJIT System.Object.Equals(System.Object)
6326e1f0   6304495c   PreJIT System.Object.GetHashCode()
632f1600   63044970   PreJIT System.Object.Finalize()
002200d0   001c38f0      JIT ConsoleApplication.Program..ctor()
00220070   001c38e4      JIT ConsoleApplication.Program.Main(System.String[])
*****************
EEClass:      001c149c
Module:       001c2e9c
Name:         ConsoleApplication.B
mdToken:      02000003
File:         C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
————————————–
MethodDesc Table
Entry MethodDesc      JIT Name
6326a7e0   63044934   PreJIT System.Object.ToString()
6326e2e0   6304493c   PreJIT System.Object.Equals(System.Object)
6326e1f0   6304495c   PreJIT System.Object.GetHashCode()
632f1600   63044970   PreJIT System.Object.Finalize()
00220120   001c3888      JIT ConsoleApplication.B..ctor()
001cc02d   001c387c     NONE ConsoleApplication.B.TestB()
*****************

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: