Man eats crow, film at eleven

Last week I’d had a bit of a rant here about not using software for anything other than what it is designed to do. Thinking back, though, I have done this quite a few times myself, particularly by employing a code profiler to do the job of a debugger.

When good software goes bad, the right axe to wield at it is usually a debugger. Anyone who has used a debugger will probably tell you that this is a last resort, though, unless they are masochists who enjoy inflicting mental anguish on themselves. Even with the whizzy .NET runtime environment we have now, trying to find the cause of a crash or hang using a debugger requires quite a lot of patience, skill, and experience.

Yesterday, I had to try to diagnose a program hang — not my favorite thing in the world. So off I went as I have done squillions of times, using the old auto-dumper from Microsoft to get a dump file for analysis:

cscript adplus.vbs -hang -pn MyFlakyProgram.exe

Only this time, the debugger died before reaching the critical part of the code, making a dump too early and detatching from MyFlakyProgram. What now? Debug the debugger?

It was time to rummage through the old toolbox and see what else I could do. I have a code profiler — sure, why not? I had attached a code profiler to the flaky application, got to the critical bit, and took a snapshot. Then another. It seemed that the application had been running a SqlDataReader.Close() method for about 214 seconds by then, which sounds about as far from normal as Timbuktu is from where I’m sitting.

Honestly, I still haven’t worked out why this had happened yet, but I am that critical one step closer, no thanks to the CDB debugger that crashed out on me.

The other time I had used a code profiler in an unorthodox way was to try to figure out why an application was crashing with a TypeInitializationException. Again, this happened too quickly to have time to attach a debugger (yes, I know about the ImageFileExecutionOptions registry key, but haven’t you figured out how unapologetically lazy I am yet?).

TypeInitializationExceptions are thrown when an application is first being loaded into the .NET runtime, so there is no possibility to get a stack trace when the program bombs. You’ll never, ever figure out what has happened without some sort of Intermediate Language X-Ray machine, which is sort-of what a code profiler does.

The profiler also shows the method results in more-or-less chronological order, so maybe, just maybe, I could work out what the last method executed by the errant program had been. And as by a crepuscular ray of light, the answer was illuminated — someone had put a method that enumerates printers inside a type initializer (the bit of a class where variables are declared and have their initial values set). Here was the culprit:

internal class MyForm : System.Windows.Forms.Form

{…

private

static bool s_HavePrinters = PrinterSettings.InstalledPrinters.Count > 0;

As fate would have it, this code doesn’t work so well when your Print Spooler service is stopped!

.NET managed code profilers are designed to show where performace problems may exist in applications by measuring the time taken to enter and exit a method in the code. They could also, however, be leveraged as a powerful potential fault-finding tool. I wouldn’t absolutely depend on one for this purpose, but it’s nice to have a backup troubleshooting method when my debugger is on the fritz.