As mentioned previously, two similar tools are available commercially:
Windows 2000 includes
Drivers can corrupt memory by accessing outside their allocated buffers. W2000 can use a special pool, with inaccessible memory at either side of a buffer. If you try to write to the inaccessible memory, a bugcheck occurs. The facility can be turned on for all your driver's memory requests. Alternatively, you can use it selectively by calling ExAllocatePoolWithTag with an appropriate Tag parameter.
Another common mistake is to access paged memory at DISPATCH_LEVEL IRQL or higher. A driver may 'get away with it' if the paged memory is resident.
Drivers must also cope well if a memory request fails. Another
The final and most powerful tool is a debugger. Standard user mode debuggers like Visual Studio do not work with drivers.
Instead, you must use a debugger designed for the job. The Microsoft DDKs come with the
NuMega Compuware at www.numega.com sells a debugger called SoftICE, which lets you to debug at source-level on a single PC.
This book includes the
The source code of the DebugPrint driver and user mode monitor are included with the book. The DebugPrint driver serves to illustrate several driver development techniques. The
DebugPrint is described in full on the web site www.phdcc.com/debugprint.
To use
You can now use the
The Wdm1 driver includes various calls to the
If you now reinstall the Wdm1 driver, selecting the checked build, you should see various events in the
Note: A revised version of installation file DebugPrintsysDebugPrint.INF is available on the book's web site, www.phdcc.com/wdmbook. This updated version will install DebugPrint in W98.
Figure 6.1 Sample DebugPrint Monitor output
The
The
The
Use the
You can save the current list of events to a .dpm file. This is an ASCII text file with the columns separated by tab characters. You can reload .dpm files.
The
First, you must copy two standard source files into your driver project, DebugPrint.c and DebugPrint.h. Include DebugPrint.h in the driver's main header file. Amend the SOURCES file so that DebugPrint.c is built. These files are in the Wdm1 project source code.
Make calls to the
By default, the DebugPrint source only prints in the 'checked' debug build. However, you can force it to work in the 'free' build by setting the DEBUGPRINT preprocessor variable to 1 before you include DebugPrint.h, e.g.,
#define DEBUGPRINT 1
The
DebugPrint Calls
Listing 6.1 illustrates a typical series of
Listing 6.1 Typical DebugPrint calls
NTSTATUS DriverEntry(…) {
#if DBG
DebugPrintInit(Wdm1 checked');
#else
DebugPrintInit('Wdm1 free');
#endif
…
DebugPrint('RegistryPath is %T', RegistryPath);
…
}
NTSTATUS Read(…) {
…
DebugPrint('IRP %I. Reading %u bytes from %x', Irp, IrpStack->Parameters.Read.Length, Irp->AssociatedIrp.SystemBuffer);
…
}
VOID Unload() {
…
DebugPrintClose();
}
Call DebugPrintInit to initialize the connection to the DebugPrint driver, passing the name of your driver as an ANSI NULL-terminated string. You typically use DebugPrintInit in your DriverEntry routine. Call DebugPrintClose to close the connection in your driver unload routine.
You can write simple NULL-terminated ANSI strings using DebugPrintMsg. For formatted print trace statements, use DebugPrint or DebugPrint2. DebugPrint uses an internal 100-byte buffer. DebugPrint2 lets you specify the size of buffer to allocate.
Calls to DebugPrint or DebugPrint2 must include a NULL-terminated ANSI format specification string, which may include one or more format specifier characters shown in Table 6.1 (e.g., %c for an ANSI character). You must provide an extra argument for each specifier in your format string. Failure to get this right could very easily result in an access violation. Be careful to use the correct