Using DeadlockDetection


The first step in using DeadlockDetection is to put DEADLOCKDETECTION.DLL, its initialization file, and the appropriate DeadDetExt DLL in the same place. The initialization file is a simple INI file that, at a minimum, must specify the name of the DeadDetExt file to load. The following sample is a DEADLOCKDETECTION.INI file that loads the supplied TEXTFILEDDEXT.DLL:

[Initialization] ; The only mandatory value, the name of the DeadDetExt file that will ; handle the output. ExtDll = "TextFileDDExt.dll"     ; If this option is 1, DeadlockDetection will initialize in it's DllMain ; so that logging can start at the earliest possible time. StartInDllMain = 0     ; If StartInDllMain is 1, this key specifies the initial options for ; DeadlockDetection. This is the VALUE of the DDOPT_* flags. ; InitialOpts = 0     ; The list of modules to ignore when hooking synchronization functions. ; IMM32.DLL is the Input Method Editor DLL that Windows XP puts in all ; processes. ; Keep the list in consecutive number order starting at 1. [IgnoreModules] Ignore1=IMM32.DLL

As you can see from some of the INI settings, DeadlockDetection can initialize just by having LoadLibrary called on it. A good proactive debugging idea would be to create a backdoor in your application initialization that calls LoadLibrary on the specified DLL name if your application sees a special registry key or an environment variable. This alternate approach to initializing your application would mean that you wouldn't need conditional compilation and you'd have a means of getting DLLs into your address space cleanly. Of course, all this assumes that the DLLs you're loading in this way are smart enough to initialize themselves completely in their DllMains and don't require you to call any other exported functions in the DLL.

Having your code set the DeadlockDetection initialization options, rather than using an INI file, means that you'll need to include DEADLOCKDETECTION.H in your application and have your application link against DEADLOCKDETECTION.LIB. If you want to initialize DeadlockDetection yourself, all you need to do is call OpenDeadlockDetection when appropriate. OpenDeadlockDetection takes a single parameter, the initial reporting options. Table 15-2 lists all the DDOPT_* flags. You'll want to call OpenDeadlockDetection before your application starts creating threads so that you can record all the key information about your synchronization objects.

At any point, you can change the reporting options by calling SetDeadlockDetectionOptions. This function takes the same OR'd set of flags as the OpenDeadlockDetection function. To see what the current options are, call GetDeadlockDetectionOptions. You can change the current options as many times as you like during your program's execution. If you want to suspend and resume logging, call the SuspendDeadlockDetection and ResumeDeadlockDetection functions.

Table 15-2: DeadlockDetection Reporting Options

Flag

Limits Logging to

DDOPT_WAIT

Wait-related functions

DDOPT_THREADS

Thread-related functions

DDOPT_CRITSEC

Critical-section functions

DDOPT_MUTEX

Mutex functions

DDOPT_SEMAPHORE

Semaphore functions

DDOPT_EVENT

Event functions

DDOPT_ALL

All hooked functions

Along with the DeadlockDetection source code that comes with this book's sample files, I've included a DeadDetExt DLL that I wrote, TEXTFILEDDEXT.DLL. This relatively simple extension writes all the information to a text file. When you run DeadlockDetection with TEXTFILEDDEXT.DLL, the extension creates a text file in the same directory as the executable program. The text file will use the name of the executable with a .DD extension. For example, if you run DDSIMPTEST.EXE, the resulting file will be DDSIMPTEST.DD. Listing 15-1 shows some sample output from TEXTFILEDDEXT.DLL.

Listing 15-1: DeadlockDetection output using TESTFILEDDEXT.DLL

start example
TID        Ret Addr     C/R Ret Value  Function & Params 0x00000DF8 [0x004011B2] (R) 0x00000000 InitializeCriticalSection 0x00404150 0x00000DF8 [0x004011CC] (R) 0x000007C0 CreateEventA 0x00000000, 1, 0,                                                 0x004040F0 [The Event Name] 0x00000DF8 [0x004011EF] (R) 0x000007BC CreateThread 0x00000000, 0x00000000,                                                      0x00401000, 0x00000000,                                                      0x00000000, 0x0012FF5C 0x00000DF8 [0x00401212] (R) 0x000007B8 CreateThread 0x00000000, 0x00000000,                                                      0x004010BC, 0x00000000,                                                      0x00000000, 0x0012FF5C 0x00000DF8 [0x00401229] (C)            EnterCriticalSection 0x00404150 0x000000A8 [0x00401030] (C)            EnterCriticalSection 0x00404150 0x00000F04 [0x004010F3] (R) 0x000007B0 OpenEventA 0x001F0003, 0, 0x004040BC                                                    [The Event Name] 0x00000DF8 [0x00401229] (R) 0x00000000 EnterCriticalSection 0x00404150 0x00000DF8 [0x0040123E] (C)            WaitForSingleObject 0x000007C0,                                                             INFINITE 0x00000F04 [0x00401121] (C)            EnterCriticalSection 0x00404150 
end example

Notice that the function and parameter information is wrapped in Listing 15-1 so that it displays on the page. The output shows the information in the following order:

  1. The ID of the executing thread.

  2. The return address to indicate which of your functions called the synchronization function. Using the CrashFinder utility from Chapter 12, you can look up the return addresses and discover how you got into the deadlock situations.

  3. The call or return indicator to help identify actions that occur before and after specific functions.

  4. The return value of the function if your program is reporting function returns.

  5. The synchronization function name.

  6. The parameter list for the synchronization function. Items in brackets are the human-readable data. I concentrated on showing string values, but it would be trivial to add more data, such as individual flags.

When you run your application and it deadlocks, kill the process and view the output file to see the last synchronization item called. TEXTFILEDDEXT.DLL keeps the file up to date by flushing the file buffers each time you call a WaitFor* function, EnterCriticalSection, or TryEnterCriticalSection.

A word of caution: if you turn on full logging of all functions, you can generate some extremely large files in no time. Using the MTGDI Visual C++ sample application, I generated an 11-MB text file in a minute or two by creating a couple of threads.




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