Using SOS in Visual Studio

One little known feature of Visual Studio 2005 is its ability to load and use the SOS debugging extension. SOS (Son of Strike) is an extension for the debuggers available in  Debugging Tools for Windows  that makes it possible to symbolically debug managed .Net applications using debuggers such as Windbg. Although I am capable of using Windbg and the like for debugging native code, I definitely prefer VS when it comes to common debugging tasks such as breakpoints and viewing data. Loading SOS in VS gives you the best of both worlds: the relaxed VS debugging environment and the power of SOS.

Sos has a larger learning curve than VisualStudio, and I have found that this scares a lot of developers away from it. However, it really is worth time to learn to use it. Why? For two reasons: It has some very powerful features not available directly in Visual Studio and it enables you to debug managed code in crash dumps. (Yes, it is true that the Visual Studio debugger does not currently support debugging managed code from a crash dump).

How do you go about using SOS in VS? Well first, you have to have the native debugging engine enabled. This is because SOS examines the CLR data structures from the debugger process so it must have access to the native address space. To do this from a console application project or winforms project, right mouse click on the project in solution explorer, choose properties, switch to the Debug tab, and make sure "Enable Unmanaged Code Debugging" is selected. With that done, close the property page add a breakpoint somewhere in your project, and hit F5. Once you've hit your breakpoint, the real fun begins.

1) Open the Immediate window (Debug->Windows->Immediate)

2) Type ".load C:\windows\Microsoft.NET\Framework\v2.0.50727\sos.dll" and hit enter (you may need to change the path to point to a different location on your machine). The immediate window should respond by saying "extension C:\windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded"

3) Type "!help" sos should dump its help contents to the immediate window. It is now ready to use.

So, what are some of those super powerful commands that SOS gives you? Well, for one thing, it gives you a "closer to the metal" view of your managed application. For a lot of these operations you'll need to understand a little bit about the data structure the CLR uses to represent your app while its running. I'll save that for the next post. One example of such a command is that SOS can dump the MSIL for a particular method. MSIL is the intermediate "assembly" language that the managed compilers all create from your source code. This is a feature I've heard several people ask for (if only to satisfy their own curiosity). The JIT compiler in the CLR converts the MSIL into native assembly language while your application runs. Visual Studio's dissassembly window dumps the native (post-jit instructions) and doesn't currently provide a way to see the MSIL.

How do you dump the MSIL for the current managed location?

1) with SOS loaded and your application in Break mode (sitting at a breakpoint) type !CLRStack. Sos will respond by printing the managed callstack for the current thread. (this is basically the equivalent of what the callstack would display). It might look something like:

!clrstack

OS Thread Id: 0xffc (4092)

ESP EIP

0013f43c 00e3009d ConsoleApplication4.Program.Main(System.String[])

0013f69c 79e88f63 [GCFrame: 0013f69c]

2) The numbers under the EIP column are the addresses of the instruction pointer at that frame. Highlight the first one (00e3009d) and copy it to the clipboard.

3) Next, execute "!ip2md 00e3009d" where 00e3009d is the address you copied to the clipboard. SOS will respond with:

!ip2md 00e3009d

MethodDesc: 00903120

Method Name: ConsoleApplication4.Program.Main(System.String[])

Class: 009012e8

MethodTable: 00903130

mdToken: 06000001

Module: 00902d5c

IsJitted: yes

m_CodeOrIL: 00e30070

This information contains the addresses of several CLR data structures and some information about the frame (such as whether or not it has been jit-compiled). We are interested in the MethodDesc address for this operation so copy its value (00903120) to the clipboard.

4) Finally, execute "!DumpIL 00903120" where 00903120 is the address of the methoddesc field. Sos will now dump:

!dumpil 00903120

ilAddr = 00402050

IL_0000: nop

IL_0001: ldc.i4.0

IL_0002: stloc.0

IL_0003: ldloc.0

IL_0004: ldc.i4.1

IL_0005: add

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008: dup

IL_0009: ldc.i4.1

IL_000a: add

IL_000b: stloc.0

IL_000c: stloc.1

IL_000d: ldloca.s VAR OR ARG 1

IL_000f: call System.Int32::ToString

IL_0014: call System.Console::WriteLine

IL_0019: nop

IL_001a: ret

And there you have it. The MSIL for the method.

In my next posting, I plan to go over some of these data structures more and explain some more of SOS's commands. For those who are curious, you can read the documentation for any sos command using the !help command. For instance, you can read about !DumpIL by executing !help DumpIL in the immediate window when SOS is loaded.

Jackson

This posting is provided "AS IS" with no warranties, and confers no rights.