/*++ Copyright (c) 1996-2002 Microsoft Corporation Module Name: dlock.c Abstract: Functions for detecting deadlocked resource dll entry point calls. Author: Chittur Subbaraman Revision History: 04-11-2002 Created --*/ #define UNICODE 1 #include "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "resmonp.h" #include #define RESMON_MODULE RESMON_MODULE_DLOCK #define FILETIMES_PER_SEC ((__int64) 10000000) // (1 second)/(100 ns) // // Globals // PRM_DUE_TIME_FREE_LIST_HEAD g_pRmDueTimeFreeListHead = NULL; PRM_DUE_TIME_MONITORED_LIST_HEAD g_pRmDueTimeMonitoredListHead = NULL; CRITICAL_SECTION g_RmDeadlockListLock; BOOL g_RmDeadlockMonitorInitialized = FALSE; PRM_DUE_TIME_ENTRY RmpInsertDeadlockMonitorList( IN LPCWSTR lpszResourceDllName, IN LPCWSTR lpszResourceTypeName, IN LPCWSTR lpszResourceName, OPTIONAL IN LPCWSTR lpszEntryPointName ) /*++ Routine Description: Inserts an entry into the deadlock monitoring list. Arguments: lpszResourceDllName - Resource dll name. lpszResourceTypeName - Resource type name. lpszResourceName - Resource name, OPTIONAL lpszEntryPointName - Entry point name. Return Value: A valid due time entry pointer on success, NULL on failure. Use GetLastError() to get error code. --*/ { PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; DWORD dwStatus = ERROR_SUCCESS; PLIST_ENTRY pListEntry; if ( !g_RmDeadlockMonitorInitialized ) { SetLastError ( ERROR_INVALID_STATE ); return ( NULL ); } // // Get an entry from the free list. // EnterCriticalSection ( &g_RmDeadlockListLock ); if ( IsListEmpty ( &g_pRmDueTimeFreeListHead->leDueTimeEntry ) ) { dwStatus = ERROR_NO_MORE_ITEMS; ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpInsertDeadlockMonitorList: Unable to insert DLL '%1!ws!', Type '%2!ws!', Resource '%3!ws!'," " Entry point '%4!ws!' info into deadlock monitoring list\n", lpszResourceDllName, lpszResourceTypeName, (lpszResourceName == NULL) ? L"Unknown" : lpszResourceName, lpszEntryPointName); LeaveCriticalSection ( &g_RmDeadlockListLock ); goto FnExit; } pListEntry = RemoveHeadList( &g_pRmDueTimeFreeListHead->leDueTimeEntry ); pDueTimeEntry = CONTAINING_RECORD( pListEntry, RM_DUE_TIME_ENTRY, leDueTimeEntry ); LeaveCriticalSection ( &g_RmDeadlockListLock ); // // Populate the entry. No locks needed for that. // StringCchCopy ( pDueTimeEntry->szResourceDllName, RTL_NUMBER_OF ( pDueTimeEntry->szResourceDllName ), lpszResourceDllName ); StringCchCopy ( pDueTimeEntry->szResourceTypeName, RTL_NUMBER_OF ( pDueTimeEntry->szResourceTypeName ), lpszResourceTypeName ); StringCchCopy ( pDueTimeEntry->szEntryPointName, RTL_NUMBER_OF ( pDueTimeEntry->szEntryPointName ), lpszEntryPointName ); if ( ARGUMENT_PRESENT ( lpszResourceName ) ) { StringCchCopy ( pDueTimeEntry->szResourceName, RTL_NUMBER_OF ( pDueTimeEntry->szResourceName ), lpszResourceName ); } else { StringCchCopy ( pDueTimeEntry->szResourceName, RTL_NUMBER_OF ( pDueTimeEntry->szResourceName ), L"None" ); } pDueTimeEntry->dwSignature = RM_DUE_TIME_MONITORED_ENTRY_SIGNATURE; GetSystemTimeAsFileTime( ( FILETIME * ) &pDueTimeEntry->uliDueTime ); pDueTimeEntry->dwThreadId = GetCurrentThreadId (); // // Insert it into the monitoring list // EnterCriticalSection ( &g_RmDeadlockListLock ); pDueTimeEntry->uliDueTime.QuadPart += g_pRmDueTimeMonitoredListHead->ullDeadLockTimeoutSecs * FILETIMES_PER_SEC; InsertTailList ( &g_pRmDueTimeMonitoredListHead->leDueTimeEntry, &pDueTimeEntry->leDueTimeEntry ); LeaveCriticalSection ( &g_RmDeadlockListLock ); FnExit: if ( dwStatus != ERROR_SUCCESS ) { SetLastError ( dwStatus ); } return ( pDueTimeEntry ); } // RmpInsertDeadlockMonitorList VOID RmpRemoveDeadlockMonitorList( IN PRM_DUE_TIME_ENTRY pDueTimeEntry ) /*++ Routine Description: Removes an entry from the deadlock monitoring list. Arguments: pDueTimeEntry - Due time entry to be removed. Return Value: None. --*/ { if ( !g_RmDeadlockMonitorInitialized ) { goto FnExit; } if ( pDueTimeEntry == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpRemoveDeadlockMonitorList: Unable to remove NULL entry from deadlock monitoring list\n"); goto FnExit; } // // Remove from the monitoring list and add it into the free list. // EnterCriticalSection ( &g_RmDeadlockListLock ); RemoveEntryList ( &pDueTimeEntry->leDueTimeEntry ); ZeroMemory ( pDueTimeEntry, sizeof ( RM_DUE_TIME_ENTRY ) ); pDueTimeEntry->dwSignature = RM_DUE_TIME_FREE_ENTRY_SIGNATURE; InsertTailList ( &g_pRmDueTimeFreeListHead->leDueTimeEntry, &pDueTimeEntry->leDueTimeEntry ); LeaveCriticalSection ( &g_RmDeadlockListLock ); FnExit: return; } // RmpRemoveDeadlockMonitorList DWORD RmpDeadlockMonitorInitialize( IN DWORD dwDeadlockDetectionTimeout ) /*++ Routine Description: Initialize the deadlock monitoring system. Arguments: None. Return Value: ERROR_SUCCESS on success, a Win32 error code otherwise. --*/ { DWORD i, dwStatus = ERROR_SUCCESS; HANDLE hDeadlockTimerThread = NULL; PRM_DUE_TIME_ENTRY pDueTimeEntryStart = NULL; // // If the deadlock monitoring susbsystem is already initialized, you are done. // if ( g_RmDeadlockMonitorInitialized ) { return ( ERROR_SUCCESS ); } // // Adjust timeouts so that it is at least equal to the minimum allowed. // dwDeadlockDetectionTimeout = ( dwDeadlockDetectionTimeout < CLUSTER_RESOURCE_DLL_MINIMUM_DEADLOCK_TIMEOUT_SECS ) ? CLUSTER_RESOURCE_DLL_MINIMUM_DEADLOCK_TIMEOUT_SECS : dwDeadlockDetectionTimeout; // // Initialize the critsec. Catch low memory conditions and return error to caller. // try { InitializeCriticalSection( &g_RmDeadlockListLock ); } except ( EXCEPTION_EXECUTE_HANDLER ) { dwStatus = GetExceptionCode(); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeadlockMonitorInitialize: Initialize critsec returned %1!u!\n", dwStatus); return ( dwStatus ); } // // Build the list heads. All are one time only allocs that are never freed. // g_pRmDueTimeMonitoredListHead = LocalAlloc ( LPTR, sizeof ( RM_DUE_TIME_MONITORED_LIST_HEAD ) ); if ( g_pRmDueTimeMonitoredListHead == NULL ) { dwStatus = GetLastError (); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeadlockMonitorInitialize: Unable to alloc memory for monitor list head, status %1!u!\n", dwStatus); goto FnExit; } InitializeListHead ( &g_pRmDueTimeMonitoredListHead->leDueTimeEntry ); g_pRmDueTimeMonitoredListHead->ullDeadLockTimeoutSecs = dwDeadlockDetectionTimeout; g_pRmDueTimeMonitoredListHead->dwSignature = RM_DUE_TIME_MONITORED_LIST_HEAD_SIGNATURE; g_pRmDueTimeFreeListHead = LocalAlloc ( LPTR, sizeof ( RM_DUE_TIME_FREE_LIST_HEAD ) ); if ( g_pRmDueTimeFreeListHead == NULL ) { dwStatus = GetLastError (); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeadlockMonitorInitialize: Unable to alloc memory for free list head, status %1!u!\n", dwStatus); goto FnExit; } InitializeListHead ( &g_pRmDueTimeFreeListHead->leDueTimeEntry ); g_pRmDueTimeFreeListHead->dwSignature = RM_DUE_TIME_FREE_LIST_HEAD_SIGNATURE; // // Build the free list // pDueTimeEntryStart = LocalAlloc ( LPTR, RESMON_MAX_DEADLOCK_MONITOR_ENTRIES * sizeof ( RM_DUE_TIME_ENTRY ) ); if ( pDueTimeEntryStart == NULL ) { dwStatus = GetLastError (); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeadlockMonitorInitialize: Unable to alloc memory for monitor list entries, status %1!u!\n", dwStatus); goto FnExit; } // // Populate the free list // for ( i = 0; i < RESMON_MAX_DEADLOCK_MONITOR_ENTRIES; i++ ) { pDueTimeEntryStart[i].dwSignature = RM_DUE_TIME_FREE_ENTRY_SIGNATURE; InsertTailList ( &g_pRmDueTimeFreeListHead->leDueTimeEntry, &pDueTimeEntryStart[i].leDueTimeEntry ); } // // Create the monitor thread // hDeadlockTimerThread = CreateThread( NULL, // Security attributes 0, // Use default process stack size RmpDeadlockTimerThread, // Function address NULL, // Context 0, // Flags NULL ); // Thread ID -- not interested if ( hDeadlockTimerThread == NULL ) { dwStatus = GetLastError (); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeadlockMonitorInitialize: Unable to create monitor thread, status %1!u!\n", dwStatus); goto FnExit; } // // Try to set the thread priority to highest. Continue even in case of an error. // if ( !SetThreadPriority( hDeadlockTimerThread, THREAD_PRIORITY_HIGHEST ) ) { ClRtlLogPrint(LOG_UNUSUAL, "[RM] RmpDeadlockMonitorInitialize: Unable to set monitor thread priority, status %1!u!\n", GetLastError()); } CloseHandle( hDeadlockTimerThread ); g_RmDeadlockMonitorInitialized = TRUE; ClRtlLogPrint(LOG_NOISE, "[RM] RmpDeadlockMonitorInitialize: Successfully initialized with a timeout of %1!u! secs\n", dwDeadlockDetectionTimeout); FnExit: if ( dwStatus != ERROR_SUCCESS ) { LocalFree ( g_pRmDueTimeMonitoredListHead ); g_pRmDueTimeMonitoredListHead = NULL; LocalFree ( g_pRmDueTimeFreeListHead ); g_pRmDueTimeFreeListHead = NULL; DeleteCriticalSection ( &g_RmDeadlockListLock ); } return ( dwStatus ); } // RmDeadlockMonitorInitialize DWORD RmpDeadlockTimerThread( IN LPVOID pContext ) /*++ Routine Description: Timer thread that monitors for deadlocks in resource dll entry points. Arguments: pContext - Context, Unused. Returns: ERROR_SUCCESS on success. Win32 error code of failure. --*/ { PRM_DUE_TIME_ENTRY pDueTimeEntry; PLIST_ENTRY pListEntry; ULARGE_INTEGER uliCurrentTime; while ( TRUE ) { Sleep ( RESMON_DEADLOCK_TIMER_INTERVAL ); GetSystemTimeAsFileTime ( ( FILETIME * ) &uliCurrentTime ); EnterCriticalSection ( &g_RmDeadlockListLock ); pListEntry = g_pRmDueTimeMonitoredListHead->leDueTimeEntry.Flink; // // Walk the deadlock monitoring list looking for a deadlock. // while ( pListEntry != &g_pRmDueTimeMonitoredListHead->leDueTimeEntry ) { pDueTimeEntry = CONTAINING_RECORD( pListEntry, RM_DUE_TIME_ENTRY, leDueTimeEntry ); pListEntry = pListEntry->Flink; if ( pDueTimeEntry->uliDueTime.QuadPart <= uliCurrentTime.QuadPart ) { RmpDeclareDeadlock ( pDueTimeEntry, uliCurrentTime ); } } // while LeaveCriticalSection ( & g_RmDeadlockListLock ); } // while return ( ERROR_SUCCESS ); }// RmpDeadlockTimerThread VOID RmpDeclareDeadlock( IN PRM_DUE_TIME_ENTRY pDueTimeEntry, IN ULARGE_INTEGER uliCurrentTime ) /*++ Routine Description: Declare a deadlock and exit this process. Arguments: pDueTimeEntry - The entry that contains information of possible deadlock causing resource dll. uliCurrentTime - Current time. Returns: None. --*/ { ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeclareDeadlock: Declaring deadlock and exiting process\n"); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeclareDeadlock: Deadlock candidate info - DLL '%1!ws!', Type '%2!ws!', Resource '%3!ws!', Entry point '%4!ws!', Thread 0x%5!08lx!\n", pDueTimeEntry->szResourceDllName, pDueTimeEntry->szResourceTypeName, pDueTimeEntry->szResourceName, pDueTimeEntry->szEntryPointName, pDueTimeEntry->dwThreadId); ClRtlLogPrint(LOG_CRITICAL, "[RM] RmpDeclareDeadlock: Current time 0x%1!08lx!:%2!08lx!, due time 0x%3!08lx!:%4!08lx!\n", uliCurrentTime.HighPart, uliCurrentTime.LowPart, pDueTimeEntry->uliDueTime.HighPart, pDueTimeEntry->uliDueTime.LowPart); ClusterLogEvent4(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_DEADLOCK_DETECTED, 0, NULL, pDueTimeEntry->szResourceDllName, pDueTimeEntry->szResourceTypeName, pDueTimeEntry->szResourceName, pDueTimeEntry->szEntryPointName); RmpSetMonitorState ( RmonDeadlocked, NULL ); ExitProcess ( 0 ); }// RmpDeclareDeadlock DWORD RmpUpdateDeadlockDetectionParams( IN DWORD dwDeadlockDetectionTimeout ) /*++ Routine Description: Update the parameters of the deadlock monitoring subsystem. Arguments: dwDeadlockDetectionTimeout - The deadlock detection timeout. Return Value: ERROR_SUCCESS on success, a Win32 error code otherwise. --*/ { if ( !g_RmDeadlockMonitorInitialized ) { ClRtlLogPrint(LOG_UNUSUAL, "[RM] RmpUpdateDeadlockDetectionParams: Deadlock monitor not initialized yet\n"); return ( ERROR_INVALID_STATE ); } // // Adjust timeouts so that it is at least equal to the minimum allowed. // dwDeadlockDetectionTimeout = ( dwDeadlockDetectionTimeout < CLUSTER_RESOURCE_DLL_MINIMUM_DEADLOCK_TIMEOUT_SECS ) ? CLUSTER_RESOURCE_DLL_MINIMUM_DEADLOCK_TIMEOUT_SECS : dwDeadlockDetectionTimeout; EnterCriticalSection ( &g_RmDeadlockListLock ); g_pRmDueTimeMonitoredListHead->ullDeadLockTimeoutSecs = dwDeadlockDetectionTimeout; LeaveCriticalSection ( &g_RmDeadlockListLock ); ClRtlLogPrint(LOG_NOISE, "[RM] RmpUpdateDeadlockDetectionParams: Updated monitor with a deadlock timeout of %1!u! secs\n", dwDeadlockDetectionTimeout); return ( ERROR_SUCCESS ); } // RmpUpdateDeadlockDetectionParams