|
|
/****************************************************************************/ /* 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(); }
|