If broken it is, fix it you should

Using the powers of the debugger to solve the problems of the world - and a bag of chips    by Tess Ferrandez, ASP.NET Escalation Engineer (Microsoft)

New commands in SOS for .NET 4.0 Part 1

New commands in SOS for .NET 4.0 Part 1

Rate This
  • Comments 6

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)

New SOS Commands in .NET 4
More debugging news in CLR 4

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 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.

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 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.

Until then,

Tess

  • Great info Tess! Thanks for thinking about translation.

  • Hi Tess,

    I am trying to debug a 64bit w3wp process (Sharepoint 2010) in a 64bit machine . After loading sos through .loadby sos clr, when I try to run any other commans I see the following message

    !threads

    The version of SOS does not match the version of CLR you are debugging.  Please

    load the matching version of SOS for the version of CLR you are debugging.

    CLR Version: 4.0.30319.1

    SOS Version: 2.0.50727.4952

    Failed to request ThreadStore

    I have spent the whole day today trying to figure what could be happening  but in vain.

    Any hints would be greatly appreciated!

  • Mallika: As you can see by the version numbers, you've inadvertently loaded the .NET 2.0 version of SOS.

    Try again but use the full path to the .NET 4 - like this:

    .load c:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll

    that should do it

  • Is this an issue or just a distruction:

    The version of SOS does not match the version of CLR you are debugging.  Please

    load the matching version of SOS for the version of CLR you are debugging.

    CLR Version: 4.0.30319.1

    SOS Version: 4.0.30319.225

  • Same issue here :(

    The version of SOS does not match the version of CLR you are debugging.  Please

    load the matching version of SOS for the version of CLR you are debugging.

    CLR Version: 4.0.30319.17929

    SOS Version: 4.0.30319.296

  • Just little late on the response:

    But you may want to get two things along with memory dump, mscordacwks.dll & sos.dll from the machine where dump was taken.

    Copy them into a separate folder e.g. c:\debug\ext

    and use .load to load them.

    If you still get the error, use .chain command to find how the sos.dll and mscordacwks.dll is loaded, in certain cases, you may end up seeing two sos.dll with different version loaded in debug session, so, just unload the one which is not correct by using .unload command.

Page 1 of 1 (6 items)
Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post