New commands in SOS for .NET 4.0 Part 1

4 minute read

My friend and fellow debugger Brian at http://Kodehoved.dk recently wrote a couple of posts on news with sos for .NET framework 4.0 (in Danish)

Since Danish, although a beautiful language is probably foreign to most of you I figured I’d write a summary of the new commands in English and add some comments of my own.

Loading sos for .NET 4.0

As in 2.0 you will find sos.dll in the framework directory so you can load it in windbg or cdb using the full path

.load C:\Windows\Microsoft.NET\Framework\v4.0.30128\sos.dll

You can also use the short hand .loadby method but since the name of the core dll has changed in 4.0, you will no longer load it using .loadby sos mscorwks, instead you can now load it using

.loadby sos clr

SOS now takes advantage of windbg’s DML feature making it easier to debug

DML stands for Debugger Markup Language and allows commands to emit hyper links in the command output. For example, in the !threads command output there are DML links for the OSID column, the State column and the Domain column.

Clicking on the first link in the OSID column (below) for example would execute the command ~~[ff4]s which would set the context to that thread. Clicking on a link in the state column would show you the !ThreadState output so you can see exactly what the state 8220 means etc. This is very handy as you don’t have to remember all the various commands.

0:042> !threads
ThreadCount:      25
UnstartedThread:  0
BackgroundThread: 17
PendingThread:    0
DeadThread:       8
Hosted Runtime:   no

                                   PreEmptive   GC Alloc                Lock
       ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
  11    1   ff4 012923d0      8220 Enabled  00000000:00000000 01289450     0 Ukn
  18    2  1dbc 0129a3a0      b220 Enabled  00000000:00000000 01289450     0 MTA (Finalizer)
  20    4  114c 012b1650   8008220 Enabled  00000000:00000000 01289450     0 MTA (Threadpool Completion Port)
...

In order to turn on DML in your debugger you can run the command .prefer_dml 1 or if you only want to enable it for a specific command you can run !threads /D for example.

DML is used in many places in sos. In !dumpheap –stat you get links for each type that executes !dumpheap –mt <methodtable> so that you can see all the objects of that type, and the !dumpobj output emits links so that you can easily click and look at the member variables in more detail.

New commands

To find a list of all the commands sos has to offer you can run !help in windbg (if sos is the last extension that was loaded) and to get help (including examples) for a specific command you can run !help <command>, for example !help DumpObj.

Understanding why an object is not garbaqe collected (!GCWhere, !FindRoots !HandleCLRN)

Previously we only had the !GCRoot command which was nice for strong references but didn’t work well when your object was not “really” rooted.

Running !GCRoot <object address> will tell us the root chain of any rooted object, in other words, if your object is not being collected because it is linked, directly or indirectly from a thread, a static object, a ref counted object etc. it will give you this link chain so that you can determine which links you need to break in order to make your object collectable.

Sometimes however you may be troubleshooting an issue where your object is not necessarily rooted but it still doesn’t get collected. The answer to why it is not collected is either that the generation your object is in has yet to be collected, or it is rooted by an object in a higher generation, and that it is waiting for this object to be collected.

If you are live debugging your application with Windbg you can now find out why your object is not collected by going through this sequence.

  1. Find out which generation your object is in by running !GCWhere

     0:030> !GCWhere 057cca48
     Address  Gen Heap segment  begin    allocated size
     057cca48   2      1     056f0000   056f0038   065c8a04    0x40(64)
    

    !GCWhere tells you what generation your object currently belongs to, as well as some info about the segment in which it is allocated. In this case our object is in Gen 2 on Heap 1.

  2. Turn on CLR GC Notifications

    Next we run !HandleCLRN to tell the debugger to stop on GC CLR Notifications

  3. Enable break on GC

    !FindRoots –gen 2 tells the debugger to stop on the next Gen 2 collection, so we issue this command and hit g (for go) to allow the debugger to continue executing

     0:030> !FindRoots -gen 2
     0:030> g
    
  4. Find the roots for your object

    When the garbage collection occurs the following is displayed in windbg and the debugger stops

     (21d0.1a74): CLR notification exception - code e0444143 (first chance)
     CLR notification: GC - Performing a gen 2 collection. Determined surviving objects...
    

    Now we can run !FindRoots <object address> to determine what is holding on to the object. This might be a root graph similar to what you see with !gcroot if the object is strongly rooted.

    It might also be something like

     0:016> !FindRoots 05e54f4c
     Object 05e54f4c will survive this collection:
         gen(0x5e54f4c) = 2 > 1 = condemned generation.
    

    …if you run !findroots for an object in a higher generation than the one you stopped on, or something like

     0:002> !findroots 06808094
     older generations::Root:  068012f8(AAA.Test+a)->
     06808094(AAA.Test+b)
    

    …if your object is being held on to by an object in an older generation

There are plenty of other new commands as well but to avoid making this post all too long, I will continue with the other commands in my next post instead.

Until then, Tess