Managed Debugging With WinDbg, Part 1 of N
February 5th, 2005
There seems to be relatively little information about WinDbg available, so I will try to post some things as I figure them out myself.
We had a severe-to-extremely-severe production problem last week, and my recent activity with WinDbg was another example of “learning with a gun to your forehead.”
Getting the Right Extensions
If you are doing any kind of managed debugging, you will want the current set of extensions which can be found here.
The simplest thing to do is just drop the .dlls in the install directory for WinDbg. The two dll’s in this set that will be most important to you are psscor.dll and sieextpub.dll. Psscor has a lot of tools for dumping the contents of managed objects, and the second has some powerful functions for showing application and thread state.
There is a third extension, sos.dll, whose functionality is mostly overlapping with psscor.
Load an extension into WinDbg like so:
0:000> .load psscor
At any point, you can see the extensions you have loaded with this command:
All of the extensions I’ve mentioned come with help commands. The help for the topmost extension in the chain can be called like this:
But you can always refer back to extensions further down in this way:
An Example — Debugging an ASP.NET Hang
This was the scenario we found ourselves in last week. The first thing you will need to do is get a hang dump of the worker process using AdPlus, which I don’t have time to cover here. However, it is relatively straightforward.
Once we’ve got the dump, the most obvious thing to try is to see what the process is doing. To do that, load psscor and use this command:
This will dump out the managed stack of all of the threads in the process (the ~*e means that we want to iterate through all of the threads and perform the specified action for each).
That will give us a general idea of what is going on. The stack of one or more particular threads is bound to be interesting, and we can narrow it down to the stack trace of a single thread using:
Here I’ve replaced * (all threads) with a single thread, 113. Assuming this is a managed thread, you should see some output like this.
Thread 113 ESP EIP ... 0x0dc5f6cc 0x0fa3bb17 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequestMain() 0x0dc5f710 0x0fa3aedf [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest() 0x0dc5f74c 0x0fa3a94b [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest(Class System.Web.HttpContext) ...
For this example, I’ll just show how to figure out which page in the application is executing. Since we’re running inside a page class for a lot of the response, the this pointer should be all we need.
Grab two stack pointers (the ESP register) and use the
psscor.DumpStackObjects command, or
dso for short:
0:000> !dso 0x0dc5f6cc 0x0dc5f74c Thread 0 ESP/REG Object Name 0x0dc5f6cc 0x31f0d208 System.Collections.Specialized.HybridDictionary 0x0dc5f6d0 0x0371c55c _ASP.incomeStatement_aspx 0x0dc5f6d8 0x0371c55c _ASP.incomeStatement_aspx 0x0dc5f714 0x31f0d208 System.Collections.Specialized.HybridDictionary 0x0dc5f720 0x070ca198 System.Globalization.CultureInfo 0x0dc5f724 0x070fb010 System.Threading.Thread 0x0dc5f728 0x0371c55c _ASP.incomeStatement_aspx 0x0dc5f73c 0x31f0d208 System.Collections.Specialized.HybridDictionary
Bingo — in this case,
_ASP.incomeStatement_aspx is an instance of the page class.
Note that the first argument to dso is the upper stack pointer, and the second is the lower stack pointer. I’ll post some more stuff when I have time.