Tracking Device Driver Memory Usage
Copyright © 1998 Mark Russinovich
Last Updated October 14, 1998
Introduction This article is intended for device driver developers.

A common location for programming errors in a complex project, be it a driver or a Win32 program, is in memory usage. Unforuntately, there do not yet exist any automated error detection tools for drivers like the ones that are available for Win32 programs. A single buffer overun, underrun, leak or double free can cause you to waste hours painfully tracking down the bug. To address this in some of my own complex drivers I've developed a simple set of memory functions that I use in place of the standard Ex-variety. With them I catch short underrun, overrun, and double free errors immediately. The functions also tell me when the system runs out of pool, and keeps track of how much of each pool my driver has allocated, along with the high-water marks for the driver's pool usage. These statistics clearly show me if my driver is leaking memoy.

Another benifit of the memory tracking functions I've developed is that they are almost totally disabled in a free build of a driver. However, their replacements for the ExInitializeLookasideList functions provide a useful service even then. In NT 4 SP3 Microsoft decided to place lookaside list depth management under the control of a dynamic tuning mechanism. Thus, the system ignores the value you pass in for the depth of the lookaside when you initialize it, and instead decides, based on the pattern of allocations and frees you make to the list, how deep the list should be over time. If you have a driver that you've tuned to expect a certain number of entries on a lookaside list you might be unpleasantly surprised at its performance under SP3's tuning algorithm. The memory functions I implement initialize lookaside lists in such a way that they are kept off of the system tuning list - the depth you specify is the real depth of a list and the system will not change it.
Using the Memory Tracking Library To use the library you first add memtrack.c to your driver's sources file. In each source file of your driver that you perform memory allocation or deallocation, include memtrack.h, and change the Ex prefix on the memory functions you use (ExAllocatePool, ExFreeToNPagedLookasideList, etc.) to Mem. Finally, call MemTrackInit() in your DriverEntry before any memory operations are invoked. If you want to see statistics regarding your drivers usage of paged and non-paged pool place calls to the MemTrackPrintStats function in appropriate places. MemTrackPrintStats takes one parameter, a text string, that it prints out to label the statistics, which are presented as DbgPrint output. Here is an example of the output:

      Filemon Nonpaged Memory Usage:

                  Current: 63838 bytes

                  Max: 127383 bytes

      Filemon Paged Memory Usage:

                  Current: 0 bytes

                  Max: 0 bytes

The way that the memory tracking functions work is straight-forward. The replacements for the lookaside list initialization functions, MemInitializeNPagedLookasideList and MemInitializePagedLookasideList, initialize a standard NT lookaside list structure by-hand, rather than letting the system do it. This prevents the system from knowing that the lookaside list exists, and therefore prevents the system lookaside list tuning algorithms from affecting the list. The memory allocation functions, MemAllocatePool, and MemAllocatePoolWithTag, use the ExAllocate functions to over-allocate the buffer that the driver requests. A buffer length signature is placed at the start and end of the buffer, and a pointer to the position in the buffer following the first signature is returned to the driver. When the driver calls MemFreePool the memory tracker can check to see if the signatures are both in place, which catches overrun and underrun conditions. Double frees are caught by placing a "buffer has been freed" signature in the buffer. If the driver frees the buffer again (and another driver hasn't allocated it and overwritten the signature in the mean-time) the double free will be detected.

The Sample I've instrumented Filemon for Windows NT as a demonstration of the memory tracking functions and their use. Changing the filemon driver to use the tracker took only a few minutes. Use DebugView, Windbg, or SoftICE to view Filemon using MemTrackPrintStats to show how much pool it is using.

Download Instrumented Filemon For NT (324KB)

Download memtrack.c and memtrack.h alone (5KB)