New commands in SOS for .NET 4.0 Part 1
My friend and fellow debugger Brian at https://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
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 executed !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.
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 do 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.