Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1482 lines
41 KiB

/*----------------------------------------------------------------------
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 <windows.h>
#include <stdio.h>
#include <stdarg.h>
//
// #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;
}