High-Level Design Issues with DeadlockDetection


I had to figure out how to implement DeadlockDetection given the preceding requirements. I first needed to determine which functions I needed to monitor so that I could report the complete deadlock trace. Table 15-1 lists all the functions, grouped by type, that I decided I needed to monitor to implement DeadlockDetection.

Table 15-1: Functions That DeadlockDetection Monitors

Type

Function

Thread-related functions

CreateThread, ExitThread, SuspendThread, ResumeThread, TerminateThread, _beginthreadex, _beginthread, _exitthreadex, _exitthread, FreeLibraryAndExitThread

Critical-section functions

InitializeCriticalSection, InitializeCriticalSectionAndSpinCount, DeleteCriticalSection, EnterCriticalSection, LeaveCriticalSection, SetCriticalSectionSpinCount, TryEnterCriticalSection

Mutex functions

CreateMutexA, CreateMutexW, OpenMutexA, OpenMutexW, ReleaseMutex

Semaphore functions

CreateSemaphoreA, CreateSemaphoreW, OpenSemaphoreA, OpenSemaphoreW, ReleaseSemaphore

Event functions

CreateEventA, CreateEventW, OpenEventA, OpenEventW, PulseEvent, ResetEvent, SetEvent

Blocking functions

WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx, SignalObjectAndWait

Special functions

CloseHandle, ExitProcess, GetProcAddress, LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW, FreeLibrary

After pondering the problem of how to collect the information I needed to satisfy the first four requirements, I realized that I was going to have to intercept (or hook) the functions in Table 15-1 to record the acquisition and release of synchronization objects. Hooking functions isn't a trivial task, and I'll explain how I implemented the code in the section "Hooking Imported Functions" later in this chapter. The one constraint that hooking imported functions imposed on DeadlockDetection is that the code for DeadlockDetection must reside in a DLL because the hooks apply only to the address space in which they're created. This constraint means that the user must load the DeadlockDetection DLL into her address space, a requirement that isn't too harsh given the benefits. As a DLL, the utility would integrate easily with a user program, requirement 7 specified in the list in the preceding section.

As you're looking over Table 15-1, you might have noticed that I didn't include certain potentially blocking message functions such as SendMessage, PostMessage, and WaitMessage. Originally, I'd intended to include these functions, but when I ran Charles Petzold's canonical "Hello World!" GUI program under DeadlockDetection, DeadlockDetection reported so many calls that the program essentially fell over. In the effort of keeping DeadlockDetection as lightweight as possible, I had to forego them.

Gathering the information to satisfy requirements 1 through 4 follows as a direct consequence of choosing the in-process function hooking approach. This approach means that each of the multithreading and synchronization functions will be calling directly into the DeadlockDetection code with all the information I need.

Making DeadlockDetection as lightweight as possible (requirement 5) was a tough condition to satisfy. I tried to code efficiently, but efficient code went only so far toward fulfilling the goal I set out to achieve. Figuring that you know best what types of synchronization objects you're using in your program, I grouped the object types so that you can specify just those functions you want to hook. For example, if you're concerned only about deadlock problems on mutexes, you can process only mutex functions.

To make DeadlockDetection even more useful, you can specify, on the fly, which sets of synchronization object functions you want to watch. You can also turn DeadlockDetection on and off as many times as needed. You might even want to give your application an accelerator key or a special menu option that toggles the entire DeadlockDetection system. Allowing this narrow scope meets requirement 5 and helps with requirement 7.

The only requirement left is number 6: making the output processing as extensible as possible. I wanted to give you the ability to slice and dice the output, rather than force you to make do with some arbitrary, hard-coded format. By keeping the main hooking and processing separate from the output code, I can achieve greater code reuse because the only part being changed, the output side, is much easier to develop than the core side. I named the output portions DeadlockDetection extensions, or DeadDetExt for short. A DeadDetExt is simply a DLL that has several exported functions that DeadlockDetection looks for and calls when appropriate.

Now it's time to explain how to use DeadlockDetection. If you understand the requirements I set out and understand how to use this utility, you'll find it easier to see how I implemented it.




Debugging Applications for Microsoft. NET and Microsoft Windows
Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
ISBN: 0735615365
EAN: 2147483647
Year: 2003
Pages: 177
Authors: John Robbins

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net