Debugging

Win32 user mode programs run within the protective arms of the operating system. For example, it stops them accessing memory that is not in a task's address space. Windows 98 programs have some of the kernel mapped into its upper addresses, so user mode programs could trample on the kernel. The protection offered by NT and Windows 2000 is better. If a user mode application runs into problems, it is usually possible to stop the program and carry on with other work.

A device driver has none of this protection because it is part of the kernel. Be especially careful to test your driver fully or you may lose data. Be prepared for the worst on your own development computer by making regular backups.

Although problems with a driver can be a real pain, it can eventually give you a greater understanding of what you are doing. You have to inspect each call or section of code carefully. Rather than just following the instructions given here, you may eventually really know what is going on. Or even, heaven forbid, why the system was designed to work the way it does. You may have suggestions for how drivers or the system might be improved.

How Do Things Go Wrong?

Windows 98 and Windows 2000 sometimes react differently to errors in drivers.

Crashes

A fatal error in NT or W2000 causes the 'blue screen of death', properly called a 'bugcheck'. You must reset the computer to continue. The blue screen gives an indication of the error, and usually a list of the kernel modules and a stack trace. More details of how to decode this information are given later. Annoying though these bug checks are, they do stop further damage being done to the operating system, such as corrupting disks.

NT and W2000 usually log a 'Save Dump' event for a bugcheck, listing the most pertinent information. An event may not be logged if the bugcheck occurred at boot time, before the event log service has started.

In W98, a similar blue screen appears for fatal errors, again giving a brief description of what went wrong. W98 can usually carry on, but it may be best to restart the PC.

The two most common causes of fatal errors are

• Accessing nonexistent memory

• Accessing paged memory at or above DISPATCH_LEVEL IRL

Core Dumps

If NT or W2000 has a bugcheck, you can also get it to produce core dumps in a file on disk, called memory.dmp by default. You have to enable this option in the Control Panel System applet Advanced tab Startup and Recovery section. You can use the WinDbg and DumpExam tools to inspect the dump. However, I found that using WinDbg in this way was not particularly productive.

In NT and W2000, you can include information in the core dump by calling KeInitializeCallbackRecord and KeRegisterBugCheckCallback. In the event of a bugcheck, your callback routine is called to store any state information in the core dump.

Driver Will Not Start

When you update a driver like Wdm1, the Device Manager may state that you need to restart the system before the driver will run. This means that the driver returned an error while loading.

There are two possible causes for such errors. Suppose your driver will start when the system reboots. This means that when it unloads it must be leaving something around that stops it from starting again. A common problem is forgetting to delete your device or the symbolic link to your device.

If your driver will not start when the system reboots, it means that you must have changed your driver so that the DriverEntry routine fails. Trace through the code to work out what is going wrong.

Just to complicate matters, in some circumstances, Windows needs to juggle the revised system resources when a driver is reloaded. In this case, Windows will not even attempt to start your driver if a reboot is necessary to satisfy the resource allocation process.

Hang Ups

A user thread can hang up if you never complete an IRP. The thread will never be able to complete. Have you queued the IRP somewhere and never processed it?

A driver can also hang up if it cannot acquire a resource that it needs. The different resources are described later. If a resource is unavailable, it may mean that just one IRP cannot be processed, or it could mean that all IRP processing grinds to halt.

A 'deadly embrace' occurs if two different pieces of code are trying to access the same resources. For example, code A might hold resource X and want resource Y. If code B holds Y and wants X, then a deadly embrace occurs, hanging the whole system. The simplest solution is to always acquire resources in the same order.

You can also get hang ups if you try to acquire a resource more than once, or if your interrupt service routine never returns. Continual interrupts can seriously degrade the system performance.

Resource Leaks

Resource leaks are less dire, but still important to fix. For example, if you forget to free some memory allocated during each read, the system will eventually run out of memory.

Some resources are more important than others. For example, nonpaged memory should be used conservatively.

Time Dependencies

Possibly the worst type of problem to sort out is related to timing.

At the simplest level, a timing problem can simply mean that you have not filled a buffer quickly enough. For example, if your hardware needs to output data regularly, you may not have provided it with the data. Or did you fill it too quickly? Check that your driver works on different speed computers.

Another possible problem is that your driver may not be reading data quickly enough. For example, an isochronous device, such as a microphone, may generate a regular number of samples per second. Check that you can keep reading data at this speed, even in a stressed system. Alternatively, have a strategy for skipping samples (e.g., a call to your device to drop samples).

Debugging Techniques

Incremental Development

An incremental approach makes debugging far easier. Get each stage working and tested before moving onto the next stage.

An incremental approach still means that you should do proper design work. Think very carefully about how your driver is going to work before starting any development. Having decided upon a design, plan a development path that allows you to implement your design in stages.

Checked Version

Driver development environments are usually set up to produce two different builds: an optimized retail release version, called the free build, and an unoptimised test debug build, called the checked build.

Put any debug or test code so that it appears in the checked build. The simplest way to do this is to check the value of the preprocessor variable DBG, which is set to 1 in the checked build and 0 in free build. Here is an example of how to use this technique.

#if DBG

 DebugPrintInit('Wdm1 checked');

#else

 DebugPrintInit('Wdm1 free');

#endif

I shall soon look at the different types of debug code that you can put in your checked version.

Running under the checked build version of NT or W2000 can also pick up driver errors as the operating system makes more internal checks.

W2000 or W98

One decision you will need to make is whether to work primarily in Windows 2000 or Windows 98. You could set up the development environment in one operating system, and only do test installs in the other. As stated previously, you ought to be testing your driver in both operating systems.

My advice is to use Windows 2000 for development. A few useful tools are only available in W2000 (and NT). And, significantly, W2000 is better at detecting driver errors. For example, W2000 will usually cause a bugcheck when you try to access a bad address, such as writing to a NULL pointer. Windows 98 may not detect this sort of error and your driver will continue, oblivious to the problem. The downside to W2000 is that it takes a bit longer to restart.

It is useful to be able to dual boot a computer with Windows 2000 and Windows 98. If a device fails during system boot, you can use the other operating system to remove the offending driver temporarily. For this technique to work, you need the Windows 2000 system drive to be accessible to Windows 98 (i.e., not formatted in NTFS).

Debugging Tools

Apart from using your brain, there are two basic techniques for debugging. The first is to include 'print' statements of some sort in your driver. The second is to do source code level debugging.

Windows 2000 Events

One way of reporting data is to generate NT and Windows 2000 event messages. For a commercial driver, you should use this facility to report any abnormal events to system administrators. However, it is somewhat complicated to set up a connection between a message number and the string it represents. You can include strings and data values as a part of the event.

The NT and Windows 2000 event viewer does not have the best display in the world, and it does not automatically update itself. Windows 98 has no event viewer.

Generating Windows Management Instrumentation (WMI) events is theoretically a way for a driver to report events. While this has the advantage that you should be able to view the data across a network, it is not a serious proposition for driver development, as it is even harder work to see events.

Tracing Tools

There are better tools available to display driver 'print' trace statements. The next section discusses the DebugPrint software. This lets a driver use formatted print statements in the code. The output appears in a user mode monitor application.

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

0

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

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