Книга: Writing Windows WDM Device Drivers

DebugPrint

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 uppercase or lowercase format specifier type character.

The available format specifiers cover a range of useful types. %I prints out the major and minor codes of an IRP. %T prints a UNICODE_STRING. Line feed and carriage return characters in the format string are ignored.

The %l, %L, %s, %S, and %x format specifier characters let you use one or more modifier characters to specify the maximum output width (e.g., %*l and %nl). The * modifier takes the maximum size from the next integer parameter to DebugPrint or DebugPrint2. The n modifier specifies the exact output width; where n is one or more characters between 1 and 9.

Table 6.1 DebugPrint format specifiers 

Format Specifier Type
%c ANSI character char
%C Wide character wchar_t
%d, %i Signed integer in decimal int
%D __int64 in decimal __int64
%I IRP major and minor codes PIRP
%l __int64 in hexadecimal __int64
%L LARGE_INTEGER in hexadecimal LARGE_INTEGER
%s NULL-terminated ANSI character string char*
%S NULL-terminated Wide character string wchar_t*
%T UNICODE_STRING PUNICODE_STRING
%u ULONG in decimal ULONG
%x ULONG in hexadecimal ULONG

Оглавление книги


Генерация: 0.772. Запросов К БД/Cache: 2 / 0
поделиться
Вверх Вниз