August 1998
Download Aug98Bugslayer.exe (75KB)
John Robbins is a software engineer at NuMega Technologies Inc. who specializes in debuggers. He can be reached at john@jprobbins.com. |
You've probably heard the proverb, "An ounce of prevention is worth a pound of cure." When it comes to handling crash problems in your code, the proverb should be: "A couple of key lines of code can keep your customers using your application so you can keep your job." Well, I guess that might not be as pithy and memorable as the original, but at least my proverb mentions code.
Since I am not going to make a million bucks writing proverbs, I had better stick to what I need to talk about here in the Bugslayer column. This month I'll cover exception handlers and unhandled exception filters or crash handlers. If you have been doing any C++ programming at all, you have probably already dealt with exception handlers. Crash handlers are those routines that can gain control right before the application shows that nice fault dialog that drives your users crazy. While the exception handlers are C++-specific, the crash handlers work with both C++ and Visual Basic®-based code. To help you make your applications more robust, I will show you how to apply some of the assistance the operating system and the compiler offer. Additionally, if used judiciously, these ideas allow you to gather more information when your app does crash. This lets you solve potential problems faster. I will start out with a brief primer on exception and crash handling as the basis for some of the concepts that I will discuss later. I will also discuss the reusable code that I wrote for this column, which you can use in your exception and crash handlers. Finally, I will deal with some of the issues that have surfaced about the IMAGEHLP symbol engine that I first presented with the CrashFinder application in the April 1998 Bugslayer), this is exactly what I did to debug it. An alternative is to use a kernel debugger like WinDBG to get around this limitation. Another issue is that calling SetUnhandledExceptionFilter is a process global operation. If you build the coolest crash handler in the world for your OLE control and the container crasheseven if it's not your faultyour crash handler will be executed. While you might think this could keep you from using SetUnhandledExceptionFilter, I have some code that might help you out.
Handle Only This
Translate This
|
|
The most interesting functions are GetFirstStack-TraceString and GetNextStackTraceString. These functions, as their names indicate, let you walk the stack. Like the FindFirstFile and FindNextFile APIs, you can call GetFirstStackTraceString and then continue to call GetNextStackTraceString until it returns FALSE to walk the entire stack. In addition to the EXCEPTION_POINTERS structure, these functions take a flag option parameter that lets you control the amount of information that you want to see in the resulting string. The following string shows all the options turned on. |
|
The values in parentheses are the possible parameters to the function. Figure 4 shows the options flags and what each will include in the output string.
To see these functions in action, I included two sample test programs. The first, CH_TEST, is a C/C++ example. The second program, CrashTest, is a Visual Basic-based example. Between these two programs, you should get a pretty good idea of how to use all of the functions I've presented. The implementation of these functions is rather straightforward and consists mainly of string buffer manipulations. For all the symbol information, I use the IMAGEHLP.DLL symbol engine that I discussed in the April 1998 Bugslayer column. The interesting part of the implementation takes place at the end of Figure 3. When I first started testing the functions, I noticed that the source and line information for an address would appear the first time that I requested it, but that it never appeared on subsequent lookups at the same address. Several astute readers had mentioned that they had seen the same thing with the CrashFinder application, but we were never able to see what was unique about the situation. It appeared that there was a bug in the SymGetLineFromAddr function. It only finds the source and line information once for an address when looking up the information in PDB symbols. It seems to work correctly for C7 and COFF symbols. To work around this bug, reader Iain Coulthard figured out that SymGetLineFromAddr only seemed to find the source lines for addresses that are at the start of a linein other words, addresses with a displacement of zero bytes. Iain found that if you look backwards from the original address until you found the next address that had a zero displacement, the source line lookup works. After finding the zero-displacement address, just subtract the original address from the found address to compute the displacement. Iain's solution was much quicker than mine; the only way I found to make the SymGetLineFromAddr function work was to completely shut down and restart the symbol engine on each SymGetLineFromAddr call. I put Iain's workaround in the InternalSym-GetLineFromAddr function so the work was not scattered across the source. InternalSymGetLineFromAddr also takes care of the case where an older version of IMAGEHLP.DLL that does not support SymGetLineFromAddr is on the system. If you plan on using SymGetLineFromAddr for a debugger, you might want to wait until a fixed version of IMAGEHLP.DLL has been released. If you do not want my workaround, undefine WORK_AROUND_SRCLINE_BUG when building BugslayerUtil.DLL.
Wrap-up
April Bug
Da Tips
|
|
Using this class, you just need to declare a structure like the following and it is taken care of automatically: |
|
Have a tricky issue dealing with bugs? Send your questions or bug slaying tips via email to John Robbins: john@jprobbins.com
|
From the August 1998 issue of Microsoft Systems Journal.