Friday, March 24, 2006

First go...recovering a call stack in WinDbg

I thought I'd write a bit about the fun I've had with debugging. This is mostly for my benefit, but if it helps you too, then whoopedoo.

If you're not already familiar with WinDbg, two really great places to go for information are John Robbins excellent book "Debugging applications for Microsoft .Net and Microsoft Windows" and Tess Fernandez's blog "If broken it is, fix it you should".

Mostly I've been wrasslin' with Microsoft's WinDbg, trawling through crash dumps with my "Wha' 'appen?" hat on. These sessions tend to go in fits and starts as there are occassional (blessed) hiatuses when the system doesn't need my undivided attention. Following a gap between debugging sessions the anaesthetics of time and family life conspire to drain my memory and I usually spend the first few minutes of analyzing a new dump in an unproductive haze. I've done this often enough to think "I gotta write some of this stuff down somewhere I can find it again."

I tend to deal with crash dumps from COM+ packages that have gone south, and one of the things I forget most frequently is "How do I recover the stack trace when COM+ has caught an exception?"

First things first: get your symbols straight. This used to be fiddly (for me, being a bear of very little brain), but since Microsoft made their symbols available on the net and made the symbol server available to us reg'lar folks it's pretty straightforward. Read the docs, point at Microsoft's symbols, set your own symbol server up and move along. You only gotta do this once, and if you're really lucky you can get one of your minions (sorry Neil:) colleagues to do it for you - it's a public service, and we all feel the benefit.

An early port of call is Microsoft's excellent DebugDiag tool available here (or google "IIS Diagnostics Toolkit" ). Running DebugDiag's crash and memory analysis scripts can show a nice recovered call stack following an exception. This is fine, and with a following wind might even be all you need. However, when you are not blessed weatherwise it's time to break out WinDbg and have a good ol' root 'round.

When you've got your dump loaded into WinDbg and the faulting thread shows that you've ended up with an exception, the call stack doesn't show the calls leading up to the instruction that threw the exception. What you'd like to see is the nice "recovered" call stack that DebugDiag shows you, and poke around on that stack to get some more info about how things got to be so broken. To reach this happy (or at least happier) state, you need to use the .cxr WinDbg command.

I can usually remember this much. But then I come unglued. How do I figure out what argument to feed to the .cxr command to get my nice happy call stack back? I know I've seen this written down somewhere, but what with age, attention span of a butterfly, etc. I can't remember where. So I keep having to find out again. What I remember now (prior to writing this, where I promise I'll look in future) is that I can work it out by looking at DebugDiag's crash dump analysis scripts. This is still pretty tedious (I remember I start by grepping for "recovered call stack" or some such if you need a clue).

When you've had an exception in a COM+ package, one way of finding the address to feed to the .cxr command (which is divinable from DebugDiag's crash dump analysis script) is to look at the call stack you currently have with the trusty kb command. If you can spot a call to comsvcs!ComSvcsExceptionFilter, you're in good shape. The first agument to this call is a pointer to an exception structure. Dump the first few bytes of this structure with the dd command thus:

dd xxxxxxxx

where xxxxxxxx is the value of the first argument to comsvcs!ComSvcsExceptionFilter. The second DWORD dumped by the dd command above is what you need to provide as the argument to the .cxr command to get the original exception-throwing call stack back with another kb command.

I think you can play pretty much the same game with kernel32!UnhandledExceptionFilter and NTDLL!rtlDispatchException, but I've yet to try it in anger.

Hope this helps (either you or me).

1 Comments:

Anonymous Anonymous said...

Oops - I knew that I'd see this done better elsewhere. For a better (more general) way to recover the call stack, see Mike Stall's .NET Debugging blog entry here

12:34 pm  

Post a Comment

<< Home