|
|
/*----------------------------------------------------------------------
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
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
typedef BOOL (WINAPI * INITIALIZE_SECURITY_DESCRIPTOR_FN) ( OUT PSECURITY_DESCRIPTOR pSecurityDescriptor, IN DWORD dwRevision );
typedef BOOL (WINAPI * SET_SECURITY_DESCRIPTOR_DACL_FN) ( IN OUT PSECURITY_DESCRIPTOR pSecurityDescriptor, IN BOOL bDaclPresent, IN PACL pDacl, IN BOOL bDaclDefaulted );
INITIALIZE_SECURITY_DESCRIPTOR_FN g_pfnInitializeSecurityDescriptor=NULL; SET_SECURITY_DESCRIPTOR_DACL_FN g_pfnSetSecurityDescriptorDacl=NULL; HINSTANCE g_hinst_AdvapiDll = NULL;
//
// #define TRACE_ENABLED
//
#include "traceint.h"
//
// Per Process global variables
//
PENDQ PendQ; BOOL fInitialized; HANDLE hShutdownEvent; DWORD dwInitializations = 0;
//
// 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
//
DWORD INTERNAL__dwEnabledTracesDefault = 0; DWORD* INTERNAL__dwEnabledTraces = &INTERNAL__dwEnabledTracesDefault;
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
//
INTERNAL__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
// LPCSTR: source file of the exception
// LPCSTR: function name of the exception
// DWORD: type of trace
//
// Returns: void
//
//----------------------------------------------------------------
__inline void SetTraceBufferInfo( LPTRACEBUF lpBuf, int iLine, LPCSTR pszFile, LPCSTR pszFunction, DWORD dwTraceMask, DWORD dwError ) { LPCSTR 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 ); }
BOOL GetProcAddresses() { //note that advapi32.dll may already be loaded here.
g_hinst_AdvapiDll = LoadLibrary(TEXT("advapi32.dll")); if (NULL == g_hinst_AdvapiDll) { INT_TRACE( "Not able to load advapi32.dll\n" ); _ASSERT(0); goto cleanup; }
g_pfnInitializeSecurityDescriptor = (INITIALIZE_SECURITY_DESCRIPTOR_FN) GetProcAddress(g_hinst_AdvapiDll, "InitializeSecurityDescriptor"); if (NULL == g_pfnInitializeSecurityDescriptor) { INT_TRACE( "Not able to find InitializeSecurityDescriptor\n"); _ASSERT(0); goto cleanup; }
g_pfnSetSecurityDescriptorDacl = (SET_SECURITY_DESCRIPTOR_DACL_FN) GetProcAddress(g_hinst_AdvapiDll, "SetSecurityDescriptorDacl"); if (NULL == g_pfnSetSecurityDescriptorDacl) { INT_TRACE( "Not able to find SetSecurityDescriptorDacl\n"); _ASSERT(0); goto cleanup; }
return TRUE; cleanup: return FALSE; }
/* This function determines whether the OS is Windows NT or
Windows 9.x */ BOOL IsWindowsNT() { OSVERSIONINFO VersionInformation; DWORD dwError; VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (FALSE == GetVersionEx(&VersionInformation)) // pointer to version
// information structure
{
dwError = GetLastError(); INT_TRACE( "GetVersionEx Failed %d\n", dwError ); dwError = dwError + 1; ASSERT(0); // assume NT
return TRUE; } if (VER_PLATFORM_WIN32_NT == VersionInformation.dwPlatformId ) { return TRUE; } return FALSE; }
//+---------------------------------------------------------------
//
// Function: GetWorldSecurityAttributes
//
// Synopsis: code is cut and pasted from the Win32 SDK help files
// AshishS: This code used the SetSecurityDescriptorDacl and
// InitializeSecurityDescriptor in Windows NT. However, these
// functions are not implemented in windows 98, so we have to
// do special handling for windows 98.
//
//
// Arguments: void
//
// Returns: static security attributes for Everyone access
//
//----------------------------------------------------------------
LPSECURITY_ATTRIBUTES GetWorldSecurityAttributes() { static SECURITY_ATTRIBUTES SecurityAttrib; static SECURITY_DESCRIPTOR SecurityDesc;
FillMemory( (char*)&SecurityDesc, sizeof(SECURITY_DESCRIPTOR), 0 ); if (IsWindowsNT()) { if (g_pfnSetSecurityDescriptorDacl == NULL || g_pfnInitializeSecurityDescriptor == NULL) { if (GetProcAddresses() == FALSE) { _ASSERT(FALSE); goto done; } } if ( g_pfnInitializeSecurityDescriptor( &SecurityDesc, SECURITY_DESCRIPTOR_REVISION) ) { //
// Add a NULL disc. ACL to the security descriptor.
//
if ( g_pfnSetSecurityDescriptorDacl(&SecurityDesc, TRUE,// specifying a disc. ACL
(PACL)NULL, FALSE))//not a default disc.ACL
{ SecurityAttrib.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttrib.lpSecurityDescriptor = &SecurityDesc; SecurityAttrib.bInheritHandle = FALSE; return &SecurityAttrib; } } } else { SecurityAttrib.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttrib.bInheritHandle = TRUE; return &SecurityAttrib; }
done: return (LPSECURITY_ATTRIBUTES) NULL; }
//+---------------------------------------------------------------
//
// Function: DllEntryPoint
//
// Synopsis: only relevence is allocating thread local storage var
//
// Arguments: see Win32 SDK
//
// Returns: see Win32 SDK
//
//----------------------------------------------------------------
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: return TRUE; // return InitAsyncTrace();
case DLL_THREAD_ATTACH: TlsSetValue( dwTlsIndex, (LPVOID)NULL ); break;
case DLL_PROCESS_DETACH: // TermAsyncTrace();
if (NULL != g_hinst_AdvapiDll) { g_pfnSetSecurityDescriptorDacl = NULL; g_pfnInitializeSecurityDescriptor = NULL; FreeLibrary(g_hinst_AdvapiDll); } return FALSE; } return TRUE; }
//+---------------------------------------------------------------
//
// Function: INTERNAL__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: LPCSTR: source file of the exception
// int: line number of the exception
// LPCSTR: function name of the exception
// DWORD: type of trace
//
// Returns: returns a BOOL 1 if successful; 0 on failure
//
//----------------------------------------------------------------
int WINAPI INTERNAL__SetAsyncTraceParams( LPCSTR pszFile , int iLine , LPCSTR pszFunction , DWORD dwTraceMask ) { LPTRACEBUF lpBuf; DWORD dwError = GetLastError();
if ( fInitialized == FALSE ) { return 0; }
if ( lpBuf = GetTraceBuffer() ) {
SetTraceBufferInfo( lpBuf, iLine, pszFile, pszFunction, dwTraceMask, dwError ); TlsSetValue( dwTlsIndex, (LPVOID)lpBuf );
return 1; } else return 0; }
//+---------------------------------------------------------------
//
// Function: INTERNAL__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
//
//----------------------------------------------------------------
int WINAPI INTERNAL__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)(DWORD_PTR)lParam;
//
// 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: INTERNAL__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
//
//----------------------------------------------------------------
int WINAPI INTERNAL__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)(DWORD_PTR)lParam;
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: INTERNAL__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
//
//----------------------------------------------------------------
BOOL WINAPI INTERNAL__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: INTERNAL__InitAsyncTrace
//
// Synopsis: exported required function to rev things up.
//
// Arguments: void
//
// Returns: BOOL: whether it worked
//
//----------------------------------------------------------------
BOOL WINAPI INTERNAL__InitAsyncTrace( DWORD* pdwEnabledTraces ) { static BOOL bInitializing = FALSE; BOOL bRC = FALSE; DWORD dwThreadId;
if ( fInitialized ) { //
// inc the count of successful initializations for this process
//
InterlockedIncrement( &dwInitializations ); return TRUE; }
if ( InterlockedExchange( (LPLONG)&bInitializing, (LONG)TRUE ) ) { //
// inc the count of successful initializations for this process
//
InterlockedIncrement( &dwInitializations ); return TRUE; }
// will read from registry later
//
INTERNAL__dwEnabledTraces = pdwEnabledTraces; 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 ) { return FALSE; }
if ( GetTraceFlagsFromRegistry() == FALSE ) { return FALSE; }
//
// Initialize the pool of trace buffers
// must happen after reading the registy
//
if ( InitTraceBuffers( PendQ.dwThresholdCount, dwIncrementSize ) == FALSE ) { return FALSE; }
PendQ.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( PendQ.hEvent == NULL ) { return FALSE; }
//
// PendQ.hFlushedEvent is manual reset so multiple threads can wait
//
PendQ.hFlushedEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( PendQ.hFlushedEvent == NULL ) { return FALSE; }
PendQ.hFlushEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( PendQ.hFlushEvent == NULL ) { return FALSE; } //
// hShutdownEvent is manual reset so multiple threads can be awaken
//
hShutdownEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( hShutdownEvent == NULL ) { return FALSE; }
//
// hFileMutex is only owned when write to the local file
// First we need to create a security descriptor
//
PendQ.hFileMutex = CreateMutex( GetWorldSecurityAttributes(), FALSE, "MSN-Shuttle-TraceFile" ); if ( PendQ.hFileMutex == NULL ) { return FALSE; }
ASSERT( PendQ.hRegNotifyThread == NULL );
PendQ.hRegNotifyThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)RegNotifyThread, NULL, 0, &dwThreadId );
if ( PendQ.hRegNotifyThread == NULL ) { 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 ) { 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 ); } } return bRC; }
//+---------------------------------------------------------------
//
// Function: INTERNAL__TermAsyncTrace
//
// Synopsis: exported required function to wind things down.
//
// Arguments: void
//
// Returns: BOOL: whether it worked
//
//----------------------------------------------------------------
BOOL WINAPI INTERNAL__TermAsyncTrace( void ) { if ( fInitialized ) { if ( InterlockedDecrement( &dwInitializations ) == 0 ) { return AsyncTraceCleanup(); } return TRUE; } else { return FALSE; } }
//+---------------------------------------------------------------
//
// Function: INTERNAL__DebugAssert
//
// Synopsis: exported required function for enhanced asserts
//
// Arguments: DWORD dwLine: source code line of the _ASSERT
// LPCSTR lpszFunction source code filename of the _ASSERT
// LPCSTR lpszExpression stringized version of _ASSERT param
//
// Returns: void
//
//----------------------------------------------------------------
void DebugAssert( DWORD dwLine, LPCSTR lpszFunction, LPCSTR lpszExpression ) { INTERNAL__DebugAssert( dwLine, lpszFunction, lpszExpression ); } char szAssertOutput[512]; void WINAPI INTERNAL__DebugAssert( DWORD dwLine, LPCSTR lpszFunction, LPCSTR 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 ); }
//
// 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 ( PendQ.hFileMutex != NULL ) { CloseHandle( PendQ.hFileMutex ); PendQ.hFileMutex = 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.
//
WaitForSingleObject( PendQ.hFileMutex, INFINITE );
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 ); } }
ReleaseMutex( PendQ.hFileMutex ); 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]; LPCSTR 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" ); }
|