Using the Debug C Run-Time Library

[Previous] [Next]

The first step in using the DCRT library is to include it and turn it on so that you can start benefiting from the memory tracking as early in your project as possible. In your main precompiled header file (or whatever header file all the source files in your project will include), add the following line before any #include directive:

#define _CRTDBG_MAP_ALLOC

After the rest of your header files, include CRTDBG.H. Defining _CRTDBG_MAP_ALLOC will redirect normal calls to memory allocation and deallocation functions to special versions that record the source file and line number for each allocation or deallocation.

The second step you need to take is to turn on the DCRT library heap code. As I mentioned at the beginning of this chapter, by default, most of the features of the DCRT library are turned off. The documentation states that most of the features are turned off to keep the code small and to increase execution speed. Although size and speed might be important for a release build, the whole point of a debug build is to find bugs! The increased size and reduced speed of debug builds is inconsequential. So don't hesitate to turn on all the features you think will help you. The _CrtSetDbgFlag function takes a set of flags, shown in Table 15-2, that can be OR'd together to turn on various options in the DCRT library.

After building your application with the appropriate #includes and #defines, and calling _CrtSetDbgFlag, you now have the DCRT library, with its slew of functions that help you control and report on memory usage, fully available. You can call these functions at any time. Many of them lend themselves to being used in assertions, so you can sprinkle them around freely and catch your memory problems close to the source.

One of the most useful DCRT library functions is _CrtCheckMemory. This function walks through all the memory you've allocated and checks to see whether you have any underwrites or overwrites and whether you've used any previously freed blocks. This one function alone makes the entire DCRT library worth using.

Another set of functions allows you to easily check the validity of any piece of memory. The _CrtIsValidHeapPointer, _CrtIsMemoryBlock, and _CrtIsValidPointer functions are perfect for use as debugging parameter validation functions. These functions, combined with _CrtCheckMemory, offer excellent memory checking features.

Table 15-2 Debug C Run-Time Library Flags

FlagDescription
_CRTDBG_ALLOC_MEM_DFTurn on the debug heap allocations and use the memory block identifiers. This is the only flag that's on by default.
_CRTDBG_CHECK_ALWAYS_DFCheck and validate all memory on each allocation and deallocation request. Turning on this flag catches any underwrites and overwrites as close to when they happen as possible.
_CRTDBG_CHECK_CRT_DFInclude _CRT_BLOCK memory allocations in all leak detection and state differences. In general, unless you're having a problem with the CRT library functions, you shouldn't turn on this flag. If you do, you'll get CRT library memory allocation reports. Because the CRT library must keep some memory allocated until the true end of the program, which is after the leaked memory reporting, you'll see a large number of false positive leak reports on that memory.
_CRTDBG_DELAY_FREE_MEM_DFInstead of truly freeing memory, keep the block allocated and in the internal heap list. The blocks are filled with the value 0xDD, so you know the memory is freed when you're looking at it in the debugger. By not freeing the memory, this flag allows you to test your program under memory stress conditions. Additionally, the DCRT library will check that you don't access the deallocated block again by ensuring that all values in the block remain 0xDD. You should always turn on this flag, but keep in mind that your program's memory requirements can easily double because the deallocated memory isn't reclaimed by the heap.
_CRTDBG_LEAK_CHECK_DFCheck for memory leaks at the end of the program. Turning on this extremely useful flag is mandatory.

Another neat feature of the DCRT library is the memory state functions: _CrtMemCheckpoint, _CrtMemDifference, and _CrtMemDumpStatistics. These functions make it easy to do before-and-after comparisons of the heap to see whether anything is amiss. For example, if you're using a common library in a team environment, you can take before-and-after snapshots of the heap when you call the library to see whether there are any leaks or to see how much memory the operation uses.

The icing on the memory-checking cake is that the DCRT library allows you to hook into the memory allocation code stream so that you can see each allocation and deallocation function call. If the allocation hook returns TRUE, the allocation is allowed to continue. If the allocation hook returns FALSE, the allocation fails. When I first discovered this functionality, my immediate thought was that, with a small amount of work, I could have a means to test code in some really nasty boundary conditions that would otherwise be very difficult to duplicate. You can see the result of this brainstorm in MemStress, a feature of BUGSLAYERUTIL.DLL that allows you to force allocation failures in your programs, which I'll present later in the chapter.

The cherry on top of the icing on the memory-checking cake is that the DCRT library also allows you to hook the memory dump functions and to enumerate client blocks (your allocated memory). You can replace the default memory dump functions with custom dump functions that know all about your data. Now, instead of seeing the cryptic dumped memory you get as the default (which besides being hard to decipher isn't that helpful), you can see exactly what the memory block contains and format it exactly as you want. MFC has the Dump function for this purpose, but it works only with CObject-derived classes. If you're like me, you don't spend your entire coding life in MFC and you need dumping functions that are more generic to accommodate different types of code.

The client block enumeration feature, as the name implies, allows you to enumerate the memory blocks you've allocated. This excellent feature gives you the power to create some interesting utilities. For example, in the MemDumperValidator functions in BUGSLAYERUTIL.DLL, I call the dumping hooks from the client block enumerator so that the enumeration can dump and validate many types of allocated memory in one operation. This extensible validation is critical in that it allows you to do deep validations instead of the surface checks of underwrites and overwrites. By deep validation, I mean an algorithm that knows the data formats in the memory block and walks those formats making sure that each data element is correct.



Debugging Applications
Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
ISBN: 0735615365
EAN: 2147483647
Year: 2000
Pages: 122
Authors: John Robbins

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net