windows-nt/Source/XPSP1/NT/termsrv/common/trace/wtrcint.c
2020-09-26 16:20:57 +08:00

856 lines
40 KiB
C

/**MOD+**********************************************************************/
/* Module: wtrcint.c */
/* */
/* Purpose: Internal tracing functions - Windows specific. */
/* */
/* Copyright(C) Microsoft Corporation 1997 */
/* */
/****************************************************************************/
/** Changes:
* $Log: Y:/logs/trc/wtrcint.c_v $
*
* Rev 1.10 22 Aug 1997 15:11:48 SJ
* SFR1291: Win16 Trace DLL doesn't write integers to ini file properly
*
* Rev 1.9 09 Jul 1997 18:03:42 AK
* SFR1016: Initial changes to support Unicode
*
* Rev 1.8 03 Jul 1997 13:29:04 AK
* SFR0000: Initial development completed
**/
/**MOD-**********************************************************************/
/****************************************************************************/
/* */
/* INCLUDES */
/* */
/****************************************************************************/
#include <adcg.h>
/****************************************************************************/
/* Define TRC_FILE and TRC_GROUP. */
/****************************************************************************/
#define TRC_FILE "wtrcint"
#define TRC_GROUP TRC_GROUP_TRACE
/****************************************************************************/
/* Trace specific includes. */
/* */
/* Note that including atrcapi.h automatically includes wtrcapi.h for us. */
/****************************************************************************/
#include <atrcapi.h>
#include <atrcint.h>
#include <wtrcrc.h>
#include <ndcgver.h>
/****************************************************************************/
/* */
/* DATA */
/* */
/****************************************************************************/
#define DC_INCLUDE_DATA
#include <atrcdata.c>
#undef DC_INCLUDE_DATA
/****************************************************************************/
/* */
/* FUNCTIONS */
/* */
/****************************************************************************/
/****************************************************************************/
/* FUNCTION: TRCGetModuleFileName(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function gets the DLL module file name, without path or extension. */
/* Global trchModule must contain the library module handle (WIN32) or */
/* instance handle (WIN16). */
/* */
/* PARAMETERS: */
/* =========== */
/* pModuleName : address of buffer into which the module name is written. */
/* */
/* RETURNS: */
/* ======== */
/* DC_RC_OK is successful, error code otherwise. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCGetModuleFileName(PDCTCHAR pModuleName,
UINT cchModuleName)
{
DCINT rc = DC_RC_OK;
PDCTCHAR pTemp;
PDCTCHAR pName;
DCTCHAR pModuleFileName[TRC_FILE_NAME_SIZE];
HRESULT hr;
/************************************************************************/
/* Get the trace DLL module file name. We use this later when we get a */
/* stack trace. */
/************************************************************************/
if ( GetModuleFileName(trchModule,
pModuleFileName,
TRC_FILE_NAME_SIZE) != 0 )
{
/********************************************************************/
/* The module file name is currently in the form of a complete */
/* path - however we only want the actual module name. */
/********************************************************************/
pName = pModuleFileName;
pTemp = DC_TSTRCHR(pName, _T('\\'));
while (NULL != pTemp)
{
pName = pTemp + 1;
pTemp = DC_TSTRCHR(pName, _T('\\'));
}
/********************************************************************/
/* Now remove the file name extension - we do this by replacing */
/* the decimal point with a null. */
/********************************************************************/
pTemp = DC_TSTRCHR(pName, _T('.'));
if (NULL != pTemp)
{
*pTemp = _T('\0');
}
/********************************************************************/
/* Finally copy what remains into the caller's buffer */
/********************************************************************/
hr = StringCchCopy(pModuleName, cchModuleName, pName);
if (FAILED(hr)) {
rc = TRC_RC_IO_ERROR;
}
}
else
{
rc = TRC_RC_IO_ERROR;
}
return(rc);
}
/**PROC+*********************************************************************/
/* Name: TRCAssertDlgProc */
/* */
/* Purpose: Dialog Proc for assert box */
/* */
/* Returns: TRUE / FALSE */
/* */
/* Params: IN usual Windows parameters */
/* */
/**PROC-*********************************************************************/
INT_PTR CALLBACK TRCAssertDlgProc(HWND hwndDlg,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
INT_PTR rc = FALSE;
RECT rect;
DCINT xPos;
DCINT yPos;
PDCTCHAR pText;
switch (msg)
{
case WM_INITDIALOG:
{
/****************************************************************/
/* Set the text */
/****************************************************************/
pText = (PDCTCHAR)lParam;
SetDlgItemText(hwndDlg, TRC_ID_TEXT, pText);
SetWindowText(hwndDlg, TRC_ASSERT_TITLE);
/****************************************************************/
/* Center on the screen, and set to topmost. */
/****************************************************************/
GetWindowRect(hwndDlg, &rect);
xPos = ( GetSystemMetrics(SM_CXSCREEN) -
(rect.right - rect.left)) / 2;
yPos = ( GetSystemMetrics(SM_CYSCREEN) -
(rect.bottom - rect.top)) / 2;
SetWindowPos(hwndDlg,
HWND_TOPMOST,
xPos, yPos,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE);
rc = TRUE;
}
break;
case WM_COMMAND:
{
switch(DC_GET_WM_COMMAND_ID(wParam))
{
case IDABORT:
case IDRETRY:
case IDIGNORE:
{
PostMessage(hwndDlg,
WM_USER + DC_GET_WM_COMMAND_ID(wParam),
0, 0);
rc = TRUE;
}
break;
default:
{
/********************************************************/
/* Ignore other messages */
/********************************************************/
}
break;
}
}
case WM_CLOSE:
{
/****************************************************************/
/* If 'x' selected, treat as 'Ignore' */
/****************************************************************/
PostMessage(hwndDlg, WM_USER + IDIGNORE, 0, 0);
}
break;
default:
{
/****************************************************************/
/* Ignore */
/****************************************************************/
}
break;
}
return(rc);
} /* TRCAssertDlgProc */
/****************************************************************************/
/* FUNCTION: TRCDisplayAssertBox(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function displays an assert box and then decides (based on the user */
/* action) whether to kill the thread, jump into a debugger or just ignore */
/* the assert. */
/* */
/* PARAMETERS: */
/* =========== */
/* pText : a pointer to the null-terminated assert text string. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCDisplayAssertBox(PDCTCHAR pText)
{
HWND hwndDlg;
MSG msg;
DCINT rc;
HRESULT hr;
TCHAR szFormattedText[TRC_FRMT_BUFFER_SIZE];
/************************************************************************/
/* If we are not currently displaying an assert dialog box then display */
/* one. This function will display an assert box and then handle the */
/* user action (i.e. whether we kill the thread, jump into the */
/* debugger or just ignore (!) the assert). */
/* */
/* Note that the testing and setting of the flag is not done under a */
/* mutex and therefore can potentially be preempted. There is */
/* therefore the possibility that multiple threads can assert */
/* simulataneously (a rare occurance) and thus we end up with multiple */
/* assert dialogs on the screen. However we avoid the cascading assert */
/* problem. */
/************************************************************************/
if (TEST_FLAG(trcpFilter->trcStatus, TRC_STATUS_ASSERT_DISPLAYED))
{
DC_QUIT;
}
/************************************************************************/
/* Set the flag to indicate that an assert is currently displayed, */
/* display the assert and then clear the flag. */
/************************************************************************/
SET_FLAG(trcpFilter->trcStatus, TRC_STATUS_ASSERT_DISPLAYED);
/************************************************************************/
/* To prevent re-entrancy, do not use MessageBox. Create a dialog and */
/* use a message loop to handle this until it has been dismissed. Note */
/* that this will block the thread which issued the assert. */
/* Pass the assert text to the dialog's WM_INITDDIALOG callback. */
/************************************************************************/
hwndDlg = CreateDialogParam(trchModule,
MAKEINTRESOURCE(TRC_IDD_ASSERT),
NULL,
TRCAssertDlgProc,
(LPARAM)(pText));
if (hwndDlg == NULL)
{
/********************************************************************/
/* Use Message Box - but note that this will give reentrancy */
/* problems. Since the choice on this dialog is */
/* Abort/Retry/Ignore, we add an explanatory message to the effect */
/* that 'Retry' is really 'Debug'. */
/********************************************************************/
hr = StringCchPrintf(szFormattedText,
SIZE_TCHARS(szFormattedText),
_T("%s %s"),
pText,
TRC_ASSERT_TEXT2);
if (SUCCEEDED(hr)) {
rc = MessageBox(NULL,
pText,
TRC_ASSERT_TITLE,
MB_ABORTRETRYIGNORE | MB_ICONSTOP |
MB_SETFOREGROUND);
}
else {
DC_QUIT;
}
}
else
{
/********************************************************************/
/* Show the dialog. */
/********************************************************************/
ShowWindow(hwndDlg, SW_SHOW);
/********************************************************************/
/* Only pull off messages for this dialog. */
/********************************************************************/
while (GetMessage (&msg, hwndDlg, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
/****************************************************************/
/* WM_USER + ID??? is used to terminate processing. */
/****************************************************************/
if (msg.message >= WM_USER)
{
/************************************************************/
/* finished */
/************************************************************/
EndDialog(hwndDlg, IDOK);
break;
}
}
/********************************************************************/
/* Get the return code from the message ID */
/********************************************************************/
if (msg.message >= WM_USER)
{
rc = msg.message - WM_USER;
}
else
{
/****************************************************************/
/* WM_QUIT - treat as an Abort. */
/****************************************************************/
rc = IDABORT;
}
}
/************************************************************************/
/* Now that the assert box is no more, clear the flag. */
/************************************************************************/
CLEAR_FLAG(trcpFilter->trcStatus, TRC_STATUS_ASSERT_DISPLAYED);
/************************************************************************/
/* Switch on the return code from MessageBox. */
/************************************************************************/
switch (rc)
{
case IDABORT:
{
/****************************************************************/
/* Abort selected - so exit the current thread. */
/****************************************************************/
TRCExitProcess(TRC_THREAD_EXIT);
}
break;
case IDRETRY:
{
/****************************************************************/
/* Retry selected - jump into the debugger if JIT (Just In */
/* Time) debugging is enabled. */
/****************************************************************/
DebugBreak();
}
break;
case IDIGNORE:
{
/****************************************************************/
/* Ignore selected - just blindly carry on... */
/****************************************************************/
}
break;
}
DC_EXIT_POINT:
return;
} /* TRCDisplayAssertBox */
/****************************************************************************/
/* FUNCTION: TRCInternalTrace(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function writes a string to the debugger on every process attach */
/* detach. Note that in general the mutex will not have been obtained */
/* when this function is called. */
/* */
/* The problem with this function is that DllMain will call this function */
/* every time a thread attaches / detaches at which point it has the */
/* process critical section. However we may be in the middle of a stack */
/* trace on another thread and holding the trace mutex. Stack tracing */
/* requires the process critical section while holding the trace mutex */
/* which deadlocks if DllMain is waiting on the trace mutex. */
/* */
/* PARAMETERS: */
/* =========== */
/* type : is this an process/thread attach/detach or a symbols */
/* loading/loaded/unloaded. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCInternalTrace(DCUINT32 type)
{
PDCTCHAR pStatus;
DC_DATE theDate;
DC_TIME theTime;
DCUINT32 processId;
DCUINT32 threadId;
DCUINT32 length;
DCTCHAR szOutputBuffer[TRC_FRMT_BUFFER_SIZE];
HRESULT hr;
/************************************************************************/
/* Determine whether this is an attach or a detach. */
/************************************************************************/
switch (type)
{
case TRC_TRACE_DLL_INITIALIZE:
{
pStatus = _T("Trace initialized");
}
break;
case TRC_TRACE_DLL_TERMINATE:
{
pStatus = _T("Trace terminated ");
}
break;
case TRC_PROCESS_ATTACH_NOTIFY:
{
pStatus = _T("Process attached ");
}
break;
case TRC_PROCESS_DETACH_NOTIFY:
{
pStatus = _T("Process detached ");
}
break;
case TRC_THREAD_ATTACH_NOTIFY:
{
pStatus = _T("Thread attached ");
}
break;
case TRC_THREAD_DETACH_NOTIFY:
{
pStatus = _T("Thread detached ");
}
break;
case TRC_SYMBOLS_LOADING_NOTIFY:
{
pStatus = _T("Loading symbols ");
}
break;
case TRC_SYMBOLS_LOADED_NOTIFY:
{
pStatus = _T("Symbols loaded ");
}
break;
case TRC_SYMBOLS_UNLOAD_NOTIFY:
{
pStatus = _T("Symbols freed ");
}
break;
case TRC_FILES_RESET:
{
pStatus = _T("Trace files reset");
}
break;
default:
{
pStatus = _T("Undefined ");
}
break;
}
/************************************************************************/
/* Get the current date and time. */
/************************************************************************/
TRCGetCurrentDate(&theDate);
TRCGetCurrentTime(&theTime);
/************************************************************************/
/* Get our process and thread IDs. */
/************************************************************************/
processId = TRCGetCurrentProcessId();
threadId = TRCGetCurrentThreadId();
/************************************************************************/
/* Format the attach/detach string. */
/************************************************************************/
hr = StringCchPrintf(
szOutputBuffer,
SIZE_TCHARS(szOutputBuffer),
_T("### %s (") TRC_PROC_FMT _T(":") TRC_THRD_FMT _T(") at ")
_T("") TRC_TIME_FMT _T(" ") TRC_DATE_FMT _T(" ###\r\n"),
pStatus,
processId,
threadId,
theTime.hour,
theTime.min,
theTime.sec,
theTime.hundredths,
theDate.day,
theDate.month,
theDate.year
);
if (SUCCEEDED(hr)) {
/************************************************************************/
/* Now output this string to the debugger. We can't output this to */
/* file as we need to have the trace mutex to do that and we may not */
/* have the mutex. To avoid confusion we only write to the debugger. */
/************************************************************************/
length = DC_TSTRLEN(szOutputBuffer);
OutputDebugString(szOutputBuffer);
}
return;
} /* TRCInternalTrace */
/****************************************************************************/
/* FUNCTION: TRCMaybeSwapFile(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function checks if the current trace file has enough space to */
/* accomodate a string of the supplied length and, if not, makes the other */
/* trace file current. */
/* */
/* PARAMETERS: */
/* =========== */
/* length : length of the string. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCMaybeSwapFile(DCUINT length)
{
/************************************************************************/
/* If the length of the string plus the offset is greater than the */
/* length of the trace file then we need to swap trace files. */
/************************************************************************/
if ((trcpSharedData->trcOffset + length) > trcpConfig->maxFileSize)
{
/********************************************************************/
/* We need to swap trace files so set the offset to 0 and then */
/* flip the trace file. */
/********************************************************************/
trcpSharedData->trcOffset = 0;
trcpSharedData->trcIndicator++;
trcpSharedData->trcIndicator %= TRC_NUM_FILES;
/********************************************************************/
/* Now we need to reset the new trace file by blanking it out. */
/********************************************************************/
TRCBlankFile(trcpSharedData->trcIndicator);
}
DC_EXIT_POINT:
return;
} /* TRCOutputToFile */
/****************************************************************************/
/* FUNCTION: TRCReadProfInt(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This reads a private profile integer from the registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* pSection : section containing the entry to read */
/* pEntry : entry name of integer to retrieve */
/* pValue : buffer to return the entry in */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success */
/* TRC_RC_IO_ERROR : I/O error. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCReadProfInt(PDCTCHAR pEntry,
PDCUINT32 pValue)
{
DCUINT rc = 0;
/************************************************************************/
/* First try to read the value from the current user section */
/************************************************************************/
rc = TRCReadEntry(HKEY_CURRENT_USER,
pEntry,
(PDCTCHAR)pValue,
sizeof(*pValue),
REG_DWORD);
if (0 != rc)
{
/********************************************************************/
/* Couldn't read the value from the current user section. Try to */
/* pick up a default value from the local machine section. */
/********************************************************************/
rc = TRCReadEntry(HKEY_LOCAL_MACHINE,
pEntry,
(PDCTCHAR)pValue,
sizeof(*pValue),
REG_DWORD);
if (0 != rc)
{
/****************************************************************/
/* There is nothing we can do so just fall through. */
/****************************************************************/
}
}
return(rc);
} /* TRCReadProfInt */
/****************************************************************************/
/* FUNCTION: TRCReadProfString(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This reads a private profile string from registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* pSection : section containing the entry to read. */
/* pEntry : entry name of string to retrieve (if NULL all entries */
/* in the section are returned). */
/* pBuffer : buffer to return the entry in. */
/* bufferSize : size of the buffer in bytes. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success. */
/* TRC_RC_IO_ERROR : I/O error. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCReadProfString(PDCTCHAR pEntry,
PDCTCHAR pBuffer,
DCINT16 bufferSize)
{
DCUINT rc = 0;
/************************************************************************/
/* First try to read the value from the current user section. */
/************************************************************************/
rc = TRCReadEntry(HKEY_CURRENT_USER,
pEntry,
pBuffer,
bufferSize,
REG_SZ);
if (0 != rc)
{
/********************************************************************/
/* Couldn't read the value from the current user section. Try to */
/* pick up a default value from the local machine section. */
/********************************************************************/
rc = TRCReadEntry(HKEY_LOCAL_MACHINE,
pEntry,
pBuffer,
bufferSize,
REG_SZ);
if (0 != rc)
{
/****************************************************************/
/* There is nothing we can do so just fall through. */
/****************************************************************/
}
}
return(rc);
} /* TRCReadProfString */
/****************************************************************************/
/* FUNCTION: TRCResetTraceFiles(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This function resets the trace files. It nulls out both trace files */
/* and then resets the file offset to 0 and the file indicator to file 0. */
/* */
/* PARAMETERS: */
/* =========== */
/* None. */
/* */
/* RETURNS: */
/* ======== */
/* Nothing. */
/* */
/****************************************************************************/
DCVOID DCINTERNAL TRCResetTraceFiles(DCVOID)
{
DCUINT i;
/************************************************************************/
/* Blank out the trace files. Note that we must have the mutex at this */
/* point. */
/************************************************************************/
for (i = 0; i < TRC_NUM_FILES; i++)
{
TRCBlankFile(i);
}
/************************************************************************/
/* Set the trace file indicator to file 0 and set the file offset to 0. */
/************************************************************************/
trcpSharedData->trcIndicator = 0;
trcpSharedData->trcOffset = 0;
/************************************************************************/
/* Output a debug string. */
/************************************************************************/
TRCInternalTrace(TRC_FILES_RESET);
} /* TRCResetTraceFiles */
/****************************************************************************/
/* FUNCTION: TRCWriteProfInt(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This writes a private profile integer to the registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* pSection : section containing the entry written */
/* pEntry : entry name of integer to write. If the entry does not */
/* exist it is created and if it is NULL the entire */
/* section is deleted. */
/* pValue : pointer to the integer to be written. If the pointer */
/* is NULL the entry is deleted. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success */
/* TRC_RC_IO_ERROR : I/O error. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCWriteProfInt(PDCTCHAR pEntry,
PDCUINT32 pValue)
{
DCUINT rc = 0;
/************************************************************************/
/* Write the entry to the current user section. */
/************************************************************************/
rc = TRCWriteEntry(HKEY_CURRENT_USER,
pEntry,
(PDCTCHAR)pValue,
sizeof(DCINT),
REG_DWORD);
if (0 != rc)
{
TRCDebugOutput(_T("Failed to write int"));
}
return(rc);
} /* TRCWriteProfInt */
/****************************************************************************/
/* FUNCTION: TRCWriteProfString(...) */
/* */
/* DESCRIPTION: */
/* ============ */
/* This writes a private profile string to the registry. */
/* */
/* PARAMETERS: */
/* =========== */
/* pSection : section containing the entry written */
/* pEntry : entry name of string to write. If the entry does not */
/* exist it is created and if it is NULL the entire */
/* section is deleted. */
/* pBuffer : buffer containing the entry. If the buffer is NULL */
/* the entry is deleted. */
/* */
/* RETURNS: */
/* ======== */
/* 0 : success */
/* TRC_RC_IO_ERROR : I/O error. */
/* */
/****************************************************************************/
DCUINT DCINTERNAL TRCWriteProfString(PDCTCHAR pEntry,
PDCTCHAR pBuffer)
{
DCUINT rc = 0;
/************************************************************************/
/* Write the entry to the current user section */
/************************************************************************/
rc = TRCWriteEntry(HKEY_CURRENT_USER,
pEntry,
pBuffer,
DC_TSTRBYTELEN(pBuffer),
REG_SZ);
if (0 != rc)
{
TRCDebugOutput(_T("Failed to write string"));
}
return(rc);
} /* TRCWriteProfString */
#include <ntrcint.c>