Improve Driver DebuggabilityUpdated: February 16, 2004 On This Page
An often overlooked way to improve the quality of a driver is to make the driver easier to debug. Here are several tips that can ease debugging during both testing and retail use. 1. ASSERT() often.The ASSERT() macro evaluates an expression and, if the expression is false, breaks into the debugger. ASSERT() works only on binaries that are created in the checked build environment. Frequent use of ASSERT() can help you identify and debug problems caused by data that is not valid. In addition, ASSERT() statements provide useful documentation for anyone who might have to maintain your code later. Use ASSERT():
2. Use Windows tracing features.Microsoft Windows operating systems support several ways to log driver-related errors and information. Use them!
3. Log, log, log!Maintain an in-memory history log to help you determine what state your driver was in when a hang, crash, or bug check occurred. Record data that might be useful in debugging, such as the I/O request in progress, the number of bytes transferred, and so forth. A simple way of recording data is to create a rolling in-memory log:
LONG MyTrackerIndex;
MY_TRACK_DATA MyTracker[1024];
VOID
LogInTracker (
MY_TRACK_DATA * Data
)
{
LONG Index = InterlockedIncrement (&MyTrackerIndex);
Index %= 1024;
MyTracker[Index] = *Data;
}
At any given time, the log contains the most recent 1024 records. To dump the contents of the log in the debugger, create a debugger extension that is tailored to the data structures in the log. 4. Combine techniques.A simple macro that combines several of the preceding techniques can work as a RETAIL_ASSERT, providing valuable data on problems with a retail build. For example:
#define MY_RUNTIME_ASSERT(expr,p1,p2) \
if (MyRuntimeAssertsLevel && !(expr)) { \
if (MyRuntimeAssertsLevel >= 1){ \
MyLogRuntimeAssert ((p1),(p2),#expr,__FILE__,...) \
if (MyRuntimeAssert >= 2) { \
DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, \
"MyDriver.sys: RETAIL ASSERT ...", ...); \
if (MyRuntimeAssert >= 3) { \
DbgBreakPoint (); \
}}}}
#if DBG
ULONG MyRuntimeAssert = 3;
#else
ULONG MyRuntimeAssert = 1;
#endif
In this prototype macro, expr represents the expression that would be tested in a normal ASSERT(). The variable MyRuntimeAssertsLevel enables the logging, and the variable MyRuntimeAssert defines the level of logging to be performed. When the retail driver runs, errors are logged to a file. If debugging is enabled, the macro sends a string to the debugger and, depending on the value of MyRuntimeAssert, can break into the debugger. Using this or a similar macro can help you track errors such as timing problems that might occur during normal operation but might not appear during debugging. 5. Bug check if irreparable corruption has occurred.Issue a bug check if all of the following are true:
It is better to bug check than to propagate the corruption elsewhere in the system, possibly causing a crash later in some unrelated component. Such a crash in an unrelated component is often difficult to diagnose and repair. If possible, try to build restart logic into your driver. If your driver is not essential to system operation, you might be able to stop the driver and restart it, instead of issuing a bug check. ResourcesSee the Windows Driver Development Kit (DDK):
|
|
