Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2201 lines
109 KiB

/****************************************************************************/
/* ntrcint.c */
/* */
/* Internal tracing functions - Windows NT specific */
/* */
/* Copyright(C) Microsoft Corporation 1997-1998 */
/****************************************************************************/
#include <adcg.h>
/****************************************************************************/
/* Define TRC_FILE and TRC_GROUP. */
/****************************************************************************/
#undef TRC_FILE
#define TRC_FILE "ntrcint"
#define TRC_GROUP TRC_GROUP_TRACE
/****************************************************************************/
/* Trace specific includes. */
/****************************************************************************/
#include <atrcapi.h>
#include <atrcint.h>
#ifndef OS_WINCE
#include <imagehlp.h>
#endif
#include <ndcgver.h>
/****************************************************************************/
/* */
/* DATA */
/* */
/****************************************************************************/
#define DC_INCLUDE_DATA
#include <atrcdata.c>
#undef DC_INCLUDE_DATA
/****************************************************************************/
/* */
/* FUNCTIONS */
/* */
/****************************************************************************/
/****************************************************************************/
/* FUNCTION: DllMain(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Entry/exit point for the trace DLL. This function is called whenever a */
/* process or thread attaches or detaches from this DLL. */
/* */
/* PARAMETERS: */
/* =========== */
/* hModule : a module handle. */
/* reasonForCall : an enumerated type that indicates which of the four */
/* reasons the DLLMain function is being called: process */
/* attach, thread attach, thread detach or process */
/* detach. */
/* lpReserved : unused. */
/* */
/* RETURNS: */
/* ======== */
/* TRUE if the attachment succeeds and FALSE otherwise. */
/* */
/****************************************************************************/
#ifndef STATICONLY
int APIENTRY DllMain(HANDLE hModule,
DWORD reasonForCall,
LPVOID lpReserved)
{
DCBOOL retValue = TRUE;
DCUINT rc = 0;
DC_IGNORE_PARAMETER(lpReserved);
#ifdef OS_WINCE
DC_IGNORE_PARAMETER(hModule);
#endif // OS_WINCE
/************************************************************************/
/* Determine the reason for the call. Note that anything we do in here */
/* is thread safe as we implicitly have the process critical section. */
/************************************************************************/
switch (reasonForCall)
{
#ifndef OS_WINCE
/********************************************************************/
/* A process is attaching to this DLL. */
/********************************************************************/
case DLL_PROCESS_ATTACH:
{
/****************************************************************/
/* Call the internal function to initialize the trace DLL. */
/* This function sets up the memory mapped shared data, and */
/* opens and initializes the trace files. It may be called */
/* either via a process attach or by the first person to call */
/* the trace DLL. The latter case can only occur if another */
/* DLL performs trace calls in its <DllMain> function and that */
/* DLLs <DllMain> function is called before the trace DLLs */
/* <DllMain> function (i.e. this function!). */
/****************************************************************/
rc = TRC_Initialize(TRUE);
if (0 != rc)
{
retValue = FALSE;
DC_QUIT;
}
/****************************************************************/
/* Save the module handle. */
/****************************************************************/
trchModule = hModule;
/****************************************************************/
/* Get the trace DLL module file name. We use this later when */
/* we get a stack trace. */
/****************************************************************/
if ( TRCGetModuleFileName(
trcpSharedData->trcpModuleFileName,
SIZE_TCHARS(trcpSharedData->trcpModuleFileName)) !=
DC_RC_OK )
{
retValue = FALSE;
DC_QUIT;
}
/****************************************************************/
/* A process is attaching so trace this fact out. */
/****************************************************************/
TRCInternalTrace(TRC_PROCESS_ATTACH_NOTIFY);
}
break;
/********************************************************************/
/* A process is detaching from this DLL. */
/********************************************************************/
case DLL_PROCESS_DETACH:
{
/****************************************************************/
/* Write out the process detach trace line. */
/****************************************************************/
TRCInternalTrace(TRC_PROCESS_DETACH_NOTIFY);
/****************************************************************/
/* Call the trace DLL termination function. This will close */
/* all files, free the shared data and then close the mutex */
/* handle. */
/****************************************************************/
TRC_Terminate(TRUE);
}
break;
/********************************************************************/
/* A thread is attaching to this DLL. */
/********************************************************************/
case DLL_THREAD_ATTACH:
{
/****************************************************************/
/* Write out the thread attach trace line. */
/****************************************************************/
TRCInternalTrace(TRC_THREAD_ATTACH_NOTIFY);
}
break;
/********************************************************************/
/* A thread is detaching from this DLL. */
/********************************************************************/
case DLL_THREAD_DETACH:
{
/****************************************************************/
/* Write out the thread detach trace line. */
/****************************************************************/
TRCInternalTrace(TRC_THREAD_DETACH_NOTIFY);
}
break;
#endif // OS_WINCE
}
/************************************************************************/
/* Now return the appropriate return value. NT currently only checks */
/* the value for the DLL_PROCESS_ATTACH case - if it is false then the */
/* app will fail to initialize. */
/************************************************************************/
DC_EXIT_POINT:
return(retValue);
} /* DllMain */
#endif
/****************************************************************************/
/* FUNCTION: TRCBlankFile(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function fills the specified trace file with spaces. */
/* */
/* PARAMETERS: */
/* =========== */
/* fileNumber : which file to blank. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCBlankFile(DCUINT fileNumber)
{
/************************************************************************/
/* Use DC_MEMSET to fill the file with spaces. */
/************************************************************************/
DC_MEMSET(trcpFiles[fileNumber], '\0', trcpConfig->maxFileSize);
/************************************************************************/
/* Finally flush this change to disk. Setting the second parameter to */
/* zero flushes the whole file to disk. */
/************************************************************************/
FlushViewOfFile(trcpFiles[fileNumber], 0);
return;
} /* TRC_BlankFile */
/****************************************************************************/
/* FUNCTION: TRCCloseSharedData(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function closes the shared data memory mapped file. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCCloseSharedData(DCVOID)
{
/************************************************************************/
/* Now we need to unmap our view of the file. */
/************************************************************************/
UnmapViewOfFile(trcpSharedData);
trcpSharedData = NULL;
/************************************************************************/
/* Now close the handle to the file mapping object. */
/************************************************************************/
CloseHandle(trchSharedDataObject);
trchSharedDataObject = NULL;
/************************************************************************/
/* NULL our static pointer to the shared configuration data. */
/************************************************************************/
trcpConfig = NULL;
/************************************************************************/
/* That's it so just return. */
/************************************************************************/
return;
} /* TRCCloseSharedData */
/****************************************************************************/
/* FUNCTION: TRCCloseSingleFile(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Closes a single trace memory mapped file. */
/* */
/* PARAMETERS: */
/* =========== */
/* fileNumber : which file to close. */
/* seconds : value to set the seconds time stamp of the file to. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCCloseSingleFile(DCUINT fileNumber, DCUINT seconds)
{
FILETIME fileTime;
SYSTEMTIME systemTime;
DCUINT32 offset;
/************************************************************************/
/* We need to reset the size of this file - we do this by determining */
/* the trace file offset. Make sure that we do this before we unmap */
/* the file. */
/************************************************************************/
offset = TRCDetermineOffset(fileNumber);
/************************************************************************/
/* Unmap the view of the file. */
/************************************************************************/
UnmapViewOfFile(trcpFiles[fileNumber]);
trcpFiles[fileNumber] = NULL;
/************************************************************************/
/* Free up the handle to the file mapping object. */
/************************************************************************/
CloseHandle(trchMappingObjects[fileNumber]);
trchMappingObjects[fileNumber] = NULL;
/************************************************************************/
/* Now set the file pointer to the end of all the trace text and then */
/* set the end of the file to this position. */
/************************************************************************/
SetFilePointer(trchFileObjects[fileNumber],
offset,
NULL,
FILE_BEGIN);
SetEndOfFile(trchFileObjects[fileNumber]);
/************************************************************************/
/* Now we have to do something a little messy - the file time is not */
/* properly updated when the memory mapped file is closed and we rely */
/* on the file time to decide which file to start tracing to at the */
/* start of day. Therefore we need to force the system to update the */
/* file times using SetFileTime (we set the created, modified and */
/* accessed times). On NT4.0 this does not guarantee that the times */
/* are the same - one file had a created time of 16:35:16 and a */
/* modified time of 16:35:18 after a call to SetFileTime! Files only */
/* have a time resolution of two seconds if they are stored on a FAT */
/* partition by NT. */
/************************************************************************/
GetSystemTime(&systemTime);
/************************************************************************/
/* Set the number of seconds of the file. */
/************************************************************************/
systemTime.wSecond = (WORD) seconds;
/************************************************************************/
/* Now convert the system time to a file time and update the file time. */
/************************************************************************/
SystemTimeToFileTime(&systemTime, &fileTime);
SetFileTime(trchFileObjects[fileNumber], &fileTime, &fileTime, &fileTime);
/************************************************************************/
/* Close the file handle. */
/************************************************************************/
CloseHandle(trchFileObjects[fileNumber]);
trchFileObjects[fileNumber] = NULL;
return;
} /* TRCCloseSingleFile */
/****************************************************************************/
/* FUNCTION: TRCDetermineIndicator(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function sets the trace file indicator as follows: */
/* */
/* - No trace files exist : indicator set to 0 */
/* - One trace file exists : indicator set to the existing file (0 or 1) */
/* - Both trace files exist : indicator set to the newer file. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCDetermineIndicator(DCVOID)
{
DCINT i;
DCBOOL rc[TRC_NUM_FILES];
DCINT32 tdRC;
FILETIME fileTime[TRC_NUM_FILES];
/************************************************************************/
/* We also need to set up the trace file indicator. By default we use */
/* trace file 0. */
/************************************************************************/
trcpSharedData->trcIndicator = 0;
/************************************************************************/
/* Determine the most recent trace file. Use GetFileTime to get the */
/* date and time of this file. */
/************************************************************************/
for (i = 0; i < TRC_NUM_FILES; i++)
{
rc[i] = TRCGetFileTime(i, &(fileTime[i]));
}
/************************************************************************/
/* Now check to see which file we should return based on the following */
/* options: */
/* */
/* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄ¿ */
/* ³ File 0 exists ³ File 1 exists ³ return ³ */
/* ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄ´ */
/* ³ No ³ No ³ file 0 ³ */
/* ³ No ³ Yes ³ file 1 ³ */
/* ³ Yes ³ Yes ³ compare ³ */
/* ³ Yes ³ No ³ file 0 ³ */
/* ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÙ */
/************************************************************************/
/************************************************************************/
/* If file 1 does not exist then we return file 0 regardless. */
/************************************************************************/
if (FALSE == rc[1])
{
DC_QUIT;
}
/************************************************************************/
/* If file 0 does not exist and file 1 does, then return file 1. */
/************************************************************************/
if ((FALSE == rc[0]) && (TRUE == rc[1]))
{
trcpSharedData->trcIndicator = 1;
DC_QUIT;
}
/************************************************************************/
/* If we have got this far then both trace files exist so we need to */
/* make a decision based on their ages. User the Win32 CompareFileTime */
/* function to do this. */
/************************************************************************/
tdRC = CompareFileTime(&(fileTime[0]), &(fileTime[1]));
/************************************************************************/
/* If the file times are equal or the first file is newer than the */
/* second then select file 0 (i.e. just quit). */
/************************************************************************/
if (tdRC >= 0)
{
DC_QUIT;
}
/************************************************************************/
/* If we get here then file 1 is newer than file 0 so set the indicator */
/* to file 1. */
/************************************************************************/
trcpSharedData->trcIndicator = 1;
DC_EXIT_POINT:
return;
} /* TRCDetermineIndicator */
/****************************************************************************/
/* FUNCTION: TRCDetermineOffset(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function determines the end-of-file offset in the selected trace */
/* file. */
/* */
/* PARAMETERS: */
/* =========== */
/* fileNum : the number of the file to determine the offset for. */
/* */
/* RETURNS: */
/* ======== */
/* The offset in that file. */
/* */
/****************************************************************************/
DCUINT32 DCINTERNAL TRCDetermineOffset(DCUINT32 fileNum)
{
DCUINT32 retVal;
PDCTCHAR pTemp;
PDCUINT8 pWork;
/************************************************************************/
/* Set the temporary pointer to point at the end of the trace file. */
/************************************************************************/
pWork = (PDCUINT8)(trcpFiles[fileNum]);
if(NULL == pWork)
{
return 0;
}
pWork += trcpConfig->maxFileSize - sizeof(DCTCHAR);
pTemp = (PDCTCHAR)pWork;
/************************************************************************/
/* Now run back through the trace file looking for the first non-space */
/* character. */
/************************************************************************/
while ((pTemp >= trcpFiles[fileNum]) &&
(_T('\0') == *pTemp))
{
pTemp--;
}
/************************************************************************/
/* Increment forward to the next blank character. It does not matter */
/* if we increment past the end of the file as we check whether we need */
/* to flip the trace files everytime we write a trace line. */
/************************************************************************/
pTemp++;
/************************************************************************/
/* Now set the offset correctly. */
/************************************************************************/
retVal = (DCUINT32)(pTemp - trcpFiles[fileNum]);
return(retVal);
} /* TRCDetermineOffset */
/****************************************************************************/
/* FUNCTION: TRCExitProcess(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function kills the current process. */
/* */
/* PARAMETERS: */
/* =========== */
/* exitCode : exit code for the terminating process */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCExitProcess(DCUINT32 exitCode)
{
#ifndef OS_WINCE
ExitProcess(exitCode);
#else
//BUGBUG this is broken if not called from the main
//thread.
ExitThread(exitCode);
#endif
}
/****************************************************************************/
/* FUNCTION: TRCGetCurrentDate(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function gets the current local date and returns it in a DC_DATE */
/* structure. */
/* */
/* PARAMETERS: */
/* =========== */
/* pDate : a pointer to a DC_DATE structure. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCGetCurrentDate(PDC_DATE pDate)
{
SYSTEMTIME systemTime;
/************************************************************************/
/* Call the Win32 API function to get the current time. */
/************************************************************************/
GetLocalTime(&systemTime);
/************************************************************************/
/* Reformat the date into a DC_DATE structure. */
/************************************************************************/
pDate->day = (DCUINT8) systemTime.wDay;
pDate->month = (DCUINT8) systemTime.wMonth;
pDate->year = (DCUINT16) systemTime.wYear;
} /* TRCGetCurrentDate */
/****************************************************************************/
/* FUNCTION: TRCGetCurrentTime(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function gets the current local time and returns it in a DC_TIME */
/* structure. */
/* */
/* PARAMETERS: */
/* =========== */
/* pTime : a pointer to a DC_TIME structure. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCGetCurrentTime(PDC_TIME pTime)
{
SYSTEMTIME systemTime;
/************************************************************************/
/* Call the Win32 API function to get the current time. */
/************************************************************************/
GetLocalTime(&systemTime);
/************************************************************************/
/* Reformat the time into a DC_TIME structure. */
/************************************************************************/
pTime->hour = (DCUINT8)systemTime.wHour;
pTime->min = (DCUINT8)systemTime.wMinute;
pTime->sec = (DCUINT8)systemTime.wSecond;
pTime->hundredths = (DCUINT8)(systemTime.wMilliseconds / 10);
} /* TRCGetCurrentTime */
/****************************************************************************/
/* FUNCTION: TRCGetFileTime(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function tests if the specified file exists - if it does it */
/* returns TRUE and fills in pFileTime with a FILETIME structure. If the */
/* file does not exist it returns FALSE. */
/* */
/* PARAMETERS: */
/* =========== */
/* fileNumber : number of file to query. */
/* pFileTime : a pointer to a FILETIME structure. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCBOOL DCINTERNAL TRCGetFileTime(DCUINT fileNumber,
PDCFILETIME pFileTime)
{
DCBOOL rc = FALSE;
HANDLE hFile;
/************************************************************************/
/* Attempt to open the file. By specifying OPEN_EXISITING, we only try */
/* to open an existing file - the call will fail if the file doesn't */
/* already exist. */
/************************************************************************/
hFile = CreateFile(trcpConfig->fileNames[fileNumber],
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
/************************************************************************/
/* Now check to see if the file exists - if it is invalid then the file */
/* doesn't exist. */
/************************************************************************/
if (INVALID_HANDLE_VALUE == hFile)
{
/********************************************************************/
/* The file doesn't exist so return FALSE. */
/********************************************************************/
DC_QUIT;
}
/************************************************************************/
/* Determine the most recent trace file. Use GetFileTime to get the */
/* date and time of this file. */
/************************************************************************/
rc = GetFileTime(hFile, NULL, NULL, pFileTime);
/************************************************************************/
/* Finally close the file handle. */
/************************************************************************/
CloseHandle(hFile);
DC_EXIT_POINT:
return(rc);
} /* TRCGetFileTime */
/****************************************************************************/
/* FUNCTION: TRCSystemError(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function obtains the value of the system error flag and outputs it */
/* to the trace file as an alert level trace. */
/* */
/* Note that NT maintains the last system error on a per-thread basis and */
/* that most Win32 API function calls set it if they fail. */
/* */
/* PARAMETERS: */
/* =========== */
/* traceComponent : the trace component. */
/* lineNumber : the line number. */
/* funcName : the function name. */
/* fileName : the file name. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCSystemError(DCUINT traceComponent,
DCUINT lineNumber,
PDCTCHAR funcName,
PDCTCHAR fileName,
PDCTCHAR string)
{
DCUINT32 length;
DWORD lastError;
DWORD rc;
HRESULT hr;
/************************************************************************/
/* Get the last system error for this thread. We will restore this at */
/* the end of this function. */
/************************************************************************/
lastError = GetLastError();
/************************************************************************/
/* Grab the mutex. */
/************************************************************************/
TRCGrabMutex();
/************************************************************************/
/* The output string will be of the format: */
/* */
/* SYSTEM ERROR in <System Call>, <id of error> , <associated string> */
/* */
/* So create the first entry in the string. */
/************************************************************************/
hr = StringCchPrintf(trcpOutputBuffer,
TRC_LINE_BUFFER_SIZE,
_T("SYSTEM ERROR in %s, %d, "),
string,
lastError);
if (SUCCEEDED(hr)) {
length = DC_TSTRLEN(trcpOutputBuffer);
rc = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
&(trcpOutputBuffer[length]),
TRC_LINE_BUFFER_SIZE - length * sizeof(DCTCHAR),
NULL);
}
else {
DC_QUIT;
}
/************************************************************************/
/* Check the return code. */
/************************************************************************/
if (0 == rc)
{
hr = StringCchPrintf(trcpOutputBuffer + length,
TRC_LINE_BUFFER_SIZE - length -1,
_T("<FormatMessage> failed with rc %#hx"),
GetLastError());
if (FAILED(hr)) {
DC_QUIT;
}
}
else
{
/********************************************************************/
/* <FormatMessage> adds an additional '\r\n' to the end of the */
/* message string - however we don't need this so we strip it off. */
/********************************************************************/
length = DC_TSTRLEN(trcpOutputBuffer);
trcpOutputBuffer[length - 2] = _T('\0');
}
/************************************************************************/
/* Now call our internal trace buffer function to trace this message */
/* out. Note that we don't need to worry about freeing the mutex - */
/* <TRC_TraceBuffer> will do that for us. */
/************************************************************************/
TRC_TraceBuffer(TRC_LEVEL_ALT,
traceComponent,
lineNumber,
funcName,
fileName);
DC_EXIT_POINT:
/************************************************************************/
/* Finally we will restore the original value of last error. */
/************************************************************************/
SetLastError(lastError);
return;
} /* TRCSystemError */
/****************************************************************************/
/* FUNCTION: TRCOpenAllFiles(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Opens all the trace files. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : Function succeeded. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCOpenAllFiles(DCVOID)
{
DCUINT rc = 0;
DCUINT i;
DCUINT j;
/************************************************************************/
/* Now if we are the first process to attach then set up the trace */
/* indicator. This tells us which file is currently active (i.e. */
/* being used for trace output). We need to do this before we open the */
/* files as if they both exist already, they will both be created. */
/* File 2 will be created after file 1 and as we trace to the most */
/* recent file we will end up tracing to file 2 - and you don't want to */
/* do that! */
/************************************************************************/
if (trcCreatedTraceFiles)
{
TRCDetermineIndicator();
}
/************************************************************************/
/* Open all the trace output files. */
/************************************************************************/
for (i = 0; i < TRC_NUM_FILES; i++)
{
/********************************************************************/
/* Call TRCOpenSingleFile to open a single trace file. */
/********************************************************************/
rc = TRCOpenSingleFile(i);
if (0 != rc)
{
DC_QUIT;
}
}
/************************************************************************/
/* Finally, if we are the first process to attach, then set up the */
/* trace offset. This is the offset within the currently active trace */
/* file. */
/************************************************************************/
if (trcCreatedTraceFiles)
{
trcpSharedData->trcOffset =
TRCDetermineOffset(trcpSharedData->trcIndicator);
}
DC_EXIT_POINT:
if (0 != rc)
{
/********************************************************************/
/* Close any files that we may already have opened. We do not need */
/* to call TRCCloseSingleFile for the file which failed to open */
/* correctly as TRCOpenSingleFile will tidy up that file for us. */
/********************************************************************/
for (j = i; j > 0; j--)
{
TRCCloseSingleFile(j - 1, 0);
}
/********************************************************************/
/* Clear the trace-to-file flag - we can't do it. */
/********************************************************************/
CLEAR_FLAG(trcpConfig->flags, TRC_OPT_FILE_OUTPUT);
}
/************************************************************************/
/* Always return 0 to allow tracing to continue to the debugger. */
/************************************************************************/
return(0);
} /* TRCOpenAllFiles */
/****************************************************************************/
/* FUNCTION: TRCOpenSharedData(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function opens the shared data memory mapped file. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : Function succeeded */
/* TRC_RC_CREATE_MAPPING_FAILED : Failed to create the file mapping */
/* TRC_RC_MAP_VIEW_FAILED : MapViewOfFile failed. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCOpenSharedData(DCVOID)
{
DCUINT rc = 0;
#ifdef RUN_ON_WINNT
DWORD dwrc;
#endif
#ifndef OS_WINCE
OSVERSIONINFO ver;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
PSID psidEveryone = NULL;
SID_IDENTIFIER_AUTHORITY sidEveryoneAuthority = SECURITY_WORLD_SID_AUTHORITY;
DCUINT32 dwDaclLength;
PACL pDacl = NULL;
#endif
/************************************************************************/
/* Attempt to create the shared data memory mapped file. If this has */
/* already been created by another instance of this DLL then */
/* CreateFileMapping will simply return the handle of the existing */
/* object. Passing 0xFFFFFFFF creates a shared data memory mapped */
/* file. */
/************************************************************************/
#ifdef OS_WINCE
/************************************************************************/
/* For Windows CE, just use global data; always reset it. Note that */
/* this prevents shared use of the Trace DLL. */
/************************************************************************/
trchSharedDataObject = NULL;
trcpSharedData = &trcSharedData;
trcCreatedTraceFiles = TRUE;
#else
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&ver);
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
/************************************************************************/
/* Get the SID for the Everyone group */
/************************************************************************/
if (!AllocateAndInitializeSid (
&sidEveryoneAuthority, // pIdentifierAuthority
1, // count of subauthorities
SECURITY_WORLD_RID, // subauthority 0
0, 0, 0, 0, 0, 0, 0, // subauthorities n
&psidEveryone)) { // pointer to pointer to SID
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AllocateAndInitializeSid failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Allocate the Dacl */
/************************************************************************/
dwDaclLength = sizeof(ACL);
dwDaclLength += (sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD)) +
GetLengthSid(psidEveryone);
dwDaclLength += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) +
GetLengthSid(psidEveryone);
pDacl = (PACL)LocalAlloc(LMEM_FIXED, dwDaclLength);
if (pDacl == NULL) {
OutputDebugString(_T("Can't allocate Dacl.\n"));
rc = TRC_RC_MAP_VIEW_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Initialize it. */
/************************************************************************/
if (!InitializeAcl(pDacl, dwDaclLength, ACL_REVISION)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("InitializeAcl failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Allow all access */
/************************************************************************/
if (!AddAccessAllowedAce(
pDacl,
ACL_REVISION,
GENERIC_ALL,
psidEveryone)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AddAccessAllowedAce failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Block Write-DACL Access */
/************************************************************************/
if (!AddAccessDeniedAce(
pDacl,
ACL_REVISION,
WRITE_DAC,
psidEveryone)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AddAccessDeniedAceEx failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Create the File Mapping */
/************************************************************************/
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE);
sa.lpSecurityDescriptor = &sd;
trchSharedDataObject = CreateFileMapping(INVALID_HANDLE_VALUE,
&sa,
PAGE_READWRITE,
0,
sizeof(TRC_SHARED_DATA),
TRC_SHARED_DATA_NAME);
}
else {
trchSharedDataObject = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof(TRC_SHARED_DATA),
TRC_SHARED_DATA_NAME);
}
/************************************************************************/
/* Check that we succeeded in creating the file mapping. */
/************************************************************************/
if (NULL == trchSharedDataObject)
{
TRCDebugOutput(_T("NULL trchSharedDataObject.\n"));
rc = TRC_RC_CREATE_MAPPING_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Determine if the file mapping already exists - if it does then we */
/* won't bother reading the registry data in or setting up the file */
/* offset and indicator values. Note that up to this point */
/* <trcCreatedTraceFiles> has been set to TRUE. */
/************************************************************************/
if (ERROR_ALREADY_EXISTS == GetLastError())
{
trcCreatedTraceFiles = FALSE;
}
/************************************************************************/
/* We now have a handle to the shared data MMF which now needs to be */
/* mapped into our address space. Setting the third, fourth and fifth */
/* parameters of MapViewOfFile to zero maps the whole file into our */
/* address space starting with the first byte of the file. */
/************************************************************************/
trcpSharedData = (PTRC_SHARED_DATA) MapViewOfFile(trchSharedDataObject,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (NULL == trcpSharedData)
{
/********************************************************************/
/* Free up the handle to the file mapping object. */
/********************************************************************/
CloseHandle(trchSharedDataObject);
trchSharedDataObject = NULL;
/********************************************************************/
/* Output a debug string and then quit. */
/********************************************************************/
TRCDebugOutput(_T("NULL trcpSharedData.\n"));
rc = TRC_RC_MAP_VIEW_FAILED;
DC_QUIT;
}
#endif /* OS_WINCE */
/************************************************************************/
/* Set up our static pointer to the shared configuration data and to */
/* the filter data. */
/************************************************************************/
trcpConfig = &(trcpSharedData->trcConfig);
trcpFilter = &(trcpSharedData->trcFilter);
trcpOutputBuffer = trcpSharedData->trcpOutputBuffer;
/************************************************************************/
/* Finally initialize the shared data block and then read in the */
/* configuration data - but only if we are the first to open the file */
/* mapping. */
/************************************************************************/
if (trcCreatedTraceFiles)
{
/********************************************************************/
/* Initialize the shared data memory mapped file. */
/********************************************************************/
DC_MEMSET(trcpSharedData, 0, sizeof(TRC_SHARED_DATA));
/********************************************************************/
/* Initialize the internal status flags. The following flags apply */
/* to all the processes. */
/********************************************************************/
CLEAR_FLAG(trcpFilter->trcStatus, TRC_STATUS_ASSERT_DISPLAYED);
/********************************************************************/
/* The following flags are maintained on a per-process basis. */
/********************************************************************/
CLEAR_FLAG(trcProcessStatus, TRC_STATUS_SYMBOLS_LOADED);
/********************************************************************/
/* Read in the configuration data. */
/********************************************************************/
TRCReadSharedDataConfig();
/********************************************************************/
/* Now split the prefix list. */
/********************************************************************/
TRCSplitPrefixes();
}
DC_EXIT_POINT:
#ifndef OS_WINCE
if (trchSharedDataObject == NULL) {
if (pDacl) LocalFree(pDacl);
if (psidEveryone) FreeSid(psidEveryone);
}
#endif
return(rc);
} /* TRCOpenSharedData */
/****************************************************************************/
/* FUNCTION: TRCOpenSingleFile(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Opens a single trace memory mapped file. */
/* */
/* PARAMETERS: */
/* =========== */
/* fileNum : which file to open. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : Function succeeded */
/* TRC_RC_CREATE_FILE_FAILED : CreateFile call failed */
/* TRC_RC_MAP_VIEW_FAILED : MapViewOfFile failed */
/* TRC_RC_CREATE_MAPPING_FAILED : Failed to create the file mapping */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCOpenSingleFile(DCUINT fileNum)
{
DCUINT rc = 0;
DCBOOL blankFile = FALSE;
#ifndef OS_WINCE
DCTCHAR objectName[30];
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
PSID psidEveryone = NULL;
SID_IDENTIFIER_AUTHORITY sidEveryoneAuthority = SECURITY_WORLD_SID_AUTHORITY;
DCUINT32 dwDaclLength;
PACL pDacl = NULL;
OSVERSIONINFO ver;
HRESULT hr;
#endif
/************************************************************************/
/* Open a single trace file. First of all we attempt to open the file */
/* with read and write access, and shared read and write access. The */
/* OPEN_ALWAYS flag ensures that the file is created if it does not */
/* already exist. We pass NULL for the security attributes and */
/* template parameters (4 and 7). */
/************************************************************************/
trchFileObjects[fileNum] = CreateFile(trcpConfig->fileNames[fileNum],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
#ifndef OS_WINCE
OPEN_ALWAYS,
#else
CREATE_ALWAYS,
#endif
FILE_ATTRIBUTE_NORMAL,
NULL);
/************************************************************************/
/* Check that the handle returned by CreateFile is valid. For some */
/* peculiar reason it does return NULL if it fails - instead it returns */
/* -1 (INVALID_HANDLE_VALUE). */
/************************************************************************/
if (INVALID_HANDLE_VALUE == trchFileObjects[fileNum])
{
TRCDebugOutput(_T("Failed to open trace file.\n"));
rc = TRC_RC_CREATE_FILE_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Now check whether the file existed before the call to CreateFile. */
/* If it did then GetLastError returns ERROR_ALREADY_EXISTS (even */
/* though the function has succeeded). */
/************************************************************************/
if (0 == GetLastError())
{
/********************************************************************/
/* If the file did not exist before the call, GetLastError returns */
/* zero. In this case we want to fill the file with spaces. */
/********************************************************************/
blankFile = TRUE;
/********************************************************************/
/* We have just created the file - so would expect to need to set */
/* the security info to allow all accesses. However, a) all works */
/* just fine without it, b) the attempt to set the security stuff */
/* fails if inserted here. So we'll just go along happily without. */
/********************************************************************/
}
#ifdef OS_WINCE
SetFilePointer(trchFileObjects[fileNum],
0,
NULL,
FILE_END);
#else
/************************************************************************/
/* Make sure that the end of the file is correctly set. The file may */
/* be of any size when we open it, but we need it to be */
/* <trcpConfig->maxFileSize> bytes long. */
/************************************************************************/
SetFilePointer(trchFileObjects[fileNum],
trcpConfig->maxFileSize,
NULL,
FILE_BEGIN);
SetEndOfFile(trchFileObjects[fileNum]);
/************************************************************************/
/* Generate the file mapping object name. This is used in */
/* CreateFileMapping. */
/************************************************************************/
hr = StringCchPrintf(objectName,
SIZE_TCHARS(objectName),
TRC_TRACE_FILE_NAME _T("%hu"), fileNum);
if (FAILED(hr)) {
DC_QUIT;
}
/************************************************************************/
/* Now create the file mapping object. Again ignore security */
/* attributes (parameter 2) and set the high order 32 bits of the */
/* object size to 0 (see Win32 SDK for more information). */
/* */
/* Create the file mapping object using a NULL Dacl so it works for all */
/* contexts. */
/************************************************************************/
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&ver);
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
/************************************************************************/
/* Get the SID for the Everyone group */
/************************************************************************/
if (!AllocateAndInitializeSid (
&sidEveryoneAuthority, // pIdentifierAuthority
1, // count of subauthorities
SECURITY_WORLD_RID, // subauthority 0
0, 0, 0, 0, 0, 0, 0, // subauthorities n
&psidEveryone)) { // pointer to pointer to SID
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AllocateAndInitializeSid failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Allocate the Dacl */
/************************************************************************/
dwDaclLength = sizeof(ACL);
dwDaclLength += (sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD)) +
GetLengthSid(psidEveryone);
dwDaclLength += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) +
GetLengthSid(psidEveryone);
pDacl = (PACL)LocalAlloc(LMEM_FIXED, dwDaclLength);
if (pDacl == NULL) {
OutputDebugString(_T("Can't allocate Dacl.\n"));
rc = TRC_RC_MAP_VIEW_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Initialize it. */
/************************************************************************/
if (!InitializeAcl(pDacl, dwDaclLength, ACL_REVISION)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("InitializeAcl failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Allow all access */
/************************************************************************/
if (!AddAccessAllowedAce(
pDacl,
ACL_REVISION,
GENERIC_ALL,
psidEveryone)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AddAccessAllowedAce failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Block Write-DACL Access */
/************************************************************************/
if (!AddAccessDeniedAce(
pDacl,
ACL_REVISION,
WRITE_DAC,
psidEveryone)) {
rc = TRC_RC_MAP_VIEW_FAILED;
OutputDebugString(_T("AddAccessDeniedAceEx failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Create the File Mapping
/************************************************************************/
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE);
sa.lpSecurityDescriptor = &sd;
trchMappingObjects[fileNum] = CreateFileMapping(trchFileObjects[fileNum],
&sa,
PAGE_READWRITE,
0,
trcpConfig->maxFileSize,
objectName);
}
else {
trchMappingObjects[fileNum] = CreateFileMapping(trchFileObjects[fileNum],
NULL,
PAGE_READWRITE,
0,
trcpConfig->maxFileSize,
objectName);
}
/************************************************************************/
/* Check that we succeeded in creating the file mapping object. */
/* CreateFileMapping returns NULL if it fails. */
/************************************************************************/
if (NULL == trchMappingObjects[fileNum])
{
TRCDebugOutput(_T("Failed to map trace file.\n"));
rc = TRC_RC_CREATE_MAPPING_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Now map a view of the file. Set the low and high order offsets to */
/* zero (parameters 3 and 4). */
/************************************************************************/
trcpFiles[fileNum] = (PDCTCHAR)MapViewOfFile(trchMappingObjects[fileNum],
FILE_MAP_ALL_ACCESS,
0,
0,
trcpConfig->maxFileSize);
/************************************************************************/
/* Check that we mapped a view of the file. */
/************************************************************************/
if (NULL == trcpFiles[fileNum])
{
TRCDebugOutput(_T("Failed to map view of trace file.\n"));
rc = TRC_RC_MAP_VIEW_FAILED;
DC_QUIT;
}
/************************************************************************/
/* Finally check to see if we need to blank this file. */
/************************************************************************/
if (blankFile)
{
TRCBlankFile(fileNum);
}
#endif
DC_EXIT_POINT:
/************************************************************************/
/* If the return code is non-zero then we need to perform some tidying */
/* up. */
/************************************************************************/
if (0 != rc)
{
#ifndef OS_WINCE
/********************************************************************/
/* Check whether we need to free the handle to the file mapping */
/* object. */
/********************************************************************/
if (NULL != trchMappingObjects[fileNum])
{
CloseHandle(trchMappingObjects[fileNum]);
trchMappingObjects[fileNum] = NULL;
}
#endif
/********************************************************************/
/* Check whether we need to free the handle to the file object. */
/********************************************************************/
if (NULL != trchFileObjects[fileNum])
{
CloseHandle(trchFileObjects[fileNum]);
trchFileObjects[fileNum] = NULL;
}
}
return(rc);
} /* TRCOpenSingleFile */
/****************************************************************************/
/* FUNCTION: TRCOutputToFile(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function writes a string to the trace file. It is used to trace */
/* both normal trace lines and stack trace lines. */
/* */
/* PARAMETERS: */
/* =========== */
/* pText : a pointer to the trace text string. */
/* length : length of the string. */
/* traceLevel : the current trace level. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCOutputToFile(PDCTCHAR pText,
DCUINT length,
DCUINT traceLevel)
{
#ifndef OS_WINCE
PDCUINT8 pFilePos;
/************************************************************************/
/* Make sure we have a trace file with enough free space. */
/************************************************************************/
TRCMaybeSwapFile(length);
/************************************************************************/
/* We can now write out the trace line. */
/************************************************************************/
pFilePos = (PDCUINT8)trcpFiles[trcpSharedData->trcIndicator] +
trcpSharedData->trcOffset;
DC_MEMCPY(pFilePos, pText, length);
/************************************************************************/
/* Check if we should flush this line to disk immediately. If this is */
/* an error or higher level trace then flush to disk regardless. */
/************************************************************************/
if ((TRUE == TEST_FLAG(trcpConfig->flags, TRC_OPT_FLUSH_ON_TRACE)) ||
(traceLevel >= TRC_LEVEL_ERR))
{
FlushViewOfFile(pFilePos, length);
}
/************************************************************************/
/* Finally update the offset. */
/************************************************************************/
trcpSharedData->trcOffset += length;
#else
DWORD dwRet;
WriteFile(trchFileObjects[0], pText, length, &dwRet, NULL);
#endif
DC_EXIT_POINT:
return;
} /* TRCOutputToFile */
/****************************************************************************/
/* FUNCTION: TRCReadEntry(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Read an entry from the given section of the registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* topLevelKey : one of: */
/* - HKEY_CURRENT_USER */
/* - HKEY_LOCAL_MACHINE */
/* pSection : the section name to read from. The DC_REG_PREFIX */
/* string is prepended to give the full name. */
/* pEntry : the entry name to read. */
/* pBuffer : a buffer to read the entry to. */
/* bufferSize : the size of the buffer. */
/* expectedDataType : the type of data stored in the entry. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCReadEntry(HKEY topLevelKey,
PDCTCHAR pEntry,
PDCTCHAR pBuffer,
DCINT bufferSize,
DCINT32 expectedDataType)
{
LONG sysrc;
HKEY key;
DCINT32 dataType;
DCINT32 dataSize;
DCTCHAR subKey[TRC_MAX_SUBKEY];
DCBOOL keyOpen = FALSE;
DCUINT rc = 0;
HRESULT hr;
/************************************************************************/
/* Get a subkey for the value. */
/************************************************************************/
hr = StringCchCopy(subKey,
SIZE_TCHARS(subKey),
TRC_SUBKEY_NAME);
if (FAILED(hr)) {
DC_QUIT;
}
/************************************************************************/
/* Try to open the key. If the entry does not exist, RegOpenKeyEx will */
/* fail. */
/************************************************************************/
sysrc = RegOpenKeyEx(topLevelKey,
subKey,
0, /* reserved */
KEY_ALL_ACCESS,
&key);
if (ERROR_SUCCESS != sysrc)
{
/********************************************************************/
/* Don't trace an error here since the subkey may not exist... */
/********************************************************************/
rc = TRC_RC_IO_ERROR;
DC_QUIT;
}
keyOpen = TRUE;
/************************************************************************/
/* We successfully opened the key so now try to read the value. Again */
/* it may not exist. */
/************************************************************************/
dataSize = (DCINT32)bufferSize;
sysrc = RegQueryValueEx(key,
pEntry,
0, /* reserved */
(LPDWORD) &dataType,
(LPBYTE) pBuffer,
(LPDWORD) &dataSize);
if (sysrc != ERROR_SUCCESS)
{
rc = TRC_RC_IO_ERROR;
DC_QUIT;
}
/************************************************************************/
/* Check that the type is correct. Special case: allow REG_BINARY */
/* instead of REG_DWORD, as long as the length is 32 bits. */
/************************************************************************/
if ((dataType != expectedDataType) &&
((dataType != REG_BINARY) ||
(expectedDataType != REG_DWORD) ||
(dataSize != 4)))
{
rc = TRC_RC_IO_ERROR;
DC_QUIT;
}
DC_EXIT_POINT:
/************************************************************************/
/* Close the key (if required). */
/************************************************************************/
if (keyOpen)
{
sysrc = RegCloseKey(key);
if (ERROR_SUCCESS != sysrc)
{
TRCDebugOutput(_T("Failed to close key.\n"));
}
}
return(rc);
} /* TRCReadEntry */
/****************************************************************************/
/* FUNCTION: TRCStackTrace(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* */
/* PARAMETERS: */
/* =========== */
/* traceLevel : the current trace level which is used to determine */
/* whether this line should be flushed to disk */
/* immediately. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCStackTrace(DCUINT traceLevel)
{
DC_IGNORE_PARAMETER(traceLevel);
#ifdef DO_STACK_TRACE
HANDLE hProcess;
HANDLE hThread;
DCBOOL sysrc;
STACKFRAME stackFrame;
DWORD machineType;
IMAGEHLP_MODULE moduleInfo;
DCINT i;
DCTCHAR formatString[TRC_FRMT_BUFFER_SIZE];
CONTEXT threadContext;
CHAR symBuffer[sizeof(IMAGEHLP_SYMBOL)+TRC_MAX_SYMNAME_SIZE];
PIMAGEHLP_SYMBOL pSymbol;
PCHAR pFuncName;
DWORD displacement = 0;
DCBOOL foundTrace = FALSE;
/************************************************************************/
/* First of all ensure that stack tracing is enabled - if it is not */
/* then just return. */
/************************************************************************/
/************************************************************************/
/* The stack trace code doesn't work for Alpha so don't bother trying. */
/************************************************************************/
#ifndef _M_ALPHA
if (!TEST_FLAG(trcpConfig->flags, TRC_OPT_STACK_TRACING))
#endif
{
DC_QUIT;
}
/************************************************************************/
/* Set <pSymbol> to point to the symbol buffer. */
/************************************************************************/
pSymbol = (PIMAGEHLP_SYMBOL) symBuffer;
/************************************************************************/
/* Zero memory structures. */
/************************************************************************/
ZeroMemory(&stackFrame, sizeof(stackFrame));
ZeroMemory(pSymbol, sizeof(IMAGEHLP_SYMBOL));
ZeroMemory(&threadContext, sizeof(CONTEXT));
/************************************************************************/
/* Initialize the symbol buffer. */
/************************************************************************/
pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
pSymbol->MaxNameLength = 1024;
/************************************************************************/
/* Get handles to the current process and thread. */
/************************************************************************/
hProcess = GetCurrentProcess();
hThread = GetCurrentThread();
/************************************************************************/
/* We need to get the values of the base pointer, stack pointer and the */
/* instruction pointer. We can use <GetContextThread> to return this */
/* information - but first of all we need to set the <ContextFlags> */
/* member of the <threadContext> struture to return the control */
/* registers. */
/************************************************************************/
threadContext.ContextFlags = CONTEXT_CONTROL;
/************************************************************************/
/* Now attempt to get the thread context. */
/************************************************************************/
if (!GetThreadContext(hThread, &threadContext))
{
/********************************************************************/
/* If <GetThreadContext> failed then there is not a lot we can do */
/* so just quit. */
/********************************************************************/
TRCInternalError(_T("GetThreadContext failed.\n"));
DC_QUIT;
}
/************************************************************************/
/* Store the instruction pointer in the <stackFrame> structure. */
/************************************************************************/
stackFrame.AddrPC.Mode = AddrModeFlat;
/************************************************************************/
/* Processor dependant section. We set the image file type here and if */
/* we are running on Intel hardware we also store the stack pointer and */
/* base pointer. */
/************************************************************************/
#if defined(_M_IX86)
machineType = IMAGE_FILE_MACHINE_I386;
stackFrame.AddrPC.Offset = threadContext.Eip;
stackFrame.AddrFrame.Offset = threadContext.Ebp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = threadContext.Esp;
stackFrame.AddrStack.Mode = AddrModeFlat;
#elif defined (_M_MRX000)
machineType = IMAGE_FILE_MACHINE_R4000;
#elif defined (_M_ALPHA)
machineType = IMAGE_FILE_MACHINE_ALPHA;
#elif defined (_M_PPC)
machineType = IMAGE_FILE_MACHINE_POWERPC;
#else
#error("Unknown machine type.");
#endif
/************************************************************************/
/* Now run down the stack. */
/************************************************************************/
for (i = 1; i < TRC_MAX_SIZE_STACK_TRACE; i++)
{
/********************************************************************/
/* Call <StackWalk> to start walking the stack. */
/********************************************************************/
sysrc = StackWalk(machineType,
hProcess,
hThread,
&stackFrame,
&threadContext,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
/********************************************************************/
/* Check the return code. */
/********************************************************************/
if (FALSE == sysrc)
{
/****************************************************************/
/* Don't trace anything here as we enter here when we reach */
/* the end of the stack. */
/****************************************************************/
DC_QUIT;
}
/********************************************************************/
/* Get the module name. */
/********************************************************************/
sysrc = SymGetModuleInfo(hProcess,
stackFrame.AddrPC.Offset,
&moduleInfo);
/********************************************************************/
/* Check the return code. */
/********************************************************************/
if (FALSE == sysrc)
{
/****************************************************************/
/* Don't trace anything as we enter here when we reach the end */
/* of the stack. */
/****************************************************************/
DC_QUIT;
}
/********************************************************************/
/* When we start we are somewhere in the midst of */
/* <GetThreadContext>. Since we're only interested in the stack */
/* above the trace module then we need to skip everything until we */
/* pass the trace module. */
/* */
/* Look for the trace module name. */
/********************************************************************/
if (DC_TSTRCMPI(trcpSharedData->trcpModuleFileName,
moduleInfo.ModuleName) == 0)
{
/****************************************************************/
/* They match so set the <foundTrace> flag and the continue. */
/****************************************************************/
foundTrace = TRUE;
continue;
}
/********************************************************************/
/* We've not found the trace module yet so just continue. */
/********************************************************************/
if (!foundTrace)
{
continue;
}
/********************************************************************/
/* Now get the symbol name. */
/********************************************************************/
sysrc = SymGetSymFromAddr(hProcess,
stackFrame.AddrPC.Offset,
&displacement,
pSymbol);
/********************************************************************/
/* Check the return code. */
/********************************************************************/
if (sysrc)
{
/****************************************************************/
/* We've found some symbols so use them. */
/****************************************************************/
pFuncName = pSymbol->Name;
}
else
{
/****************************************************************/
/* No symbols available. */
/****************************************************************/
pFuncName = _T("<nosymbols>");
}
/********************************************************************/
/* Finally format the string. */
/********************************************************************/
hr = StringCchPrintf(
formatString,
SIZE_TCHARS(formatString),
_T(" ") TRC_MODL_FMT _T("!") TRC_FUNC_FMT _T(" : ") TRC_STCK_FMT_T("\r\n"),
moduleInfo.ModuleName,
trcpConfig->funcNameLength,
trcpConfig->funcNameLength,
pFuncName,
displacement,
stackFrame.AddrFrame.Offset,
stackFrame.AddrReturn.Offset,
stackFrame.Params[0],
stackFrame.Params[1],
stackFrame.Params[2],
stackFrame.Params[3]
);
if (SUCCEEDED(hr)) {
/********************************************************************/
/* Output this line of the <formatString>. */
/********************************************************************/
TRCOutput(formatString, DC_TSTRLEN(formatString), traceLevel);
}
}
DC_EXIT_POINT:
return;
#endif /* DO_STACK_TRACE */
} /* TRCStackTrace */
/****************************************************************************/
/* FUNCTION: TRCSymbolsLoad(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Function to load symbolic debugging information. This function should */
/* only be called if the trace mutex has been obtained. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success. */
/* TRC_SYMBOL_LOAD_FAILED : failed to load symbols. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCSymbolsLoad(DCVOID)
{
DCUINT rc = 0;
HANDLE hProcess;
#ifdef DO_STACK_TRACE
DWORD options;
#endif
/************************************************************************/
/* Get the current process handle. */
/************************************************************************/
hProcess = GetCurrentProcess();
/************************************************************************/
/* We're about to load symbols - so trace a line out. */
/************************************************************************/
TRCInternalTrace(TRC_SYMBOLS_LOADING_NOTIFY);
#ifdef DO_STACK_TRACE
/************************************************************************/
/* Now set the deferred symbol load option. For some peculiar reason */
/* this is not set by default. */
/************************************************************************/
options = SymGetOptions();
SymSetOptions(options | SYMOPT_DEFERRED_LOADS);
/************************************************************************/
/* Initialize the symbol handler for this process. By setting param 2 */
/* to NULL the search path for the symbols is as follows: */
/* */
/* - Current directory */
/* - Env variable _NT_SYMBOL_PATH */
/* - Env variable _NT_ALTERNATE_SYMBOL_PATH */
/* - Env variable SYSTEMROOT */
/* */
/* By setting the third parameter to TRUE we tell IMAGEHLP to enumerate */
/* the loaded modules for this process (this effectively calls */
/* <SymLoadModule> for each module). */
/************************************************************************/
/************************************************************************/
/* LAURABU: */
/* SymInitialize returns FALSE on Win95. Moreover, it makes no sense */
/* to fail to start up on either NT or Win95 just because this dll */
/* couldn't load debug symbols. Therefore don't fail. */
/************************************************************************/
if (!(SymInitialize(hProcess, NULL, TRUE)))
{
#ifdef DC_OMIT
rc = TRC_RC_SYMBOL_LOAD_FAILED;
#endif
TRCDebugOutput(_T("SymInitialize failed.\n"));
#ifdef DC_OMIT
DC_QUIT;
#endif
}
#endif
/************************************************************************/
/* Set the flag to indicate the symbols have been loaded. */
/************************************************************************/
SET_FLAG(trcProcessStatus, TRC_STATUS_SYMBOLS_LOADED);
/************************************************************************/
/* Write a status line. The assumption here is that this is done under */
/* the mutex. */
/************************************************************************/
TRCInternalTrace(TRC_SYMBOLS_LOADED_NOTIFY);
DC_EXIT_POINT:
return(rc);
} /* TRCSymbolsLoad */
/****************************************************************************/
/* FUNCTION: TRCSymbolsUnload(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* TRUE if successful and FALSE otherwise. */
/* */
/****************************************************************************/
DCBOOL DCINTERNAL TRCSymbolsUnload(DCVOID)
{
DCBOOL rc = TRUE;
#ifdef DO_STACK_TRACE
HANDLE hProcess;
/************************************************************************/
/* Get the current process handle. */
/************************************************************************/
hProcess = GetCurrentProcess();
/************************************************************************/
/* Cleanup the symbols. */
/************************************************************************/
rc = SymCleanup(hProcess);
/************************************************************************/
/* Check the return code. */
/************************************************************************/
if (FALSE == rc)
{
TRCDebugOutput(_T("SymCleanup failed.\n"));
DC_QUIT;
}
#endif
/************************************************************************/
/* Clear the symbols loaded flag. */
/************************************************************************/
CLEAR_FLAG(trcProcessStatus, TRC_STATUS_SYMBOLS_LOADED);
/************************************************************************/
/* Write a status line to the trace file. The assumption here is that */
/* this is done under the mutex. */
/************************************************************************/
TRCInternalTrace(TRC_SYMBOLS_UNLOAD_NOTIFY);
DC_EXIT_POINT:
return(rc);
} /* TRCSymbolsLoad */
/****************************************************************************/
/* FUNCTION: TRCWriteEntry(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* Write an entry to the given section of the registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* topLevelKey : one of: */
/* - HKEY_CURRENT_USER */
/* - HKEY_LOCAL_MACHINE */
/* pEntry : the entry name to write. */
/* pData : a pointer to the data to be written. */
/* dataSize : the size of the data to be written. For strings, this */
/* should include the NULL terminator. */
/* dataType : the type of the data to be written. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success */
/* TRC_RC_IO_ERROR : I/O error. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCWriteEntry(HKEY topLevelKey,
PDCTCHAR pEntry,
PDCTCHAR pData,
DCINT dataSize,
DCINT32 dataType)
{
LONG sysrc;
HKEY key;
DCTCHAR subKey[TRC_MAX_SUBKEY];
DWORD disposition;
DCBOOL keyOpen = FALSE;
DCUINT rc = 0;
HRESULT hr;
/************************************************************************/
/* Get a subkey for the value. */
/************************************************************************/
hr = StringCchCopy(subKey,
SIZE_TCHARS(subKey),
TRC_SUBKEY_NAME);
if (FAILED(hr)) {
DC_QUIT;
}
/************************************************************************/
/* Try to create the key. If the entry already exists, RegCreateKeyEx */
/* will open the existing entry. */
/************************************************************************/
sysrc = RegCreateKeyEx(topLevelKey,
subKey,
0, /* reserved */
NULL, /* class */
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL, /* security attributes */
&key,
&disposition);
if (ERROR_SUCCESS != sysrc)
{
DCTCHAR output[12];
TRCDebugOutput(_T("Failed to create key failed with error "));
hr = StringCchPrintf(output, SIZE_TCHARS(output),
_T("%#lx"), GetLastError());
if (SUCCEEDED(hr)) {
TRCDebugOutput(output);
}
DC_QUIT;
}
keyOpen = TRUE;
/************************************************************************/
/* We've got the key, so set the value. */
/************************************************************************/
sysrc = RegSetValueEx(key,
pEntry,
0, /* reserved */
dataType,
(LPBYTE) pData,
(DCINT32) dataSize);
if (ERROR_SUCCESS != sysrc)
{
DCTCHAR output[12];
TRCDebugOutput(_T("Failed to set value failed with error "));
hr = StringCchPrintf(output, SIZE_TCHARS(output),
_T("%#lx"), GetLastError());
if (SUCCEEDED(hr)) {
TRCDebugOutput(output);
}
DC_QUIT;
}
DC_EXIT_POINT:
/************************************************************************/
/* Close the key (if required) */
/************************************************************************/
if (keyOpen)
{
sysrc = RegCloseKey(key);
if (ERROR_SUCCESS != sysrc)
{
TRCDebugOutput(_T("Failed to close key.\n"));
}
}
return(rc);
} /* TRCWriteEntry */
/****************************************************************************/
/* We have our own implementation of DebugBreak that on NT checks if a */
/* debugger is present first before calling DebugBreak(). Otherwise the */
/* app will just get terminated due to an unhandled exception. */
/****************************************************************************/
typedef BOOL (WINAPI * PFN_ISDEBUGGERPRESENT)(void);
DCVOID DCINTERNAL TRCDebugBreak(DCVOID)
{
static PFN_ISDEBUGGERPRESENT s_pfnIsDebuggerPresent = NULL;
static BOOL s_fHaveWeTriedToFindIt = FALSE;
if (! s_pfnIsDebuggerPresent)
{
if (!InterlockedExchange((long *)&s_fHaveWeTriedToFindIt, TRUE))
{
/****************************************************************/
/* Try to get the proc address of "IsDebuggerPresent". Note we */
/* can just write into this variable without Interlocked stuff */
/* since dwords get written to and read from atomically. */
/****************************************************************/
#ifndef OS_WINCE
s_pfnIsDebuggerPresent = (PFN_ISDEBUGGERPRESENT)
GetProcAddress(GetModuleHandle(_T("kernel32.dll")),
"IsDebuggerPresent");
#else // OS_WINCE
HMODULE hmod;
hmod = LoadLibrary(_T("kernel32.dll"));
s_pfnIsDebuggerPresent = (PFN_ISDEBUGGERPRESENT)
GetProcAddress(hmod,
_T("IsDebuggerPresent"));
FreeLibrary(hmod);
#endif
}
}
/************************************************************************/
/* If this api doesn't exist, we are on Win95, so go ahead and call */
/* DebugBreak(). If it does, we are on NT 4, so find out if a debugger */
/* is around. If a debugger isn't there, then don't break for now */
/* since we don't have debuggers attached to most of our NT machines */
/* yet. */
/************************************************************************/
if (!s_pfnIsDebuggerPresent || (s_pfnIsDebuggerPresent()))
DebugBreak();
}