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!
Good one. There are few undocumented psscor2 functions
http://naveensrinivasan.com/2010/04/03/undocumented-psscor2-functions/