≡ Menu

First Look at Psscor2 the new WinDBG Debugging Extension for Managed Code

About two weeks ago Microsoft announced the release of Psscor2 – a managed debugging extension for WinDBG which is a superset of the awesome SOS debugging extension. This is an insanely useful tool when you are trying to debug problems on Production machines where you don’t (and can’t) install Visual Studio, or when you need a deeper understanding of what is going on with your managed code. While there are several articles listing out the commands and a detailed command help view, there isn’t much else.

I’m teaching my .NET Debugging class in two weeks for a client in NYC and wanted to include a section on Psscor2. Since I was going through the commands, I thought it would be fun to post them for the world to see as well.

(Note: Don’t leave this blog post without looking at the FindDebugTrue and FindDebugModules commands!)

Loading Psscor2

First, download it from the download site above. Run the executable which extracts a zip file. Now extract the files from the zip. You’ll see three folders – one for each architecture. To load psscor2 in WinDBG, start up WinDBG, attach to a process (or open a dump file) and type “.load C:\[psscor2installdir]\[arch]\psscor2.dll”. For example, my students will load it from “C:\labfiles\psscor2\x86\psscor2.dll” or “C:\labfiles\psscor2\amd64\psscor2.dll”.

Once you have it loaded, you can type “!help” to see all of the commands. If you’ve used SOS before, this should look very similar.

!dae – DumpAllExceptions

The first new command is !dae. This dumps out all exceptions on the heap along with the call stack. For this example, I loaded psscor2 and told WinDBG to break on any managed exceptions (“sxe clr”). When it broke in, I ran the “!dae” command and got the following output:

0:000> !dae
Going to dump the .NET Exceptions found in the heap.
Loading the heap objects into our cache.
Number of exceptions of this type:        1
Exception MethodTable: 000007ff001e1020
Exception object: 000000000265b0b8
Exception type: DebuggingWF.DebuggingWFException
Message: Exception occurred
InnerException: System.NullReferenceException, use !PrintException 000000000265b030 to see more
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131600
-----------------

Number of exceptions of this type:        1
Exception MethodTable: 000007feead3c240
Exception object: 000000000265b030
Exception type: System.NullReferenceException
Message: Service URL needs to be populated
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80004003
-----------------

Number of exceptions of this type:        1
Exception MethodTable: 000007feead0f288
Exception object: 00000000024c1158
Exception type: System.ExecutionEngineException
Message: <none>
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131506
-----------------

Number of exceptions of this type:        1
Exception MethodTable: 000007feead0f178
Exception object: 00000000024c10d0
Exception type: System.StackOverflowException
Message: <none>
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 800703e9
-----------------

Number of exceptions of this type:        1
Exception MethodTable: 000007feead0f068
Exception object: 00000000024c1048
Exception type: System.OutOfMemoryException
Message: <none>
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 8007000e
-----------------

Number of exceptions of this type:        2
Exception MethodTable: 000007feead0f398
Exception object: 00000000024c11e0
Exception type: System.Threading.ThreadAbortException
Message: <none>
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131530
-----------------

Number of exceptions of this type:        3
Exception MethodTable: 000007feead3e060
Exception object: 0000000002568e10
Exception type: System.IO.FileNotFoundException
Message: Could not load file or assembly 'DebuggingWF.XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
InnerException: System.IO.FileNotFoundException, use !PrintException 00000000025699a0 to see more
StackTrace (generated):
    SP               IP               Function
    00000000001FEB80 0000000000000001 System.Reflection.Assembly._nLoad(System.Reflection.AssemblyName, System.String, System.Security.Policy.Evidence, System.Reflection.Assembly, System.Threading.StackCrawlMark ByRef, Boolean, Boolean)
    00000000001FEB80 000007FEEABEBF61 System.Reflection.Assembly.InternalLoad(System.Reflection.AssemblyName, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, Boolean)
    00000000001FEC10 000007FEEAC247C4 System.Reflection.Assembly.Load(System.Reflection.AssemblyName)
    00000000001FEC50 000007FEE9285C0A System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(System.Type, System.String, System.Xml.Serialization.XmlSerializerImplementation ByRef)

StackTraceString: <none>
HResult: 80070002
-----------------

Total 10 exceptions

One thing you may have noticed are three odd-looking exceptions: ExecutionEngineException, StackOverflowException and OutOfMemoryException. These are three exceptions that every .NET process has on the heap. That’s because if you think about exception handling, when the runtime encounters an exception, it creates an exception object which is passed up the stack. If you are out of memory, or have a stack overflow guess what – you can’t create the object you need to report the exception.

One thing that would be very nice in !dae would be to somehow visually show hierarchies. For example, the first two exceptions have a parent/child relationship (NullReference is an inner exception of DebuggingWFException). But this is still quite helpful.

 

!CLRUsage

This command spits out statistics about the current CLR Usage. While you could infer this information through other means using SOS – “!eeheap –gc” for example – it is nice to have it in one place

0:000> !CLRUsage
Native Heap for .NET: 0x00480000
Number of GC Heaps: 1
------------------------------
GC Heap Size          0x1b2758(1,779,544)
Total Commit Size  00000000001c4000 (1 MB)
Total Reserved Size  0000000017e3c000 (382 MB)
Initial reservation type:
Initial Allocation Size: 0 (0) (0 MB)
Reserved Memory Size: 18000000 (402,653,184) (384 MB)
Reserved Memory Limit Size: 18000000 (402,653,184) (384 MB)

 

!DumpXmlDocument

There are several new dump commands available, but this looked interesting. If you have an XmlDocument on your heap, you can dump the contents using this command. Unfortunately trying to run this command I found two bugs:

  • “!dumpheap –type” used to take in partial type names. It appears you have to know the full type name now
  • “!DumpXmlDocument” isn’t working on my 64-bit system. It’s erroring with the following

0:000> !DumpXmlDocument 00000000026128b8
0x0000000000000000 is not an object

Which is important to note – Psscor2 is not supported by Microsoft, so you might find bugs in it.

The really cool features are in the ASP.NET support. To show this I attached WinDBG to my w3wp process and loaded psscor2.

 

!ASPXPages

This dumps out all HttpContexts in the heap and shows the relation to the .aspx page. This appears to be the same at !DumpHttpContext:

0:019> !ASPXPages
Going to dump the HttpContexts found in the heap.
Loading the heap objects into our cache.
HttpContext    Timeout  Completed     Running  ThreadId ReturnCode   Verb RequestPath+QueryString
0x00000000ff0c7890    0                    no       113 Sec     XXX        200    /DebuggingASP/
0x000000013f0c92a8    19200 Sec        no       113 Sec     XXX        200   GET /DebuggingASP/default.aspx
Total 2 HttpContext objects

 

!DumpASPNETCache

This dumps out all of the Cache objects from the Heap:

0:019> !DumpASPNETCache
Going to dump the ASP.NET Cache.
Loading the heap objects into our cache.
000000017f0c6c40

Name: System.Web.Configuration.MapPathCacheInfo
MethodTable: 000007fee674fe38
EEClass: 000007fee63b15d0
Size: 40(0x28) bytes
GC Generation: 0
(C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
              MT            Field           Offset                 Type VT             Attr            Value Name
000007feead0ec90  40018d6        8        System.String  0 instance 000000017f0c6cd0 MapPathResult
000007feead0de60  40018d7       18       System.Boolean  1 instance                1 Evaluated
000007feead0ef58  40018d8       10     System.Exception  0 instance 0000000000000000 CachedException
      Normal
04/08/2010 14:05:08
04/08/2010 14:15:08
04/08/2010 14:05:08

-----------------

[Output Truncated for blog purposes]

-----------------

000000013f048850

Name: System.Web.CachedPathData
MethodTable: 000007fee674ec38
EEClass: 000007fee6419bd0
Size: 64(0x40) bytes
GC Generation: 0
(C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
              MT            Field           Offset                 Type VT             Attr            Value Name
000007fee674f7f8  4000d09       30 ...l.SafeBitVector32  1 instance 000000013f048880 _flags
000007feead0ec90  4000d0a        8        System.String  0 instance 000000013f0487d8 _configPath
000007fee674c818  4000d0b       10 ...m.Web.VirtualPath  0 instance 0000000000000000 _virtualPath
000007feead0ec90  4000d0c       18        System.String  0 instance 0000000000000000 _physicalPath
000007fee674ea68  4000d0d       20 ...ion.RuntimeConfig  0 instance 000000013f0687e0 _runtimeConfig
000007fee66c2f58  4000d0e       28 ...andlerMappingMemo  0 instance 0000000000000000 _handlerMemo
000007fee674ecc8  4000d08       18 ...emRemovedCallback  0   shared           static s_callback
                                 >> Domain:Value  0000000000d48450:NotInit  00000000037cdb30:000000013f042540 <<
      Normal
04/08/2010 14:05:08
12/31/9999 23:59:59
04/08/2010 14:05:08

-----------------

Total 20 cache objects, Total size: 2,080

 

!DumpHttpRuntime

This dumps out all Http Runtime information found in the heap. This can be quite helpful when trying to diagnose things like AppDomain problems

0:019> !DumpHttpRuntime
Going to dump the HttpRuntimes found in the heap.
Loading the heap objects into our cache.
HttpRuntime 0x00000000ff0aeff0:
_shutdownInProgress: 0
_requestQueue:

0x000000013f0c9e90
_appDomainAppPath: C:\inetpub\wwwroot\DebuggingASP\
_appDomainAppId: /LM/W3SVC/1/ROOT/DebuggingASP
_fcm:

Name: System.Web.FileChangesMonitor
MethodTable: 000007fee674c730
EEClass: 000007fee6419778
Size: 104(0x68) bytes
GC Generation: 0
(C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
              MT            Field           Offset                 Type VT             Attr            Value Name
000007fee674e4d8  4000dcf       58 ...ReadWriteSpinLock  1 instance 00000000ff0b0758 _lockDispose
000007feead0de60  4000dd0       50       System.Boolean  1 instance                0 _disposed
000007feead165e8  4000dd1        8 ...ections.Hashtable  0 instance 00000000ff0b0950 _aliases
000007feead165e8  4000dd2       10 ...ections.Hashtable  0 instance 00000000ff0b09b0 _dirs
000007fee674e558  4000dd3       18 ....DirectoryMonitor  0 instance 00000000ff0b2bc0 _dirMonSubdirs
000007feead165e8  4000dd4       20 ...ections.Hashtable  0 instance 00000000ff0b0b28 _subDirDirMons
000007feead15b78  4000dd5       28 ...ections.ArrayList  0 instance 000000013f0400b0 _dirMonSpecialDirs
000007fee674e3f8  4000dd6       30 ...hangeEventHandler  0 instance 00000000ff0b2358 _callbackRenameOrCriticaldirChange
000007feead15f00  4000dd7       48         System.Int32  1 instance                0 _activeCallbackCount
000007fee674e558  4000dd8       38 ....DirectoryMonitor  0 instance 0000000000000000 _dirMonAppPathInternal
000007feead0ec90  4000dd9       40        System.String  0 instance 0000000000000000 _appPathInternal
000007feead15f00  4000dda       4c         System.Int32  1 instance                0 _FCNMode
000007feead04748  4000dce       48      System.Object[]  0   shared           static s_dirsToMonitor
                                 >> Domain:Value  0000000000d48450:NotInit  00000000037cdb30:00000000ff0b24a0 <<

 

Finally, perhaps the most important commands one can run against a production application:

!FindDebugTrue and !FindDebugModules

It’s amazing how often people slip ASP.NET deployments to production while leaving the Debug=true element in their web.config. These commands find this problem and located any modules compiled in Debug mode:

0:019> !FindDebugTrue
Loading the heap objects into our cache.
Debug set to true for Runtime: ff0aeff0, AppDomain: C:\inetpub\wwwroot\DebuggingASP\
Total 2 HttpRuntime objects

0:019> !FindDebugModules
Loading all modules.
Searching for modules built in debug mode...

App_Web_mlxisvdk.dll not built release
DebuggingASP.DLL not built release

Done Seaching

 

Hope you’ve enjoyed this little journey. If you want to learn more about .NET Debugging, check out Tess Ferrandez’s blog and the slides from a talk I did recently called Debugging .NET Applications with WinDBG. Enjoy!

Comments on this entry are closed.