Figure 1   Functions Hooked by DeadlockDetection

Type Function
Thread-related functions CreateThread
ExitThread
SuspendThread
ResumeThread
TerminateThread
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


Figure 2   DeadlockDetection Option Flags

Flag Limits To
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


Figure 3   DeadlockDetection Output Using TextFileDDExt.dll

Thread ID Return Address C/R Return Value Function and Parameters
0x000000E1 [0x00401071] R 0x00000000 InitializeCriticalSection 0x00404B18
0x000000E1 [0x0040107C] R   EnterCriticalSection 0x00404B18
0x000000E1 [0x0040107C] R 0x00000000 EnterCriticalSection 0x00404B18
0x000000E1 [0x00401087] R 0x00000000 LeaveCriticalSection 0x00404B18
0x000000E1 [0x00401092] R 0x00000000 DeleteCriticalSection 0x00404B18
0x000000E1 [0x004010C6] R 0x00000001 InitializeCriticalSectionAndSpinCount 0x00404B30, 400
0x000000E1 [0x004010FA] R 0x00000000 SetCriticalSectionSpinCount 0x00404B30, 2048
0x000000E1 [0x00401129] R   TryEnterCriticalSection 0x00404B30
0x000000E1 [0x00401129] R 0x00000001 TryEnterCriticalSection 0x00404B30
0x000000E1 [0x00401144] R 0x00000028 CreateEventA 0x00000000, 1, 0, 0x0040449C [Manual rest event]
0x000000E1 [0x00401151] R 0x00000001 SetEvent 0x00000028
0x000000E1 [0x0040115B] R 0x00000001 ResetEvent 0x00000028
0x000000E1 [0x0040116D] R 0x0000002C OpenEventA 0x001F0003, 0, 0x004044B0 [Manual rest event]
0x000000E1 [0x00401181] R 0x00000030 CreateEventW 0x00000000, 1, 0, 0x004044C4 [Wide Char event]


Figure 4   DD_Funcs.h


 /*----------------------------------------------------------------------
    John Robbins - Oct '98 Microsoft Systems Journal Bugslayer Column
 ------------------------------------------------------------------------
 The prototypes for all the hook functions and the prolog/epilog code.
 ----------------------------------------------------------------------*/
 
 #ifndef _DD_FUNCS_H
 #define _DD_FUNCS_H
 
 /*//////////////////////////////////////////////////////////////////////
     All of the hook functions are __declspec(naked) functions, therefore
     I must provide the prolog and epilog.  The reasons for providing custom
     prolog and epilog are:
 1.  Functions written in C have no control over what registers are used
     and when the compiler saves the original registers off.  This means
     that It is nearly impossible to get the return address.  For the
     DeadlockDetection project, the return address is very important.
 2.  I also wanted to hand the parameters to the extension DLL processing
     function without having to do massive amounts of data copying on
     each function call.
 3.  Since almost all of the hook functions do the same things, I set up
     the common variables needed in all functions.
 4.  Hook functions cannot change any of the returned values, including
     the value from GetLastError.  By doing my own prolog and epilog, I
     can make it much easier to handle returning the correct value. Also,
     I need to restore the register values that normally would be there
     after the real function call.
 
 Your basic hook function looks like the following:
 
 HANDLE NAKEDDEF DD_OpenEventA ( DWORD  dwDesiredAccess ,
                                 BOOL   bInheritHandle  ,
                                 LPCSTR lpName           )
 {
     // This must be declared at the top of the function.  If the
     //  function has any local variables, declare them first.
     HOOKFN_PROLOG ( ) ;
 
     // Determine if this function class is being logged.
     if ( TRUE == DoLogging ( DDOPT_EVENT ) )
     {
         // Call this macro to fill the DDEVENTINFO, stEvtInfo, that is
         //  part of each hook function.  It comes from the HOOKFN_PROLOG
         //  macro.
         FILL_EVENTINFO ( eOpenEventA ) ;
 
         // This macro *MUST* be called before calling the real function
         //  or ESI and EDI will not be the same that the user set.
         REAL_FUNC_PRE_CALL ( ) ;
 
         // The real function call.  The return value handling is part of
         //  the REAL_FUNC_POST_CALL processing so there is no need to
         //  manually set it.
         OpenEventA ( dwDesiredAccess ,
                      bInheritHandle  ,
                      lpName           ) ;
 
         // This macro *MUST* be called AFTER calling the real function.
         //  This saves off the register values and the last error value.
         REAL_FUNC_POST_CALL ( ) ;
 
         // Call this function to log the event.
         ProcessEvent ( &stEvtInfo ) ;
     }
     else
     {
         // See the comments above.  This is just the case where the
         //  function is not being logged.
         REAL_FUNC_PRE_CALL ( ) ;
         OpenEventA ( dwDesiredAccess ,
                      bInheritHandle  ,
                      lpName           ) ;
         REAL_FUNC_POST_CALL ( ) ;
     }
 
     // This must be the last thing in the function.  The parameter is
     //  a count of the number of functions so the stack gets cleaned up
     //  correctly.  This takes care of setting all the registers to the
     //  same values that the real function returned.
     HOOKFN_EPILOG ( 3 ) ;
 }
 
 //////////////////////////////////////////////////////////////////////*/
 /*//////////////////////////////////////////////////////////////////////
     The register state structure.  I use this to ensure that ALL
 registers are returned exactly as they came from the real function.
 Note that EBP and ESP are handled as part of the prolog.
 //////////////////////////////////////////////////////////////////////*/
 typedef struct tag_REGSTATE
 {
     DWORD   dwEAX ;
     DWORD   dwEBX ;
     DWORD   dwECX ;
     DWORD   dwEDX ;
     DWORD   dwEDI ;
     DWORD   dwESI ;
     DWORD   dwEFL ;
 } REGSTATE , * PREGSTATE ;
 
 /*//////////////////////////////////////////////////////////////////////
     The common prolog code for all DD_* functions.
 //////////////////////////////////////////////////////////////////////*/
 #define HOOKFN_PROLOG()                                                \
 DDEVENTINFO stEvtInfo   ;       /* The event info for the function.  */\
 DWORD       dwLastError ;       /* The last error value.             */\
 REGSTATE    stRegState  ;       /* The register state to ensure I    */\
                                 /*  restore everything correctly.    */\
 {                                                                      \
 __asm PUSH  EBP                 /* Always save EBP explicitly        */\
 __asm MOV   EBP , ESP           /* Move the stack                    */\
 __asm MOV   EAX , ESP           /* Get the stack ptr to calculate the*/\
                                 /*  return address and parameters.   */\
 __asm SUB   ESP , __LOCAL_SIZE  /* Save off space for the local vars.*/\
 __asm ADD   EAX , 04h + 04h     /* Account for PUSH EBP, and the     */\
                                 /*  return address.                  */\
                                 /* Save start of params on the stack.*/\
 __asm MOV   [stEvtInfo.dwParams] , EAX                                 \
 __asm SUB   EAX , 04h           /* Get back to return addr.          */\
 __asm MOV   EAX , [EAX]         /* EAX now holds return address.     */\
                                 /* Save off the return  address.     */\
 __asm MOV   [stEvtInfo.dwAddr] , EAX                                   \
 __asm MOV   dwLastError , 0     /* Initialize dwLastError.           */\
                                 /* Initialize the event info.        */\
 __asm MOV   [stEvtInfo.eFunc] , eUNINITIALIZEDFE                       \
 __asm MOV   [stRegState.dwEDI] , EDI /* Save the two regs that need  */\
 __asm MOV   [stRegState.dwESI] , ESI /*  to be saved across func     */\
                                      /*  calls.                      */\
 }
 
 /*//////////////////////////////////////////////////////////////////////
     The common epilog code for all DD_* functions.  iNumParams is the
 number of parameters to the function.  This is so the stack can be
 restored for stdcall functions.
 //////////////////////////////////////////////////////////////////////*/
 #define HOOKFN_EPILOG(iNumParams)   \
 { \
 SetLastError ( dwLastError ) ;      /* Set the real fn's last err val*/\
 __asm ADD   ESP , __LOCAL_SIZE      /* Add back on the local vars.   */\
 __asm MOV   EBX , [stRegState.dwEBX] /* Restore all the regs so      */\
 __asm MOV   ECX , [stRegState.dwECX] /*  this call looks identical   */\
 __asm MOV   EDX , [stRegState.dwEDX] /*  to what the intercepted     */\
 __asm MOV   EDI , [stRegState.dwEDI] /*  function would look like.   */\
 __asm MOV   ESI , [stRegState.dwESI]                                   \
 __asm MOV   EAX , [stRegState.dwEFL]                                   \
 __asm SAHF                                                             \
 __asm MOV   EAX , [stRegState.dwEAX]                                   \
 __asm MOV   ESP , EBP             /* Put ESP back.                   */\
 __asm POP   EBP                   /* Restore the saved EBP.          */\
 __asm RET   iNumParams * 4        /* stdcall restore of the stack.   */\
 }
 
 /*//////////////////////////////////////////////////////////////////////
     This macro needs to be place IMMEDIATELY *BEFORE* ANY call to the
 real function the hook function is handling.  This takes care of setting
 EDI and ESI to the original values passed in by the user.
 //////////////////////////////////////////////////////////////////////*/
 #define REAL_FUNC_PRE_CALL()                                           \
 {                                                                      \
 __asm MOV   EDI , [stRegState.dwEDI]    /* Restore real EDI.         */\
 __asm MOV   ESI , [stRegState.dwESI]    /* Restore real ESI.         */\
 }
 
 /*//////////////////////////////////////////////////////////////////////
     This macro needs to be place IMMEDIATELY *AFTER* ANY call to the
 real function the hook function is handling.  This macro saves off the
 register values on the return so that the function epilog can return
 the exact same values as the real function.
 //////////////////////////////////////////////////////////////////////*/
 #define REAL_FUNC_POST_CALL()                                          \
 {                                                                      \
 __asm MOV   [stRegState.dwEAX] , EAX    /* Save EAX value.           */\
 __asm MOV   [stRegState.dwEBX] , EBX    /* Save EBX value.           */\
 __asm MOV   [stRegState.dwECX] , ECX    /* Save ECX value.           */\
 __asm MOV   [stRegState.dwEDX] , EDX    /* Save EDX value.           */\
 __asm MOV   [stRegState.dwEDI] , EDI    /* Save EDI value.           */\
 __asm MOV   [stRegState.dwESI] , ESI    /* Save ESI value.           */\
 __asm XOR   EAX , EAX                   /* Zero out EAX.             */\
 __asm LAHF                              /* Save flags into AH.       */\
 __asm MOV   [stRegState.dwEFL] , EAX    /* Save flags value.         */\
 }                                                                      \
 dwLastError = GetLastError ( ) ;        /* Save the last error value.*/\
 {                                                                      \
 __asm MOV   EAX , [stRegState.dwEAX]    /* Restore EAX to orig value.*/\
                                         /* Set return value for info.*/\
 __asm MOV   [stEvtInfo.dwRetValue] , EAX                               \
 }
 
 /*//////////////////////////////////////////////////////////////////////
     A convenient macro to fills out the event information structure.
 //////////////////////////////////////////////////////////////////////*/
 #define FILL_EVENTINFO(eFn)                         \
     stEvtInfo.eFunc      = eFn       ;              \
     stEvtInfo.ePrePost   = ePostCall ;              \
     stEvtInfo.dwThreadId = GetCurrentThreadId ( )
 
 /*//////////////////////////////////////////////////////////////////////
     The declaration for all DD_* definitions.
 //////////////////////////////////////////////////////////////////////*/
 #define NAKEDDEF __declspec(naked)
 
 /*//////////////////////////////////////////////////////////////////////
 BIG NOTE BIG NOTE BIG NOTE BIG NOTE
 All of the following prototypes look like cdecl functions.  They are
 not, they are all stdcall!  The custom prolog and epilog ensure that
 everything gets straightened out!
 //////////////////////////////////////////////////////////////////////*/
 
 ////////////////////////////////////////////////////////////////////////
 // The mandatory functions that have to be intercepted to make the
 //  system work.
 HMODULE DD_LoadLibraryA ( LPCSTR lpLibFileName    ) ;
 HMODULE DD_LoadLibraryW ( LPCWSTR lpLibFileName   ) ;
 HMODULE DD_LoadLibraryExA ( LPCSTR lpLibFileName  ,
                             HANDLE hFile          ,
                             DWORD  dwFlags        ) ;
 HMODULE DD_LoadLibraryExW ( LPCWSTR lpLibFileName ,
                             HANDLE  hFile         ,
                             DWORD   dwFlags       ) ;
 
 VOID DD_ExitProcess ( UINT uExitCode ) ;
 
 FARPROC DD_GetProcAddress ( HMODULE hModule , LPCSTR lpProcName ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // The thread specific functions.
 HANDLE DD_CreateThread (LPSECURITY_ATTRIBUTES  lpThreadAttributes   ,
                         DWORD                  dwStackSize          ,
                         LPTHREAD_START_ROUTINE lpStartAddress       ,
                         LPVOID                 lpParameter          ,
                         DWORD                  dwCreationFlags      ,
                         LPDWORD                lpThreadId            ) ;
 VOID DD_ExitThread ( DWORD dwExitCode ) ;
 DWORD DD_SuspendThread ( HANDLE hThread ) ;
 DWORD DD_ResumeThread ( HANDLE hThread ) ;
 BOOL DD_TerminateThread ( HANDLE hThread , DWORD dwExitCode ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Waiting and special functions.
 DWORD DD_WaitForSingleObject ( HANDLE hHandle        ,
                                DWORD  dwMilliseconds  ) ;
 DWORD DD_WaitForSingleObjectEx ( HANDLE hHandle        ,
                                  DWORD  dwMilliseconds ,
                                  BOOL   bAlertable      ) ;
 DWORD DD_WaitForMultipleObjects( DWORD          nCount          ,
                                  CONST HANDLE * lpHandles       ,
                                  BOOL           bWaitAll        ,
                                  DWORD          dwMilliseconds   ) ;
 DWORD DD_WaitForMultipleObjectsEx( DWORD          nCount         ,
                                    CONST HANDLE * lpHandles      ,
                                    BOOL           bWaitAll       ,
                                    DWORD          dwMilliseconds ,
                                    BOOL           bAlertable      ) ;
 DWORD DD_MsgWaitForMultipleObjects ( DWORD    nCount        ,
                                      LPHANDLE pHandles      ,
                                      BOOL     fWaitAll      ,
                                      DWORD    dwMilliseconds,
                                      DWORD    dwWakeMask     ) ;
 DWORD DD_MsgWaitForMultipleObjectsEx ( DWORD    nCount          ,
                                        LPHANDLE pHandles        ,
                                        DWORD    dwMilliseconds  ,
                                        DWORD    dwWakeMask      ,
                                        DWORD    dwFlags          ) ;
 DWORD DD_SignalObjectAndWait ( HANDLE hObjectToSignal ,
                                HANDLE hObjectToWaitOn ,
                                DWORD  dwMilliseconds  ,
                                BOOL   bAlertable       ) ;
 BOOL DD_CloseHandle ( HANDLE hObject ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Critical Section functions.
 VOID DD_InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
 BOOL DD_InitializeCriticalSectionAndSpinCount (
                                    LPCRITICAL_SECTION lpCriticalSection,
                                    DWORD              dwSpinCount     );
 VOID DD_DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection ) ;
 VOID DD_EnterCriticalSection ( LPCRITICAL_SECTION lpCriticalSection ) ;
 VOID DD_LeaveCriticalSection ( LPCRITICAL_SECTION lpCriticalSection ) ;
 DWORD DD_SetCriticalSectionSpinCount (
                                    LPCRITICAL_SECTION lpCriticalSection,
                                    DWORD              dwSpinCount     );
 BOOL DD_TryEnterCriticalSection ( LPCRITICAL_SECTION lpCriticalSection);
 
 ////////////////////////////////////////////////////////////////////////
 // Mutex functions.
 HANDLE DD_CreateMutexA ( LPSECURITY_ATTRIBUTES lpMutexAttributes ,
                          BOOL                  bInitialOwner     ,
                          LPCSTR                lpName             ) ;
 HANDLE DD_CreateMutexW ( LPSECURITY_ATTRIBUTES lpMutexAttributes ,
                          BOOL                  bInitialOwner     ,
                          LPCWSTR               lpName             ) ;
 HANDLE DD_OpenMutexA ( DWORD  dwDesiredAccess ,
                        BOOL   bInheritHandle  ,
                        LPCSTR lpName           ) ;
 HANDLE DD_OpenMutexW ( DWORD   dwDesiredAccess ,
                        BOOL    bInheritHandle  ,
                        LPCWSTR lpName           ) ;
 BOOL DD_ReleaseMutex ( HANDLE hMutex ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Semaphore functions.
 HANDLE
     DD_CreateSemaphoreA ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes ,
                           LONG                  lInitialCount         ,
                           LONG                  lMaximumCount         ,
                           LPCSTR                lpName                );
 HANDLE
     DD_CreateSemaphoreW ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes ,
                           LONG                  lInitialCount         ,
                           LONG                  lMaximumCount         ,
                           LPCWSTR               lpName                );
 HANDLE DD_OpenSemaphoreA ( DWORD  dwDesiredAccess ,
                            BOOL   bInheritHandle  ,
                            LPCSTR lpName           ) ;
 HANDLE DD_OpenSemaphoreW ( DWORD   dwDesiredAccess ,
                            BOOL    bInheritHandle  ,
                            LPCWSTR lpName           ) ;
 BOOL DD_ReleaseSemaphore ( HANDLE hSemaphore      ,
                            LONG   lReleaseCount   ,
                            LPLONG lpPreviousCount  ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Event functions.
 HANDLE DD_CreateEventA ( LPSECURITY_ATTRIBUTES lpEventAttributes ,
                          BOOL                  bManualReset      ,
                          BOOL                  bInitialState     ,
                          LPCSTR                lpName             ) ;
 HANDLE DD_CreateEventW ( LPSECURITY_ATTRIBUTES lpEventAttributes ,
                          BOOL                  bManualReset      ,
                          BOOL                  bInitialState     ,
                          LPCWSTR               lpName             ) ;
 HANDLE DD_OpenEventA ( DWORD  dwDesiredAccess ,
                        BOOL   bInheritHandle  ,
                        LPCSTR lpName           ) ;
 HANDLE DD_OpenEventW ( DWORD   dwDesiredAccess ,
                        BOOL    bInheritHandle  ,
                        LPCWSTR lpName           ) ;
 BOOL DD_PulseEvent ( HANDLE hEvent ) ;
 BOOL DD_ResetEvent ( HANDLE hEvent ) ;
 BOOL DD_SetEvent ( HANDLE hEvent ) ;
 
 #endif  // _DD_FUNCS_H