/*---------------------------------------------------------------------- ASYNCTRC.C Implementation of the async tracing library Copyright (C) 1994 Microsoft Corporation All rights reserved. Authors: gordm Gord Mangione History: 01/30/95 gordm Created. ----------------------------------------------------------------------*/ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #define _DBGTRACE_DLL_IMPLEMENTATION #include #include #include // // #define TRACE_ENABLED // #include "traceint.h" #include "randint.h" // // Per Process global variables // PENDQ PendQ; BOOL fInitialized; HANDLE hShutdownEvent; DWORD dwInitializations = 0; CHAR mszModules[MODULES_BUFFER_SIZE] = {0}; // critical section to protect against the race condition // where one service is starting and a second service is // terminating. CRITICAL_SECTION g_csInitialize; // // critical section to protect reentracy on Write routine // Also used by the signal thread to ensure that no threads // are using hFile as it dynamically opens and closes trace file. // During Async mode the background thread will be able to grab // this critSec each time without waiting unless we're in the // process of shutting down. // CRITICAL_SECTION critSecWrite; // // critical section to protect reentracy on Flush routine // CRITICAL_SECTION critSecFlush; // // exported trace flag used by trace macros to determine if the trace // statement should be executed // DbgTraceDLL DWORD __dwEnabledTraces; DWORD dwMaxFileSize; DWORD dwNumTraces; DWORD dwTraceOutputType; DWORD dwAsyncTraceFlag; int nAsyncThreadPriority; DWORD dwIncrementSize; DWORD dwTlsIndex = 0xFFFFFFFF; // // pointer to the previous top level exception handler // LPTOP_LEVEL_EXCEPTION_FILTER lpfnPreviousFilter = NULL; // // Internal Function to debugger tracing if DEBUG is defined. // see traceint.h for the INT_TRACE macro which can be // inserted at the appropriate point and has the same // parameters as printf. // #ifdef TRACE_ENABLED void CDECL InternalTrace( const char *s, ... ) { char sz[256]; va_list marker; va_start( marker, s ); wvsprintf( sz, s, marker ); OutputDebugString( sz ); va_end( marker ); } #endif //+--------------------------------------------------------------- // // Function: TopLevelExceptionFilter // // Synopsis: exception handler to flush the PendQ before hitting // the debugger // // Arguments: see Win32 help file // // Returns: always returns EXCEPTION_CONTINUE_SEARCH // //---------------------------------------------------------------- LONG WINAPI TopLevelExceptionFilter( EXCEPTION_POINTERS *lpExceptionInfo ) { DWORD dwLastError = GetLastError(); // // flush the background queue; ignore the ret code // FlushAsyncTrace(); // // restore the overwritten last error code // SetLastError( dwLastError ); // // chain the ret code if there is a previous exception handler // else continue the search // return lpfnPreviousFilter != NULL ? (*lpfnPreviousFilter)( lpExceptionInfo ) : EXCEPTION_CONTINUE_SEARCH ; } //+--------------------------------------------------------------- // // Function: SetTraceBufferInfo // // Synopsis: used to set the non-sprintf trace variables // // Arguments: LPTRACEBUF: target buffer // int: line number of the exception // LPSTR: source file of the exception // LPSTR: function name of the exception // DWORD: type of trace // // Returns: void // //---------------------------------------------------------------- __inline void SetTraceBufferInfo( LPTRACEBUF lpBuf, int iLine, LPSTR pszFile, LPSTR pszFunction, DWORD dwTraceMask, DWORD dwError ) { LPSTR psz; WORD wVariableOffset = 0; PFIXEDTR pFixed = &lpBuf->Fixed; lpBuf->dwLastError = dwError; pFixed->wSignature = 0xCAFE; pFixed->wLength = sizeof(FIXEDTRACE); pFixed->wLine = LOWORD( iLine ); pFixed->dwTraceMask = dwTraceMask; pFixed->dwThreadId = GetCurrentThreadId(); pFixed->dwProcessId = PendQ.dwProcessId; GetLocalTime( &pFixed->TraceTime ); if ( pszFile ) { if ( (psz = strrchr( pszFile, '\\' )) != NULL ) { psz++; // fully qualified path name - strip path } else { psz = pszFile; // simple file name } lstrcpyn( lpBuf->Buffer, psz, MAX_FILENAME_SIZE ); pFixed->wFileNameOffset = sizeof(FIXEDTRACE) + wVariableOffset; wVariableOffset = lstrlen( psz ) + 1; } else { pFixed->wFileNameOffset = 0; } if ( pszFunction != NULL ) { lstrcpyn( lpBuf->Buffer + wVariableOffset, pszFunction, MAX_FUNCTNAME_SIZE ); pFixed->wFunctNameOffset = sizeof(FIXEDTRACE) + wVariableOffset; wVariableOffset += lstrlen( pszFunction ) + 1; } else { pFixed->wFunctNameOffset = 0; } // // set the current offset into the variable buffer // pFixed->wVariableLength = wVariableOffset; } //+--------------------------------------------------------------- // // Function: CommitTraceBuffer // // Synopsis: deal with the buffer; either sync write or async queue // // Arguments: LPTRACEBUF lpBuf: the buffer to commit // // Returns: void // //---------------------------------------------------------------- __inline void CommitTraceBuffer( LPTRACEBUF lpBuf ) { DWORD dwError = lpBuf->dwLastError; if ( dwAsyncTraceFlag == 0 ) { WriteTraceBuffer( lpBuf ); FreeTraceBuffer( lpBuf ); } else { QueueAsyncTraceBuffer( lpBuf ); } // // restore last error before initial Trace call // SetLastError( dwError ); } //+--------------------------------------------------------------- // // Function: DllEntryPoint // // Synopsis: only relevence is allocating thread local storage var // // Arguments: see Win32 SDK // // Returns: see Win32 SDK // //---------------------------------------------------------------- DbgTraceDLL BOOL WINAPI DllEntryPoint( HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved ) { // // InitAsyncTrace and TermAsyncTrace cannot be called from this entrypoint // because they create and interact with background threads // See CreateThread in Win32 Help file for more info // switch( dwReason ) { case DLL_PROCESS_ATTACH: InitializeCriticalSection(&g_csInitialize); return TRUE; // return InitAsyncTrace(); case DLL_THREAD_ATTACH: if(fInitialized) // TlsAlloc hasn't been called till fInitialized = TRUE { TlsSetValue( dwTlsIndex, (LPVOID)NULL ); TlsSetValue( dwRandFailTlsIndex, (LPVOID)NULL ); } break; case DLL_PROCESS_DETACH: if (lpReserved == NULL) DeleteCriticalSection(&g_csInitialize); // TermAsyncTrace(); return FALSE; } return TRUE; } //+--------------------------------------------------------------- // // Function: SetAsyncTraceParams // // Synopsis: exported function to setup trace buffer with // required fields // // This is the first call for a trace statement. // Second call is different for strings or binary // // Arguments: LPSTR: source file of the exception // int: line number of the exception // LPSTR: function name of the exception // DWORD: type of trace // // Returns: returns a BOOL 1 if successful; 0 on failure // // Note: Feb 24, 1999. This function obsoleted by SetAsyncTraceParamsEx. // This function is retained purely for code that linked with // this dll, but won't be rebuilt to call SetAsyncTraceParamsEx. // //---------------------------------------------------------------- DbgTraceDLL int WINAPI SetAsyncTraceParams( LPSTR pszFile, int iLine, LPSTR pszFunction, DWORD dwTraceMask ) { LPTRACEBUF lpBuf; DWORD dwError = GetLastError(); if ( fInitialized == FALSE ) { return 0; } if ( ShouldLogModule("ALL") == FALSE ) { return 0; } if ( lpBuf = GetTraceBuffer() ) { SetTraceBufferInfo( lpBuf, iLine, pszFile, pszFunction, dwTraceMask, dwError ); TlsSetValue( dwTlsIndex, (LPVOID)lpBuf ); return 1; } else return 0; } //+--------------------------------------------------------------- // // Function: SetAsyncTraceParamsEx // // Synopsis: exported function to setup trace buffer with // required fields. // // This is the first call for a trace statement. // Second call is different for strings or binary // // Arguments: LPSTR: module name // LPSTR: source file of the exception // int: line number of the exception // LPSTR: function name of the exception // DWORD: type of trace // // Returns: returns a BOOL 1 if successful; 0 on failure // //---------------------------------------------------------------- DbgTraceDLL int WINAPI SetAsyncTraceParamsEx( LPSTR pszModule, LPSTR pszFile, int iLine, LPSTR pszFunction, DWORD dwTraceMask ) { LPTRACEBUF lpBuf; DWORD dwError = GetLastError(); if ( fInitialized == FALSE ) { return 0; } if (ShouldLogModule(pszModule) == FALSE) { return 0; } if ( lpBuf = GetTraceBuffer() ) { SetTraceBufferInfo( lpBuf, iLine, pszFile, pszFunction, dwTraceMask, dwError ); TlsSetValue( dwTlsIndex, (LPVOID)lpBuf ); return 1; } else return 0; } //+--------------------------------------------------------------- // // Function: AsyncStringTrace // // Synopsis: exported function to finish setting up trace buffer // with optional fields for sprintf style traces // // Arguments: LPARAM: 32bit trace param used app level filtering // LPCSTR: format string // va_list: marker for vsprintf functions // // Returns: returns length of the trace statement // //---------------------------------------------------------------- DbgTraceDLL int WINAPI AsyncStringTrace(LPARAM lParam, LPCSTR szFormat, va_list marker ) { LPTRACEBUF lpBuf; PFIXEDTR pFixed; int iLength; int iMaxLength; if ( fInitialized == FALSE ) { return 0; } if ( (lpBuf = (LPTRACEBUF)TlsGetValue( dwTlsIndex )) != NULL ) { TlsSetValue( dwTlsIndex, NULL ); pFixed = &lpBuf->Fixed; iMaxLength = MAX_VARIABLE_SIZE - pFixed->wVariableLength; iLength = _vsnprintf( lpBuf->Buffer + pFixed->wVariableLength, iMaxLength, szFormat, marker ) + 1; if ( iLength == 0 || iLength == iMaxLength + 1 ) { iLength = iMaxLength; lpBuf->Buffer[MAX_VARIABLE_SIZE-1] = '\0'; } _ASSERT( iLength <= iMaxLength ); pFixed->wBinaryOffset = sizeof(FIXEDTRACE) + pFixed->wVariableLength; pFixed->wVariableLength += LOWORD( (DWORD)iLength ); pFixed->wBinaryType = TRACE_STRING; pFixed->dwParam = (DWORD)lParam; // This is a 32-bit flag so the cast is OK // // this is a specific area where the app can overwrite // data. Could have used vnsprintf to avoid the overwrite // but this woudl have dragged in the C runtime and // introduced its overhead and own critical sections // ASSERT( pFixed->wVariableLength <= MAX_VARIABLE_SIZE ); CommitTraceBuffer( lpBuf ); // // need to use dwLength since we relinquish lpBuf // after we return from QueueAsyncTraceBuffer which // cannot fail // return iLength; } else return 0; } //+--------------------------------------------------------------- // // Function: AsyncBinaryTrace // // Synopsis: exported function to finish setting up trace buffer // with optional fields for binary traces // // Arguments: LPARAM: 32bit trace param used app level filtering // DWORD: type of binary data ( ie Message, User... ) // LPBYTE: ptr to the data // DWORD: length of the data // // Returns: returns length of the trace statement // //---------------------------------------------------------------- DbgTraceDLL int WINAPI AsyncBinaryTrace(LPARAM lParam, DWORD dwBinaryType, LPBYTE pbData, DWORD cbData ) { LPTRACEBUF lpBuf; WORD wLength; PFIXEDTR pFixed; if ( fInitialized == FALSE ) { return 0; } if ( (lpBuf = (LPTRACEBUF)TlsGetValue( dwTlsIndex )) != NULL ) { TlsSetValue( dwTlsIndex, NULL ); pFixed = &lpBuf->Fixed; wLength = LOWORD( min( cbData, MAX_BUFFER_SIZE ) ); CopyMemory( lpBuf->Buffer + pFixed->wVariableLength, pbData, wLength ); pFixed->wBinaryOffset = sizeof(FIXEDTRACE) + pFixed->wVariableLength; pFixed->wVariableLength += wLength; pFixed->wBinaryType = LOWORD( dwBinaryType ); pFixed->dwParam = (DWORD)lParam; // This is a 32-bit flag so the cast is OK CommitTraceBuffer( lpBuf ); // // need to use dwLength since we relinquish lpBuf // after we return from QueueAsyncTraceBuffer which // cannot fail // return (int)wLength; } else return 0; } //+--------------------------------------------------------------- // // Function: FlushAsyncTrace // // Synopsis: exported function to empty the pending queue. All // threads which call this function block until the // queue is empty // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- DllExport BOOL WINAPI FlushAsyncTrace( void ) { static long lPendingFlushs = -1; if ( fInitialized == FALSE ) { return FALSE; } else { EnterCriticalSection( &critSecFlush ); if ( PendQ.dwCount > 0 ) { SetEvent( PendQ.hFlushEvent ); if ( nAsyncThreadPriority < THREAD_PRIORITY_ABOVE_NORMAL ) { SetThreadPriority( PendQ.hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL ); } WaitForSingleObject( PendQ.hFlushedEvent, INFINITE ); if ( nAsyncThreadPriority < THREAD_PRIORITY_ABOVE_NORMAL ) { SetThreadPriority( PendQ.hWriteThread, nAsyncThreadPriority ); } ResetEvent( PendQ.hFlushedEvent ); } LeaveCriticalSection( &critSecFlush ); return TRUE; } } //+--------------------------------------------------------------- // // Function: InitAsyncTrace // // Synopsis: exported required function to rev things up. // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- DllExport BOOL WINAPI InitAsyncTrace( void ) { static BOOL bInitializing = FALSE; BOOL bRC = FALSE; DWORD dwThreadId; EnterCriticalSection(&g_csInitialize); if ( fInitialized ) { // // inc the count of successful initializations for this process // InterlockedIncrement( &dwInitializations ); LeaveCriticalSection(&g_csInitialize); return TRUE; } if ( InterlockedExchange( (LPLONG)&bInitializing, (LONG)TRUE ) ) { // // inc the count of successful initializations for this process // InterlockedIncrement( &dwInitializations ); LeaveCriticalSection(&g_csInitialize); return TRUE; } // to guard against race condition when the initializing thread is about to execute // 'InterlockedExchange( (LPLONG)&bInitializing, (LONG)FALSE )', while // there is another thread that passed the // if ( InterlockedExchange( (LPLONG)&bInitializing, (LONG)TRUE )) condition // if ( fInitialized) { // // inc the count of successful initializations for this process // InterlockedIncrement( &dwInitializations ); InterlockedExchange( (LPLONG)&bInitializing, (LONG)FALSE ); LeaveCriticalSection(&g_csInitialize); return TRUE; } // will read from registry later // dwNumTraces = 0; PendQ.dwProcessId = GetCurrentProcessId(); PendQ.hFile = INVALID_HANDLE_VALUE; PendQ.cbBufferEnd = 0; PendQ.dwThresholdCount = DEFAULT_MAX_FILE_SIZE / AVERAGE_TRACE_SIZE; __try { InitializeCriticalSection( &PendQ.critSecTail ); InitializeCriticalSection( &critSecWrite ); InitializeCriticalSection( &critSecFlush ); if ( (dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } if ( (dwRandFailTlsIndex = TlsAlloc()) == 0xFFFFFFFF ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } if ( GetTraceFlagsFromRegistry() == FALSE ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } // // Initialize the pool of trace buffers // must happen after reading the registy // if ( InitTraceBuffers( PendQ.dwThresholdCount, dwIncrementSize ) == FALSE ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } PendQ.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( PendQ.hEvent == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } // // PendQ.hFlushedEvent is manual reset so multiple threads can wait // PendQ.hFlushedEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( PendQ.hFlushedEvent == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } PendQ.hFlushEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( PendQ.hFlushEvent == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } // // hShutdownEvent is manual reset so multiple threads can be awaken // hShutdownEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( hShutdownEvent == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } ASSERT( PendQ.hRegNotifyThread == NULL ); PendQ.hRegNotifyThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)RegNotifyThread, NULL, 0, &dwThreadId ); if ( PendQ.hRegNotifyThread == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } else { // // bumping the priority onthis almost always dorminate thread // ensures that trace changes are applied soon after the // registry changes // SetThreadPriority( PendQ.hRegNotifyThread, THREAD_PRIORITY_ABOVE_NORMAL ); } ASSERT( PendQ.hWriteThread == NULL ); PendQ.hWriteThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)WriteTraceThread, NULL, 0, &dwThreadId ); if ( PendQ.hWriteThread == NULL ) { LeaveCriticalSection(&g_csInitialize); return FALSE; } else { // // setting the priority on this thread ensures that the // physical writing of the traces will not impact performance // of the main application task. Default is BELOW_NORMAL although // its controlled by a reg entry // SetThreadPriority( PendQ.hWriteThread, nAsyncThreadPriority ); } PendQ.pHead = PendQ.pTail = (LPTRACEBUF)&PendQ.Special; // // set our top level exception handler // lpfnPreviousFilter = SetUnhandledExceptionFilter( TopLevelExceptionFilter ); fInitialized = TRUE; InterlockedExchange( (LPLONG)&bInitializing, (LONG)FALSE ); // // inc the count of successful initializations for this process // InterlockedIncrement( &dwInitializations ); bRC = TRUE; } __finally { if ( bRC == FALSE ) { DWORD dwLastError = GetLastError(); AsyncTraceCleanup(); SetLastError( dwLastError ); } } LeaveCriticalSection(&g_csInitialize); return bRC; } //+--------------------------------------------------------------- // // Function: TermAsyncTrace // // Synopsis: exported required function to wind things down. // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- DllExport BOOL WINAPI TermAsyncTrace( void ) { EnterCriticalSection(&g_csInitialize); if ( fInitialized ) { if ( InterlockedDecrement( &dwInitializations ) == 0 ) { BOOL fRet; fRet = AsyncTraceCleanup(); LeaveCriticalSection(&g_csInitialize); return fRet; } LeaveCriticalSection(&g_csInitialize); return TRUE; } else { LeaveCriticalSection(&g_csInitialize); return FALSE; } } //+--------------------------------------------------------------- // // Function: DebugAssert // // Synopsis: exported required function for enhanced asserts // // Arguments: DWORD dwLine: source code line of the _ASSERT // LPSTR lpszFunction source code filename of the _ASSERT // LPSTR lpszExpression stringized version of _ASSERT param // // Returns: void // //---------------------------------------------------------------- char szAssertOutput[512]; DllExport void WINAPI DebugAssert( DWORD dwLine, LPSTR lpszFunction, LPSTR lpszExpression ) { DWORD dwError = GetLastError(); wsprintf( szAssertOutput, "\nASSERT: %s,\n File: %s,\n Line: %d\n Error: %d\n\n", lpszExpression, lpszFunction, dwLine, dwError ); OutputDebugString( szAssertOutput ); SetLastError( dwError ); DebugBreak(); } //+--------------------------------------------------------------- // // Function: QueueAsyncTraceBuffer // // Synopsis: Routine to implement the appending of TRACEBUF to // the FIFO PendQ // // Arguments: LPTRACEBUF: the buffer // // Returns: void // //---------------------------------------------------------------- void QueueAsyncTraceBuffer( LPTRACEBUF lpBuf ) { LPTRACEBUF pPrevTail; ASSERT( lpBuf != NULL ); ASSERT( lpBuf->dwSignature == TRACE_SIGNATURE ); lpBuf->pNext = NULL; EnterCriticalSection( &PendQ.critSecTail ); // // number of buffers on the queue can only decrease while // in this critical section since WriteTraceThread can continue // to pull buffers from the queue. // // WriteAsyncThread will not write this buffer until it has // been appended to the queue by incrementing PendQ.dwCount // // PendQ.pTail is only modified here and in a special case on the // background writer thread. The special case is when Special needs // to be moved from the Head of the queue to the Tail. Only during // this brief special case can both the background writer and the // foreground appender thread be operating on the same trace buffer. // pPrevTail = PendQ.pTail; pPrevTail->pNext = PendQ.pTail = lpBuf; LeaveCriticalSection( &PendQ.critSecTail ); InterlockedIncrement( &PendQ.dwCount ); // // wake up WriteTraceThread if necessary. It may not be since // WriteTraceThread will always empty its queue before sleeping // SetEvent( PendQ.hEvent ); } //+--------------------------------------------------------------- // // Function: DequeueAsyncTraceBuffer // // Synopsis: Routine to dequeue the top Trace Buffer from // the FIFO PendQ // // Arguments: void // // Returns: LPTRACEBUF: the buffer // //---------------------------------------------------------------- LPTRACEBUF DequeueAsyncTraceBuffer( void ) { LPTRACEBUF lpBuf; LPTRACEBUF pPrevTail; // // check to see if Special is at the head of the queue. If so, move // it to the end of the queue // if ( PendQ.pHead == (LPTRACEBUF)&PendQ.Special ) { // // need to NULL Special.pNext before the Exchange so the list // is terminated as soon as we do the exchange. We can lazily // set the old Tails next pointer since we're the only thread // that would dereference this pointer once its not the last // buffer in the FIFO // PendQ.pHead = PendQ.Special.pNext; PendQ.Special.pNext = NULL; EnterCriticalSection( &PendQ.critSecTail ); // // see comment in QueueAsyncTraceBuffer to describe why we // to grab the Tail critical section here. If we did not // include this Special buffer then we would have to grab // the critSec each time. // pPrevTail = PendQ.pTail; pPrevTail->pNext = PendQ.pTail = (LPTRACEBUF)&PendQ.Special; LeaveCriticalSection( &PendQ.critSecTail ); } // // again no critical section required since we're the only thread // accessing these PendQ.pHead. This needs to be remembered if we // were to add integratity checking to the queues at a later date // since this queue is effectively in a corrupt state. // lpBuf = PendQ.pHead; PendQ.pHead = lpBuf->pNext; InterlockedDecrement( &PendQ.dwCount ); ASSERT( lpBuf != NULL ); ASSERT( lpBuf->dwSignature == TRACE_SIGNATURE ); return lpBuf; } //+--------------------------------------------------------------- // // Function: AsyncTraceCleanup // // Synopsis: internla routine to clean things up // the FIFO PendQ // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- BOOL AsyncTraceCleanup( void ) { HANDLE hThreads[2]; int nObjects = 0; DWORD dw; INT_TRACE( "AsyncTraceCleanup Enter\n" ); if ( InterlockedExchange( &PendQ.fShutdown, TRUE ) == TRUE ) { return FALSE; } if ( dwTlsIndex != 0xFFFFFFFF ) { TlsFree( dwTlsIndex ); } if ( dwRandFailTlsIndex != 0xFFFFFFFF ) { TlsFree( dwRandFailTlsIndex ); } // // restore the initial Exception filter; NULL signifies use the default // SetUnhandledExceptionFilter( lpfnPreviousFilter ); if ( hShutdownEvent != NULL ) { INT_TRACE( "AsyncTraceCleanup Calling SetEvent( hShutdownEvent )\n" ); SetEvent( hShutdownEvent ); INT_TRACE( "AsyncTraceCleanup Called SetEvent: Error: 0x%X\n", GetLastError() ); } if ( PendQ.hWriteThread != NULL ) { hThreads[nObjects++] = PendQ.hWriteThread; } if ( PendQ.hRegNotifyThread != NULL ) { hThreads[nObjects++] = PendQ.hRegNotifyThread; } // // allow background threads forever to shutdown // if ( nObjects != 0 ) { INT_TRACE( "AsyncTraceCleanup Calling WFMO\n" ); dw = WaitForMultipleObjects(nObjects, hThreads, TRUE, INFINITE ); INT_TRACE( "AsyncTraceCleanup Called WFMO: dw: 0x%X Error: 0x%X\n", dw, GetLastError() ); } if ( PendQ.hWriteThread != NULL ) { CloseHandle( PendQ.hWriteThread ); PendQ.hWriteThread = NULL; } if ( PendQ.hRegNotifyThread != NULL ) { CloseHandle( PendQ.hRegNotifyThread ); PendQ.hRegNotifyThread = NULL; } if ( PendQ.hEvent != NULL ) { CloseHandle( PendQ.hEvent ); PendQ.hEvent = NULL; } if ( PendQ.hFlushEvent != NULL ) { CloseHandle( PendQ.hFlushEvent ); PendQ.hFlushEvent = NULL; } if ( PendQ.hFlushedEvent != NULL ) { CloseHandle( PendQ.hFlushedEvent ); PendQ.hFlushedEvent = NULL; } if ( hShutdownEvent != NULL ) { CloseHandle( hShutdownEvent ); hShutdownEvent = NULL; } #if FALSE INT_TRACE( "TailCritSec - Contention: %d, Entry: %d\n", PendQ.critSecTail.DebugInfo->ContentionCount, PendQ.critSecTail.DebugInfo->EntryCount ); INT_TRACE( "WriteCritSec - Contention: %d, Entry: %d\n", critSecWrite.DebugInfo->ContentionCount, critSecWrite.DebugInfo->EntryCount ); INT_TRACE( "FlushCritSec - Contention: %d, Entry: %d\n", critSecFlush.DebugInfo->ContentionCount, critSecFlush.DebugInfo->EntryCount ); #endif DeleteCriticalSection( &PendQ.critSecTail ); DeleteCriticalSection( &critSecWrite ); DeleteCriticalSection( &critSecFlush ); if ( PendQ.hFile != INVALID_HANDLE_VALUE ) { CloseHandle( PendQ.hFile ); } PendQ.pHead = PendQ.pTail = (LPTRACEBUF)&PendQ.Special; PendQ.Special.pNext = (LPTRACEBUF)NULL; // // free up the trace buffer CPool // TermTraceBuffers(); INT_TRACE( "Total number of traces: %d\n", dwNumTraces ); InterlockedExchange( &PendQ.fShutdown, FALSE ); fInitialized = FALSE; return TRUE; } //+--------------------------------------------------------------- // // Function: FlushBufferedWrites // // Synopsis: internal routine to write the PendQ temporary buffer // to disk. Used to avoid multiple OS calls and increase // the write buffers. // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- BOOL FlushBufferedWrites( void ) { BOOL b = TRUE; DWORD dwBytes; BOOL bRetry = TRUE; // // need to lock the file since multiple process on multiple machines // may be tracing the same file and both writes have to complete as one. // if ( PendQ.cbBufferEnd ) { DWORD dwOffset; ASSERT( PendQ.cbBufferEnd < MAX_WRITE_BUFFER_SIZE ); dwOffset = SetFilePointer( PendQ.hFile, 0, 0, FILE_END ); // // if the file is too big then we need to truncate it // if (dwOffset > dwMaxFileSize) { SetFilePointer(PendQ.hFile, 0, 0, FILE_BEGIN); SetEndOfFile(PendQ.hFile); } try_again: b = WriteFile( PendQ.hFile, PendQ.Buffer, PendQ.cbBufferEnd, &dwBytes, NULL ); if ( b == FALSE || dwBytes != PendQ.cbBufferEnd ) { DWORD dwError = GetLastError(); if( dwError && bRetry ) { bRetry = FALSE; Sleep( 100 ); goto try_again; } // ASSERT( FALSE ); INT_TRACE( "Error writing to file: %d, number of bytes %d:%d\n", dwError, PendQ.cbBufferEnd, dwBytes ); } } PendQ.cbBufferEnd = 0; return b; } //+--------------------------------------------------------------- // // Function: WriteTraceBuffer // // Synopsis: internal routine to route the trace info to the // appropriate trace log // // Arguments: LPTRACEBUF: the buffer to write // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- BOOL WriteTraceBuffer( LPTRACEBUF lpBuf ) { ASSERT( lpBuf != NULL ); ASSERT( lpBuf->dwSignature == TRACE_SIGNATURE ); InterlockedIncrement( &dwNumTraces ); EnterCriticalSection( &critSecWrite ); if ( IsTraceFile( dwTraceOutputType ) && PendQ.hFile != INVALID_HANDLE_VALUE ) { DWORD dwWrite; // // assert must be handled inside critical section // ASSERT( PendQ.cbBufferEnd+MAX_TRACE_ENTRY_SIZE < MAX_WRITE_BUFFER_SIZE ); CopyMemory( PendQ.Buffer + PendQ.cbBufferEnd, (char *)&lpBuf->Fixed, dwWrite = sizeof(FIXEDTRACE) + lpBuf->Fixed.wVariableLength ); PendQ.cbBufferEnd += dwWrite; if ( PendQ.cbBufferEnd + MAX_TRACE_ENTRY_SIZE >= MAX_WRITE_BUFFER_SIZE || dwAsyncTraceFlag == 0 ) { FlushBufferedWrites(); } } else if ( dwTraceOutputType & TRACE_OUTPUT_DEBUG ) { char szThread[16]; LPSTR lpsz; EnterCriticalSection( &critSecWrite ); wsprintf( szThread, "0x%08X: ", lpBuf->Fixed.dwThreadId ); OutputDebugString( szThread ); switch( lpBuf->Fixed.wBinaryType ) { case TRACE_STRING: // // lstrcat may appear wasteful here; but it is less expensive than an // additional call to OutputDebugString( "\r\n" ); which works by // raising an exception. // // although appending \r\n on already full buffer is even worse // lpsz = lpBuf->Buffer + lpBuf->Fixed.wBinaryOffset - sizeof(FIXEDTRACE); OutputDebugString( lpsz ); OutputDebugString( "\r\n" ); break; case TRACE_BINARY: OutputDebugString( "Binary Trace\r\n" ); break; case TRACE_MESSAGE: OutputDebugString( "Message Trace\r\n" ); break; } LeaveCriticalSection( &critSecWrite ); } else if ( dwTraceOutputType & TRACE_OUTPUT_DISCARD ) { // // fastest way to remove buffers. Used to find // deadlocks and race conditions // } else if ( dwTraceOutputType & TRACE_OUTPUT_INVALID ) { InterlockedDecrement( &dwNumTraces ); // // unknown trace output type // ASSERT( FALSE ); } LeaveCriticalSection( &critSecWrite ); return TRUE; } //+--------------------------------------------------------------- // // Function: FlushAsyncPendingQueue // // Synopsis: internal routine to empty the PendQ queue from the // background thread // Assumes it is not called re-entrantly: actually the // FIFO queue assumes only one thread dequeues buffers // // Arguments: void // // Returns: BOOL: whether it worked // //---------------------------------------------------------------- void FlushAsyncPendingQueue( void ) { LPTRACEBUF lpBuf; while( PendQ.dwCount > 0 ) { lpBuf = DequeueAsyncTraceBuffer(); // // if we've buffered more than we'll write before // truncating the file then throw away the trace // if ( PendQ.dwCount < PendQ.dwThresholdCount ) { WriteTraceBuffer( lpBuf ); } else { INT_TRACE( "Discarding traces: %u\n", PendQ.dwCount ); } FreeTraceBuffer( lpBuf ); } FlushBufferedWrites(); } #define NUM_WRITE_THREAD_OBJECTS 3 //+--------------------------------------------------------------- // // Function: WriteTraceThread // // Synopsis: background thread routine for pulling and writing // trace buffers from PendQ FIFO queue. // // Arguments: see Win32 SDK - ignored here // // Returns: DWORD: 0 if we exitted gracefully // //---------------------------------------------------------------- DWORD WriteTraceThread( LPDWORD lpdw ) { HANDLE Handles[NUM_WRITE_THREAD_OBJECTS]; DWORD dw; // // preference given to Shutdown, FlushEvent and then the // normal buffer event. This ensures that provide a quick // response on both shutdown and to a lesser extent Flush // since other threads are waiting for this thread to respond. // Handles[0] = hShutdownEvent; Handles[1] = PendQ.hFlushEvent; Handles[2] = PendQ.hEvent; INT_TRACE( "WriteTraceThreadId 0x%X\n", GetCurrentThreadId() ); for ( ;; ) { dw = WaitForMultipleObjects(NUM_WRITE_THREAD_OBJECTS, Handles, FALSE, INFINITE ); switch( dw ) { // // normal signalled event // case WAIT_OBJECT_0+2: FlushAsyncPendingQueue(); break; // // signalled by a foreground thread to flush our Q // case WAIT_OBJECT_0+0: case WAIT_OBJECT_0+1: FlushAsyncPendingQueue(); if ( dw == WAIT_OBJECT_0+1 ) { SetEvent( PendQ.hFlushedEvent ); } else { INT_TRACE( "Exiting WriteTraceThread for hShutdownEvent\n" ); return 0; } break; default: GetLastError(); ASSERT( FALSE ); } } INT_TRACE( "Exiting WriteTraceThread abnormally\n" ); } //+--------------------------------------------------------------------------- // // Function: ShouldLogModule // // Synopsis: Figures out whether a particular module is on the list of // modules to be logged. // // Arguments: [szModule] -- Name of module to check // // Returns: TRUE if module should log, FALSE if logging is disabled for // MODULE // // Notes: // //---------------------------------------------------------------------------- BOOL ShouldLogModule( LPCSTR szModule) { LPSTR szEntry; // // If a list of modules has not been specified, then all modules are to be // logged // if (mszModules[0] == 0) { return TRUE; } // // Otherwise, we check to see if the name of the module is in the list // mszModule is expected to be a set of NULL terminated strings, with the // last string double-null-terminated. // szEntry = mszModules; while (szEntry[0] != 0) { if (lstrcmpi(szEntry, szModule) == 0) { return TRUE; } szEntry += (strlen(szEntry) + 1); } return FALSE; }