Getting down to the metal: using the CLR Runtime's SOS extention in Visual Studio

In my last blog I talked a bit about how you go about using Visual Studio to look at "Release" code so that you can find out what code is actually generated by a particular high level programming construct.     As a quick recap the tricky part of insuring this is to

  1. Insure that your code is compiled 'Release' (Not 'Debug')

  2. Insure that Tools->Options->Debugging->General ->'Suppress JIT Optimization' is UNCHECKED

  3. Insure that Tools->Options->Debugging->General ->'Enable Just My Code' is UNCHECKED

In upcoming blog entries I am going to talk about how to use this general technique to answer questions like 'how to static, non-virtual, virtual, interface, and delegate calls compare'?    Before I start that, there is another very useful technique that I wanted to show you. 

The SOS Extension DLL

Microsoft current support two very different debugging tools:

  1. Visual Studio:  This is the product that Microsoft sells to professional developers and is in general the best tool for debugging (as well as code development in general).  It is designed to be easy ot use, and optimized for development time experience.  Incidentally from now until 11/2006 you can download an express version of Visual Studio for free.    (Update!:  Sadly I have had feedback that suggests that the instructions below do NOT work with the express version of Visual Studio.  I will be looking into exactly what can and can't be done and report back.  For now, assume that you can only follow the instructions below with a full version of VS).   
  2. The 'Debugger Tools for Windows' toolset.  This include a command line debugger called 'cdb' or 'ntsd' (The only difference is that ntsd spawns a new window when it launches and cdb does not), as well a GUI debugger called windbg (but is not nearly as friendly as Visual Studio).   These tools all use the same underlying technology and generally speaking are interchangeable.  For here on in I will simply refer to these tools as 'windbg' (even though they are actually windbg, cdb, and ndsd).   These tools were designed to deal with support issues in windows.  Thus they were designed to be easy to deploy (xcopy), fast to startup, full featured, but not necessarily friendly. 

Visual Studio has a lot of built in support for managed (.NET Runtime), code.  However the 'windbg' debuggers did not.   However there are times (eg in the field), where you might not have a Visual Studio handy and need to fall back on windbg.    To make managed debugging possible the .NET Rutime team developed an 'extension' DLL that can be loaded by the 'windbg' debugger.   This extension DLL understands how to decode various .NET Runtime data structures and is INDISPENSIBLE for debugging managed code if you are not using Visual Studio.   For example see the MSDN article on production debugging for an example of using windbg and SOS to debug a 'memory leak' in a production ASP.NET scenario. 

Using the SOS DLL in Visual Studio.

It is not particularly well known that you can also use the .NET Runtime SOS DLL in Visual Studio.   This allows you to combine the ease of use of Visual Studio with some of the powerful features of the SOS.DLL.      Note that this feature does NOT exist in VS 2003.  You need to upgrade to VS 2005 for the following to work.

Debugger Modes

To use the SOS.DLL you need to know a bit more about 'debugging modes' in Visual Studio.    Most of the time when you are debugging managed code, you don't care about operating system calls, or other calls out to unmanaged code.  By default when a managed application is run under Visual Studio, it runs in 'managed mode' where only managed code is shown.  Calls out to the runtime or to unmanaged code simply can't be viewed.  This is very annoying for people who want to see all the underlying instructions (like me).    You can however tell Visual Studio to debug ONLY unmanaged code, or to debug both.  The SOS dll can only be used when Visual Studio is debugging in unmanaged mode or in 'both'. 

Unfortunately, the exact mechanism for selecting the debugging mode varies.  For C# projects, you select you select the project's properties (right click on the project icon in the 'Solution Explorer' window on the right of the screen), and select the 'Debug' tab.  Under it you will find a box labeled 'Enable unmanaged code debugging'.  Checking this box puts the debugger in 'both' mode and allows the SOS DLL to be used.

If you attach to an existing process, the 'Attach to Process' dialog has a 'Attach To' box that has a 'Select' button.  Clicking on this leads to a dialog where you can select both 'managed' and 'native' code which will also put the debugger into 'both' mode.   

Finally you can start the debugger on a exe (eg hello.exe arg1 arg2), using the command below

  • start devenv /debugexe hello.exe arg1 arg2 

When Visual Studio is brought up in this way, you can access the debugger properties by right clicking on the exe name in the Solution Explorer window.    One of the properties is 'debugger Type'  and can be set to 'Mixed' to enable 'both' mode.

Loading SOS

Once you have selected the debugger type, you are ready to load SOS.  To do so you need to open the debuggers 'Immediate Window' (Debug->Windows->Immediate).   The Immediate window is basically a place where you an type commands and see the results (a non-GUI debugger).    To load SOS simply step into your application (F10)

  • .load sos

It should reply with a message like

· extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

At this point you can type SOS commands.  All such commands begin with a ! mark.  The most important of these is the following command

  • !Help

which tells you about the rest of the commands.   For example, one of the more interesting commands is the !DumpHeap -stat which shows you a histogram of GC heap (grouped by object type).

  • !DumpHeap -stat

More complete details of using this command to find 'leaks' (memory that should be trash, but erroneously it being referenced), is detailed in production debugging  article.

One of my favorite commands is the !u command.  This command disassembles code, but also annotates it with useful symbolic information like the names of .NET Runtime helper routines.   We will see this in action in a later blog.

Recap

I think I have done enough for one blog entry.  What we have covered in this entry is

  1. The .NET runtime has a cool debugger extension DLL called SOS that knows how to parse .NET runtime structures.

  2. This extension DLL was originally designed for windbg, however starting with VS 2005, can be used in Visual Studio.

  3. To use SOS, first put VS into a debugging mode that allows debugging both managed and unmanaged code.  How this is done depends on how you are using VS (see above).

  4. Once VS is in the right mode, and the program is started, '.load sos' loads the extensions, and !Help shows you help. 

  5. !DumpHeap -stat is a interesting command to play with.

  6. More on what you can do with SOS in a later blog.