Before I start, I just want to issue a quick disclaimer that I used to work at NuMega and was on the teams who wrote BoundsChecker versions 3, 4, and 5. Other products that compete with BoundsChecker, such as Rational’s Purify, are very good and deserve your consideration. Obviously, I am quite partial to BoundsChecker and I just feel that you should be aware of that fact up front.
As someone who concentrates on debugging for a living, one of the most frequent questions I get is people want to know how I integrate and use Compuware/NuMega’s BoundsChecker to solve problems. Many people have BoundsChecker or are considering it, but are very curious as to how to get the most out of a complicated product. In this column, I will clarify some confusion about different parts of BoundsChecker. The meat of the column will be about which settings I use and how I go about using BoundsChecker in my daily development. Finally, I will cover how I use BoundsChecker on the hardest to debug things of all, ISAPI extensions.
If you have looked at NuMega’s product line, you have probably noticed that they also have a product called SmartCheck. SmartCheck is a cousin of BoundsChecker that helps find errors in Visual Basic application. The two products are similar in usage, but since SmartCheck has all of that Visual Basic knowledge so it can show events in Visual Basic terms and can analyze those completely obtuse Visual Basic errors. While I will use the term “BoundsChecker” throughout this column, if you are doing Visual Basic development, just substitute “SmartCheck” as I use both products essentially the same way. The main difference is that SmartCheck does not have the FinalCheck technology that BoundsChecker does.
Clearing the Air
BoundsChecker is a fantastic error detection tool that finds all your memory corruption problems no matter if they are in heap allocated memory, static memory, or stack memory. The best part of BoundsChecker, in my opinion, is in the API/COM parameter and return value checking. Since the Window’s API has grown through accretion, instead of any overarching plan, there is some strange consistencies across different parts. While you might think calling GetVersionEx would be a straight forward, it can fail and you have no idea why. The OSVERSIONINFO structure you pass to GetVersionEx has one of those annoying size fields that you must fill before you can call the API. BoundsChecker catches those kinds of errors with aplomb and instead of spending your time debugging stupid problems; you will spend your time implementing features after using BoundsChecker. To learn more than you ever wanted to know about BoundsChecker, check out all the literature at www.numega.com.
One area where there’s quite a bit of confusion with BoundsChecker is because there are two modes of operation, ActiveCheck and FinalCheck. Part of the problem I think is that there have been some misrepresentations about when each mode is appropriate. ActiveCheck does not require a compilation and will find tons of errors for you. To use active check, either you start debugging with the BoundsChecker integration enabled, or you open the file in BC.EXE. That’s all there is to using ActiveCheck. FinalCheck does require a recompile because BoundsChecker’s instrumentation will insert context into the project so that you can get wonderful error reporting such as the exact point when memory is leaked instead of waiting until the application shuts down like you have to do with ActiveCheck. The problem is that some people have been given the impression that to get any error checking whatsoever out of BoundsChecker, you have to always recompile your whole application all the time. Nothing could be farther from the truth, as you will see in the section about how I decide when to use which mode.
The final issue I want to address with BoundsChecker is its performance. Granted, if BoundsChecker takes three hours to run your application, which normally takes only ten minutes, there’s a serious problem and you should report it to NuMega. However, I have had developers tell me they don’t use BoundsChecker because it takes a few minutes more for their overall application run. That’s perfectly fine with me if you would rather save the few minutes on a run but spend two weeks or more tracking down a bug that BoundsChecker would have caught. Because of all the parameter, memory, return value, and COM interface tracking, there will be additional overhead when running your application under BoundsChecker. It’s simply a fact of life. However, your time is so valuable, anything you can do to find that error faster is a great thing. If you are not using an error detection tool because you think it’s too “slow,” you are leaving tons of bugs in your code.
My Preferred Settings
BoundsChecker has plenty of options to keep you busy figuring out which ones you should use. I’ll make it very simple for you; set the error detection level to Maximum and leave it here. In my opinion, Normal mode suppresses errors I think you should see. If you have never used Maximum mode you will see many more errors reported in the popup window. That’s fine, just click on suppress and so you suppress the ones that are in modules where you don’t have source code. To get all your projects set to Maximum mode, either run the standalone BoundsChecker shell, BC.EXE without a file open, or don’t have a project open in Visual C++. In the BoundsChecker settings dialog Error Detection tab, select Maximum in the Error Detection Scheme list box. Now all projects you create will start in Maximum mode.
If I am tracking down some nasty memory problems, I will set the error detection settings to Custom settings. I make sure all the same settings are checked as they are in Maximum mode, but in the “Memory error checking” category, I will check “Check all heap blocks on each memory function call” so that memory is constantly checked. This will slow the application down, but trigger error popups closer to the point where they happen.
By default, I leave the “Collect and report program event data” unchecked in the BoundsChecker Settings dialog, Event Reporting tab. I only turn event reporting on when I need to see the flow of the application in a specific project. If I do want to look at events, I check all the boxes in “Additional Event Reporting” so I see everything. One setting I always turn off in the Event Reporting tab is the “Prompt to save program results” because I view the event logs from BoundsChecker as simply a snapshot in time and have no need to save them.
Even though NuMega’s marketing department will shoot me, I have to admit that I don’t use BoundsChecker all the time. When developing new code, or updating existing code, I follow a standard pattern. I write a little bit of code, maybe a couple of functions or the initial parts of a complicated function and immediately test the code with the debugger. The primary reason is so I can evaluate general logic and flow. Since BoundsChecker can’t catch those types of problems, I work to avoid them right from the beginning. After I have gotten a particular feature or written a significant amount of code (on the order of 100-200 lines, including comments), I turn on integrated debugging, or start BC.EXE, and run my tests under BoundsChecker using just the ActiveCheck portion. One thing I have found is that taking the incremental approach to development like this means that BoundsChecker does not find many errors for me when using ActiveCheck. If you are doing your job correctly, you shouldn’t see any errors in BoundsChecker anyway!
Approximately once every day or at least every other day I am developing code, I will use BoundsChecker’s FinalCheck and instrument all of the new code I have been writing. Using the instrumented version, I test the code fairly extensively so that I get as much code coverage as possible. Since you can use both BoundsChecker instrumentation and NuMega’s TrueCoverage instrumentation at the same time, I generally turn on both so I can see what sections of the code need better testing.
The whole key here is that I am pounding on the code as much as possible with BoundsChecker long before I check it in to the master sources. If everyone on the team does the same thing, the quality of your master sources will shoot through the roof. Yes, it takes much more effort than you probably have considered for testing, but my opinion is that code quality is solely the developer’s responsibility and we have to be prepared to do what it takes to get there.
I want to clarify how much FinalCheck instrumentation I use. If I am working on a large application, I generally only use BoundsChecker FinalCheck instrumentation on the module where I am adding code. There is no need to instrument your 30MB of source code over 15 modules each time you want to run BoundsChecker. With my approach, you get the advantage of FinalCheck on the parts that need it, without the overhead on the parts that don’t. About once a week, I will instrument the entire project and put it through its paces. That way you won’t see any surprises down the road.
Since I recommend running in with the error detection level set to Maximum, you will probably get quite a few more error popups than you are used to seeing. Many times, I turn off Report Errors Immediately so I can do the entire test run without being bothered. When the run ends, I start at the top of the errors and look at each one. What you will see are many errors in various modules where you do not have source. For example, I use the Intellipoint software, which has a DLL called MSH_ZWF.DLL that helps with the mouse wheel. When you run a GUI application, under BoundsChecker, there are at least a few errors from that DLL in each of my runs. Obviously, any errors out of MSH_ZWF.DLL is something that I want to suppress by right clicking on the error, selecting Suppress from the menu, and choose the third item down, “Suppress this error only when it occurs in the EXE or DLL.” If an error of any type comes out of a module where I do not have source code, you can generally safely suppress it after you look at it to verify the error is not a parameter you passed in wrong. The errors that I am most interested in looking at are those that I have source code, as they are the ones I put in the code.
For the errors that do come out of my source code, I evaluate them carefully. If BoundsChecker reports an API Failure, I verify that I am properly checking that return value. If I am doing the appropriate check, I will suppress the error. When looking at memory leaks, I check them very carefully. Occasionally, BoundsChecker’s injected DLL, BCCORE.DLL, will get unloaded before other parts of your application after ExitProcess executes. When that happens, BoundsChecker takes the safe route and reports any allocated memory as memory leaks. You will mostly see memory and resource leaks on memory and resources allocated by static classes because those are the last things cleaned up in your process. If you can see that you are properly releasing resources and memory in those cases, it’s safe to suppress those errors.
Since I am a paranoid person, I temporarily rename my project’s suppression file one a week to ensure I did not accidentally suppress any real errors. The project suppression file is stored in the same directory as the main executable for your project and is named
//SUPPRESSIONPROJ:wordpad //VERSION:5.00 //ENABLE:Yes !include Mfc.sup ; NOTE!!include's lines are part of the header. ignore failure USER32.DLL:ShowCaret in module RICHED20.DLL ignore param 1 GDI32.DLL:GetDeviceCaps in module RICHED20.DLL ignore param 1 USER32.DLL:GetSystemMetrics in module CSCUI.DLL ignore param 1 KERNEL32.DLL:VerifyVersionInfoW in module CSCUI.DLL ignore failure KERNEL32.DLL:VerifyVersionInfoW in module CSCUI.DLL
To make my life a little easier with suppressions, I add DLL’s that I dont have source for to the master suppression files. There are two master suppression files, one for Windows 9x, SKIP_32C.SUP, and one for Windows NT/2000, SKIP_NT.SUP, and they are in the
ignore everything in module MSH_ZWF.DLL
Using BoundsChecker on ISAPI Extensions
Since everyone is now out to create the next amazon.com, seemingly everyone is using IIS with ISAPI extensions to drive their web sites. However, debugging an ISAPI extension is hard enough and using error detection tools is even harder. Fortunately, I found a little trick that makes using an error detection tool quite simple. As most of you should realize, you can run IIS as a console user-mode application as specified in “TN063: Debugging Internet Extension DLLs” in the MFC Technical Notes on the MSDN. That makes general debugging easier, but error detection tools need a process to step so that they can properly match up report all memory leaks.
The trick I use is to add a special command to my extensions, something like “KillIIS,” that can be called from the browser. All the KillIIS function does is call ExitProcess. Now you can use the integrated BoundsChecker in the Visual C++ debugger, and get all the great error information easily. While there are a few issues with running IIS as a console application, the benefits of complete error checking make it worth it.
In this column, I’ve only been able to touch on the surface all the power you have in BoundsChecker. In addition, some areas I didn’t cover include the fact that you can add your own function parameter validation to BoundsChecker or the amazingly cool Compliance Window, which can report if your application uses APIs that prevent it from running on other Windows versions. I hope I gave you a good idea how I use BoundsChecker and helped you speed up your bug bashing. In a future column here on CodeGuru, I will discuss reverse engineering techniques and just to give you a taste, I will leave you with the thought that BoundsChecker makes reverse engineering in some scenarios mere child’s play!