As mentioned previously, two similar tools are available commercially: Driver Monitor in the Compuware Numega DriverWorks software, and OSRTracer in the Open Systems Resources OSR DDK software.

Driver Verifier

Windows 2000 includes Driver Verify for catching some common types of driver error. Use the Driver Verifier Manager (Start+Programs+Development Kits+Windows 2000 DDK+Driver Verifier) to change the driver verify settings.

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. Driver Verify can flush all paged memory to disk before your driver is called at elevated IRQL.

Drivers must also cope well if a memory request fails. Another Driver Verify feature can forcibly fail a random number of memory allocation requests.

Debuggers

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 WinDbg debugger for NT and Windows 2000 systems. To use this, you must have two PCs — one running a checked build of NT/W2000 and another the free build — connected together using a serial cable. A normal network connection will also be useful. You can insert trace statements and debug at source-level. There are instructions for WinDbg in the DDKs. It will not be covered in this book.

NuMega Compuware at www.numega.com sells a debugger called SoftICE, which lets you to debug at source-level on a single PC.

DebugPrint

This book includes the DebugPrint software, which lets you use formatted print statements to trace the execution of a driver. Although not the ultimate debugging solution, it certainly gives a driver writer a useful tool for tracing what code a driver runs and what data is in variables. DebugPrint works in Windows 2000 and Windows 98, but not in earlier versions of these operating systems.

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 source is described in Chapter 14.

DebugPrint is described in full on the web site www.phdcc.com/debugprint.

Trying out DebugPrint

To use DebugPrint, you must first install the DebugPrint driver. Do this in a similar way to the Wdm1 driver. This time, browse to the DebugPrintsys directory and install the free build driver called 'DebugPrint driver debugging tool'.

You can now use the DebugPrint Monitor application to listen for trace print events from drivers that you are testing. You will probably find it convenient to set up a shortcut to this application at DebugPrintexeReleaseDebugPrintMonitor.exe.

The Wdm1 driver includes various calls to the DebugPrint software. However, these calls only appear in the checked build of the driver. (You can opt to include the DebugPrint output in free builds.)

If you now reinstall the Wdm1 driver, selecting the checked build, you should see various events in the DebugPrint Monitor window, as shown in Figure 6.1. The displayed output is described in the next chapter.

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

Using the DebugPrint Monitor

The DebugPrint Monitor program runs on the same computer as the drivers you are testing. It is a standard user mode MFC Win32 application that needs the shared MFC42.DLL library.

The Monitor is very easy to use. When started, it begins listening for DebugPrint trace events. If any events are buffered up, these are read and displayed first.

The Monitor displays one event per line. It has columns for the driver name, a timestamp, and the actual trace message. The latest event is always scrolled into view.

Use the Edit+Delete events menu to clear all the events from the display.

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 Monitor remembers its position on the screen and the column widths. Printing events is not yet supported.

Using DebugPrint in Drivers

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 DebugPrint functions in your driver code. You can only make these calls at DISPATCH_LEVEL IRQL or lower. This means that you can make DebugPrint calls in your DriverEntry, the main IRP dispatch routines, and in StartIo and Deferred Procedure Call (DPC) routines, but not in interrupt handling routines. The DebugPrintInit routine must be called at PASSIVE_LEVEL

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 functions will never overflow the internal output buffer that they allocate. All wide characters are simply cast to single-byte ANSI characters when they are put in an output string.

DebugPrint calls should only introduce minor delays into the execution of your driver. The main work of the DebugPrint calls takes place in a system thread that runs in the background at a low real-time priority.

DebugPrint Calls

Listing 6.1 illustrates a typical series of DebugPrint calls in three standard driver routines.

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату