windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/iisrtl/pudebug.c

2241 lines
70 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name :
pudebug.c
Abstract:
This module defines functions required for
Debugging and logging messages for a dynamic program.
Author:
Murali R. Krishnan ( MuraliK ) 10-Sept-1994
Modified to be moved to common dll in 22-Dec-1994.
Revisions:
MuraliK 16-May-1995 Code to load and save debug flags from registry
MuraliK 16-Nov-1995 Remove DbgPrint (undoc api)
JasAndre Jan-1998 Replaced tracing mechanism with WMI Tracing
--*/
/************************************************************
* Include Headers
************************************************************/
# include <nt.h>
# include <ntrtl.h>
# include <nturtl.h>
# include <windows.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <string.h>
# include "pudebug.h"
# include <winbase.h>
# include <coguid.h>
# include <objbase.h>
#ifndef _NO_TRACING_
DEFINE_GUID(IisRtlGuid,
0x784d8900, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
#endif
/*************************************************************
* Global Variables and Default Values
*************************************************************/
# define MAX_PRINTF_OUTPUT ( 10240)
# define DEFAULT_DEBUG_FLAGS_VALUE ( 0)
# define DEBUG_FLAGS_REGISTRY_LOCATION_A "DebugFlags"
# define DEBUG_BREAK_ENABLED_REGKEYNAME_A "BreakOnAssert"
/*************************************************************
* Functions
*************************************************************/
# ifndef _NO_TRACING_
// Critical section used to control access to the list of known guids used for
// WMI tracing
CRITICAL_SECTION g_csGuidList;
// The list of known guids which are used with WMI Tracing
LIST_ENTRY g_pGuidList;
// The sequence number of the trace message we are about to log
LONG g_dwSequenceNumber = 0;
// This flag tells us whether or not to do any WMI handling
BOOL g_bTracingEnabled = FALSE;
// This flag tells us if we had to start WMI tracing
BOOL g_bStartedTracing = FALSE;
TRACEHANDLE g_hLoggerHandle = 0;
// This flag tells us whether to enable the guids as they are defined or to
// wait for an enable command from WMI
BOOL g_bTracingActive = FALSE;
// This flag tells us whether or not to log info to OutputDebugString, this is
// SLOW. It is on by default for CHK builds and off by default for FRE builds
//#ifdef DBG
//BOOL g_ODSEnabled = TRUE;
//#else
BOOL g_ODSEnabled = FALSE;
//#endif
// The filename and session name to use for the WMI Tracing file
char g_szLogFileName[MAX_PATH] = "";
char g_szLogSessionName[MAX_PATH] = "";
// The WMI Tracing buffer size, in KB, and the min and max number of buffers
// that are permitted, and max file size, 0 means use default
// If max file size is set then circular buffers are used
ULONG g_ulBufferSize = 0;
ULONG g_ulMinBuffers = 0;
ULONG g_ulMaxBuffers = 0;
ULONG g_ulMaxFileSize = 0;
// Three special modes that it is possible to start the logger in
BOOL g_bRealTimeMode = FALSE;
BOOL g_bInMemoryMode = FALSE;
BOOL g_bUserMode = FALSE;
// The default values used for control flags and the error level. These
// settings are only used for modules that have their active flags set
DWORD g_dwDefaultControlFlag = 0;
DWORD g_dwDefaultErrorLevel = 0;
// This flag tells us if we have done the registry initialization, we can't
// register with WMI until this has been done
BOOL g_bHaveDoneInit = FALSE;
// This flag tells us whether we have registered with WMI or not
BOOL g_bHadARegister = FALSE;
void SetLevel(int iDefaultErrorLevel, int *piErrorFlags)
/*++
Support function used by all and sundry to convert the level, ie. 1, 2 or 3
into the bitmapped format used in the error flags variable
Arguments:
iDefaultErrorLevel new value to decode for the error level
*piErrorFlags pointer to the variable to set the error level in
Returns:
--*/
{
if (piErrorFlags) {
// Mask out all but the ODS flag
// Note: Why do we do this? There is a problem that if this module is
// compiled as FRE but the caller is CHK then this function would
// erase the callers ODS setting, so by masking out all but the ODS
// flag here we can maintain the callers ODS setting
*piErrorFlags &= DEBUG_FLAG_ODS;
// Then or in the appropriate bit
switch (iDefaultErrorLevel) {
case DEBUG_LEVEL_ERROR:
*piErrorFlags |= DEBUG_FLAG_ERROR;
break;
case DEBUG_LEVEL_WARN:
*piErrorFlags |= DEBUG_FLAG_WARN;
break;
case DEBUG_LEVEL_INFO:
*piErrorFlags |= DEBUG_FLAG_INFO;
break;
default:
break;
};
if (g_ODSEnabled)
*piErrorFlags |= DEBUG_FLAG_ODS;
}
}
SGuidList *FindGuid(
IN GUID *ControlGuid
)
/*++
Support function used by all and sundry to find a guid in the guid list.
NOTE: It is the responsibility of the caller to enter the guid list
critical section before calling this function
Arguments:
*ControlGuid pointer to the guid that we are looking for
Returns:
NULL if the guid is not in the list
*DEBUG_PRINTS the data relating to the guid if it is in the list
--*/
{
LIST_ENTRY *pEntry;
SGuidList *pGE = NULL;
BOOL bFound = FALSE;
for (pEntry = g_pGuidList.Flink;
pEntry != &g_pGuidList;
pEntry = pEntry->Flink)
{
pGE = CONTAINING_RECORD(pEntry,
SGuidList,
m_leEntry);
DBG_ASSERT(TRACESIG == pGE->dwSig);
if (IsEqualGUID(ControlGuid, &pGE->m_dpData.m_guidControl)) {
bFound = TRUE;
break;
}
}
return bFound ? pGE : NULL;
}
dllexp ULONG IISTraceControlCallback(
IN WMIDPREQUESTCODE RequestCode,
IN PVOID RequestContext,
IN OUT ULONG *InOutBufferSize,
IN OUT PVOID Buffer
)
/*++
Callback function supplied to WMI so that it can notify me when the user
requests a change to the level or control flags for logging
Arguments:
RequestCode control code used to determine what function to perform
RequestContext not used
InOutBufferSize size of the buffer supplied by WMI, set to 0 as we don't
send any data back to WMI through this mechanism
Buffer contains EVENT_TRACE_HEADER needed to identify whom a
request is for
Returns:
--*/
{
PEVENT_TRACE_HEADER pHeader = (PEVENT_TRACE_HEADER) Buffer;
TRACEHANDLE hTrace = GetTraceLoggerHandle(Buffer);
LPGUID pGuid = &pHeader->Guid;
ULONG Status = ERROR_SUCCESS;
SGuidList *pGE = NULL;
switch (RequestCode)
{
case WMI_ENABLE_EVENTS:
{
EnterCriticalSection(&g_csGuidList);
DBG_ASSERT(pGuid);
DBG_REQUIRE(pGE = FindGuid(pGuid));
if (pGuid && pGE) {
DEBUG_PRINTS *pGEData = &pGE->m_dpData;
pGEData->m_iControlFlag = GetTraceEnableFlags(hTrace);
pGEData->m_hLogger = hTrace;
if (pGEData->m_piErrorFlags) {
SetLevel(GetTraceEnableLevel(hTrace), pGEData->m_piErrorFlags);
// Flag us as no longer needing an initialize, important step
// to prevent re-initialization attempts in PuInitiateDebug
pGE->m_iInitializeFlags &= ~DEBUG_FLAG_INITIALIZE;
pGE->m_iDefaultErrorLevel = GetTraceEnableLevel(hTrace);
}
else {
pGE->m_iInitializeFlags = DEBUG_FLAG_DEFERRED_START;
pGE->m_iDefaultErrorLevel = GetTraceEnableLevel(hTrace);
}
}
LeaveCriticalSection(&g_csGuidList);
break;
}
case WMI_DISABLE_EVENTS:
{
EnterCriticalSection(&g_csGuidList);
if (pGuid && (NULL != (pGE = FindGuid(pGuid)))) {
DEBUG_PRINTS *pGEData = &pGE->m_dpData;
pGEData->m_iControlFlag = 0;
pGEData->m_hLogger = hTrace;
if (pGEData->m_piErrorFlags) {
SetLevel(0, pGEData->m_piErrorFlags);
pGE->m_iDefaultErrorLevel = 0;
}
}
LeaveCriticalSection(&g_csGuidList);
break;
}
default:
{
char szTemp[MAX_PATH];
_snprintf(szTemp, sizeof(szTemp),
"IISTRACE:\t%s(%d), IISTraceControlCallback: Invalid parameter\n",
__FILE__, __LINE__);
OutputDebugString(szTemp);
Status = ERROR_INVALID_PARAMETER;
break;
}
}
*InOutBufferSize = 0;
return Status;
}
void LoadGuidFromRegistry(char *pszModuleName)
/*++
This function is used to load all the information stored in the registry
about a module and to allocate an entry in the guid list for it
NOTE: It is the responsibility of the caller to enter the guid list
critical section before calling this function
Arguments:
pszModuleName pointer to the name of the module we are about to process
Returns:
--*/
{
char KeyName[MAX_PATH * 2] = REG_TRACE_IIS "\\";
HKEY hk = 0;
// Create the name of the key for this GUID using the english name
strcat(KeyName, pszModuleName);
// Open the GUIDs key
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, KeyName, &hk)) {
WCHAR szwTemp[MAX_PATH];
DWORD dwReadSize;
// 1. Load the guid number from the registry, we can only continue
// processing this module if we find a guid for it
dwReadSize = sizeof(szwTemp);
if (ERROR_SUCCESS == RegQueryValueExW(hk, REG_TRACE_IIS_GUID,
NULL, NULL,
(BYTE *) szwTemp,
&dwReadSize)) {
CLSID ControlGuid;
// 2. Convert the guid string into a guid
if (SUCCEEDED(CLSIDFromString(szwTemp, &ControlGuid))) {
// 3. Ensure it is not already in the guid list
if (!FindGuid(&ControlGuid)) {
DEBUG_PRINTS *pGEData = NULL;
BOOL bGuidEnabled;
int iTemp;
// 4. Try and insert it into the guid list
SGuidList *pGE = (SGuidList *) LocalAlloc(LPTR, sizeof(SGuidList));
if (pGE) {
pGEData = &pGE->m_dpData;
// Set the GuidList signature
pGE->dwSig = TRACESIG;
// Set the SGuidList.m_dpData member variables
strncpy(pGEData->m_rgchLabel,
pszModuleName, MAX_LABEL_LENGTH - 1);
pGEData->m_guidControl = ControlGuid;
pGEData->m_bBreakOnAssert = TRUE; // Default - Break if Assert Fails
// Add it to the head of the list. Put it at the head in case this
// is an object which is being loaded and unloaded continuosly.
InsertHeadList(&g_pGuidList, &pGE->m_leEntry);
// 5. Try to load any previous control settings from the registry
// 5.1 See if the guid is enabled, if so flag us as needing
// to start this guid when we initialize with WMI
dwReadSize = sizeof(bGuidEnabled);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_ACTIVE,
NULL, NULL,
(BYTE *) &bGuidEnabled,
&dwReadSize)) {
if (bGuidEnabled)
pGE->m_iInitializeFlags = DEBUG_FLAG_INITIALIZE;
}
// Otherwise use the global setting to determine this
else if (g_bTracingActive)
pGE->m_iInitializeFlags = DEBUG_FLAG_INITIALIZE;
// If it is enabled then load the remaining flags
if (pGE->m_iInitializeFlags & DEBUG_FLAG_INITIALIZE) {
// 5.2 So next load the ControlFlags, the flags default
// to the global setting
dwReadSize = sizeof(&iTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_CONTROL,
NULL, NULL,
(BYTE *) &iTemp,
&dwReadSize))
pGEData->m_iControlFlag = iTemp;
else
pGEData->m_iControlFlag = g_dwDefaultControlFlag;
// 5.3 Then load the Level, it also defaults to the global
// setting
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LEVEL,
NULL, NULL,
(BYTE *) &iTemp,
&dwReadSize)) {
pGE->m_iDefaultErrorLevel = iTemp;
}
else
pGE->m_iDefaultErrorLevel = g_dwDefaultErrorLevel;
}
}
}
}
}
RegCloseKey(hk);
}
}
dllexp void
AddToRegistry(
IN SGuidList *pGE
)
/*++
This function creates new registry entries for this module so that it will
be correctly loaded next time IIS is started
NOTE: It is the responsibility of the caller to enter the guid list
critical section before calling this function
Arguments:
*pGE pointer to the SGuidList entry which has all the
information about the guid to add
Returns:
This returns void as it does not matter to the running program whether
this works or not, it just improves restart performance if it does.
--*/
{
HKEY hk = 0;
HKEY hkNew = 0;
// Open the IIS Trace registry key
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, REG_TRACE_IIS, &hk)) {
DEBUG_PRINTS *pGEData = &pGE->m_dpData;
BOOL bCreatedGuid = FALSE;
DWORD dwDisposition;
// 1. Create a new key for the module
if (ERROR_SUCCESS == RegCreateKeyEx(hk, pGEData->m_rgchLabel,
0, NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL, &hkNew, &dwDisposition))
{
WCHAR szwTemp[MAX_PATH];
// 2. Convert the guid to a string so that we can add it to the
// registry, the format is E.g.
// {37EABAF0-7FB6-11D0-8817-00A0C903B83C}
if (StringFromGUID2(&pGEData->m_guidControl,
(WCHAR *) szwTemp,
MAX_PATH)) {
// 3. Add the guid string to the module information
if (ERROR_SUCCESS == RegSetValueExW(hkNew,
REG_TRACE_IIS_GUID,
0,
REG_SZ,
(BYTE *) szwTemp,
wcslen(szwTemp) * sizeof(WCHAR))) {
bCreatedGuid = TRUE;
}
}
RegCloseKey(hkNew);
if (!bCreatedGuid && (REG_CREATED_NEW_KEY == dwDisposition)) {
RegDeleteKey(hk, pGEData->m_rgchLabel);
}
}
RegCloseKey(hk);
}
}
TRACEHANDLE
GetTraceFileHandle(
VOID)
/*++
This function create the tracing file for the current log file. It only does
this if a module that has the DEBUG_FLAG_INITIALIZE bit is successfully
registered with WMI.
Arguments:
Returns:
--*/
{
TRACEHANDLE hLogger = 0;
// There must be a valid log file name and session name in order to create
// the trace file
if (g_szLogFileName[0] && g_szLogSessionName[0]) {
struct {
EVENT_TRACE_PROPERTIES Header;
char LoggerName[MAX_PATH];
char LogFileName[MAX_PATH];
} Properties;
PEVENT_TRACE_PROPERTIES LoggerInfo;
PCHAR Offset;
ULONG Status = ERROR_SUCCESS;
LoggerInfo = (PEVENT_TRACE_PROPERTIES)&Properties.Header;
// Set up the request structure
RtlZeroMemory(&Properties, sizeof(Properties));
LoggerInfo->Wnode.BufferSize = sizeof(Properties);
LoggerInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
if (0 == g_ulMaxFileSize)
LoggerInfo->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;
else {
LoggerInfo->LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR;
LoggerInfo->MaximumFileSize = g_ulMaxFileSize;
}
if (g_bRealTimeMode)
LoggerInfo->LogFileMode |= EVENT_TRACE_REAL_TIME_MODE;
else if (g_bInMemoryMode)
LoggerInfo->LogFileMode |= EVENT_TRACE_BUFFERING_MODE;
else if (g_bUserMode)
LoggerInfo->LogFileMode |= EVENT_TRACE_PRIVATE_LOGGER_MODE;
strcpy(&Properties.LoggerName[0], g_szLogSessionName);
strcpy(&Properties.LogFileName[0], g_szLogFileName);
LoggerInfo->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
LoggerInfo->LogFileNameOffset = LoggerInfo->LoggerNameOffset +
MAX_PATH;
LoggerInfo->BufferSize = g_ulBufferSize;
LoggerInfo->MinimumBuffers = g_ulMinBuffers;
LoggerInfo->MaximumBuffers = g_ulMaxBuffers;
Status = QueryTrace(0,
g_szLogSessionName,
(PEVENT_TRACE_PROPERTIES) LoggerInfo);
if (ERROR_SUCCESS == Status)
hLogger = LoggerInfo->Wnode.HistoricalContext;
else if (ERROR_WMI_INSTANCE_NOT_FOUND == Status) {
// The logger is not already started so try to start it now
Status = StartTrace(&hLogger,
g_szLogSessionName,
LoggerInfo);
}
if (ERROR_SUCCESS == Status) {
g_bStartedTracing = TRUE;
strcpy(g_szLogSessionName, &Properties.LoggerName[0]);
strcpy(g_szLogFileName, &Properties.LogFileName[0]);
}
else {
char szTemp[MAX_PATH];
_snprintf(szTemp, sizeof(szTemp),
"IISRTL:\t%s(%d), Unable to get the Logger Handle, return code = %d\n",
__FILE__, __LINE__,
Status);
OutputDebugString(szTemp);
}
}
return hLogger;
}
dllexp VOID
PuUninitiateDebug(
VOID)
/*++
This function stops tracing for the current log file but only if we started
the tracing in the initiate debug
Arguments:
Returns:
--*/
{
BOOL bStartedTracing = FALSE;
DWORD dwError;
EnterCriticalSection(&g_csGuidList);
bStartedTracing = g_bStartedTracing;
g_bStartedTracing = FALSE;
LeaveCriticalSection(&g_csGuidList);
if (bStartedTracing) {
struct {
EVENT_TRACE_PROPERTIES Header;
char LoggerName[MAX_PATH];
char LogFileName[MAX_PATH];
} Properties;
PEVENT_TRACE_PROPERTIES LoggerInfo;
LoggerInfo = (PEVENT_TRACE_PROPERTIES) &Properties.Header;
RtlZeroMemory(&Properties, sizeof(Properties));
LoggerInfo->Wnode.BufferSize = sizeof(Properties);
LoggerInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
LoggerInfo->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
LoggerInfo->LogFileNameOffset = LoggerInfo->LoggerNameOffset + MAX_PATH;
// Stop tracing to the log file
dwError = StopTrace(g_hLoggerHandle, g_szLogSessionName, LoggerInfo);
if (dwError == ERROR_SUCCESS) {
strcpy(g_szLogFileName, &Properties.LogFileName[0]);
strcpy(g_szLogSessionName, &Properties.LoggerName[0]);
}
}
}
dllexp VOID
PuInitiateDebug(
VOID
)
/*++
This function iterates through the list of modules in the guid list
registering all of them and starting up logging if that is the state that
was stored in the registry
Arguments:
Returns:
--*/
{
BOOL bStartedLogger = FALSE;
EnterCriticalSection(&g_csGuidList);
if (!g_bHadARegister) {
LIST_ENTRY pRegList;
SGuidList *pGEReg = NULL;
LIST_ENTRY *pEntry;
SGuidList *pGE = NULL;
char szTemp[MAX_PATH];
// Flag us as having done this so that we can later leave this
// critical section
g_bHadARegister = TRUE;
// Only do the WMI registration if tracing is enabled
if (g_bTracingEnabled) {
// Initialize the list of guids which need to be registered with WMI
InitializeListHead(&pRegList);
// Iterate through the guid list
for (pEntry = g_pGuidList.Flink;
pEntry != &g_pGuidList;
pEntry = pEntry->Flink)
{
pGE = CONTAINING_RECORD(pEntry,
SGuidList,
m_leEntry);
DBG_ASSERT(TRACESIG == pGE->dwSig);
// Allocate memory for a local copy of the entry so that we can
// add it to our local list
pGEReg = (SGuidList *) _alloca(sizeof(SGuidList));
if (NULL != pGEReg) {
// Copy the entry and add it to the local list
memcpy(pGEReg, pGE, sizeof(SGuidList));
InsertHeadList(&pRegList, &pGEReg->m_leEntry);
}
}
LeaveCriticalSection(&g_csGuidList);
// Now iterate through our local copy of the list and register all the
// guids with WMI
for (pEntry = pRegList.Flink;
pEntry != &pRegList;
pEntry = pEntry->Flink)
{
TRACE_GUID_REGISTRATION RegGuids;
ULONG Status = ERROR_SUCCESS;
pGEReg = CONTAINING_RECORD(pEntry,
SGuidList,
m_leEntry);
DBG_ASSERT(TRACESIG == pGEReg->dwSig);
// Initialise the GUID registration structure
memset(&RegGuids, 0x00, sizeof(RegGuids));
RegGuids.Guid = (LPGUID) &pGEReg->m_dpData.m_guidControl;
// And then register the guid
Status = RegisterTraceGuidsW(IISTraceControlCallback,
NULL,
(LPGUID) &pGEReg->m_dpData.m_guidControl,
1,
&RegGuids,
NULL,
NULL,
&pGEReg->m_dpData.m_hRegistration);
if (ERROR_SUCCESS != Status)
{
_snprintf(szTemp, sizeof(szTemp),
"%16s:\t%s(%d), RegisterTraceGuids returned %d, main ID = %08X\n",
pGEReg->m_dpData.m_rgchLabel,
__FILE__, __LINE__,
Status,
pGEReg->m_dpData.m_guidControl.Data1);
OutputDebugString(szTemp);
}
else if (pGEReg->m_iInitializeFlags & DEBUG_FLAG_INITIALIZE) {
// Turn off the initialize flag
pGEReg->m_iInitializeFlags &= ~DEBUG_FLAG_INITIALIZE;
// Get the trace file handle if necessary
if (!bStartedLogger) {
bStartedLogger = TRUE;
g_hLoggerHandle = GetTraceFileHandle();
}
// And enable tracing for the module. If this is successful
// then WMI will call the IISTraceControlCallback
Status = EnableTrace(TRUE,
pGEReg->m_dpData.m_iControlFlag,
pGEReg->m_iDefaultErrorLevel,
(LPGUID) &pGEReg->m_dpData.m_guidControl,
g_hLoggerHandle);
if ((ERROR_SUCCESS != Status) && (ERROR_WMI_ALREADY_ENABLED != Status)) {
_snprintf(szTemp, sizeof(szTemp),
"%16s:\t%s(%d), Unable to EnableTrace, return code = %d\n",
pGEReg->m_dpData.m_rgchLabel,
__FILE__, __LINE__,
Status);
OutputDebugString(szTemp);
}
}
}
// Now re-enter the critical section so that we can save the results
// back to our global data structure
EnterCriticalSection(&g_csGuidList);
for (pEntry = pRegList.Flink;
pEntry != &pRegList;
pEntry = pEntry->Flink)
{
pGEReg = CONTAINING_RECORD(pEntry,
SGuidList,
m_leEntry);
DBG_ASSERT(TRACESIG == pGEReg->dwSig);
// Find the guid in our original guid list
pGE = FindGuid(&pGEReg->m_dpData.m_guidControl);
if (pGE) {
// Save the only two things that could have changed
pGE->m_iInitializeFlags = pGEReg->m_iInitializeFlags;
pGE->m_dpData.m_hRegistration = pGEReg->m_dpData.m_hRegistration;
}
}
}
}
LeaveCriticalSection(&g_csGuidList);
} // PuInitiateDebug()
VOID
PuLoadRegistryInfo(
VOID
)
/*++
This function loads all the registry settings for the project and adds all
the known modules to the Guid list
NOTE: It is the responsibility of the caller to enter the guid list
critical section before calling this function
Arguments:
Returns:
--*/
{
HKEY hk = 0;
// Get the global settings which need only be read once, and only for
// the master
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, REG_TRACE_IIS, &hk)) {
DWORD dwIndex = 0;
ULONG ulTemp = 0;
LONG iRegRes = 0;
char szTemp[MAX_PATH];
char szModuleName[MAX_PATH];
DWORD dwReadSize = sizeof(ulTemp);
DWORD dwSizeOfModuleName = sizeof(szModuleName);
// 1. Get the enabled flag, if this is false we do not do any WMI
// processing, by default this is FALSE
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_ENABLED,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_bTracingEnabled = ulTemp;
// 2. Get the AlwaysODS flag, if this is true we will always write
// trace out with OutputDebugString, effectively CHK build
// compatability mode
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_ODS,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_ODSEnabled = ulTemp;
// 3. Determine if the active flag is set, if it was then it means
// that something was active at shut down, so for each module load
// read the appropriate registry entries at start up
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_ACTIVE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_bTracingActive = ulTemp;
// 4. Get the trace configuration information, log file name,
// logging session name, min/max number of buffers, buffer size & mode
dwReadSize = sizeof(szTemp);
if (ERROR_SUCCESS == (iRegRes = RegQueryValueEx(hk,
REG_TRACE_IIS_LOG_FILE_NAME,
NULL,
NULL,
(BYTE *) &szTemp,
&dwReadSize)))
strcpy(g_szLogFileName, szTemp);
else if (ERROR_MORE_DATA == iRegRes) {
DBGERROR((DBG_CONTEXT,
"Unable to load tracing logfile name, name too long.\n"));
}
else
DBGWARN((DBG_CONTEXT,
"Unable to load tracing logfile name, Windows error %d\n",
iRegRes));
dwReadSize = sizeof(szTemp);
if (ERROR_SUCCESS == (iRegRes = RegQueryValueEx(hk,
REG_TRACE_IIS_LOG_SESSION_NAME,
NULL,
NULL,
(BYTE *) &szTemp,
&dwReadSize)))
strcpy(g_szLogSessionName, szTemp);
else if (ERROR_MORE_DATA == iRegRes) {
DBGERROR((DBG_CONTEXT,
"Unable to load tracing log session name, name too long.\n"));
}
else
DBGWARN((DBG_CONTEXT,
"Unable to load tracing log session name, Windows error %d\n",
iRegRes));
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_BUFFER_SIZE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_ulBufferSize = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MIN_BUFFERS,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_ulMinBuffers = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MAX_BUFFERS,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_ulMaxBuffers = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MAX_FILESIZE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_ulMaxFileSize = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MAX_FILESIZE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_bRealTimeMode = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MAX_FILESIZE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_bInMemoryMode = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LOG_MAX_FILESIZE,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_bUserMode = ulTemp;
// 5. Load the ControlFlags and Level. Both default to 0, off
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_CONTROL,
NULL, NULL,
(BYTE *) &ulTemp, &dwReadSize))
g_dwDefaultControlFlag = ulTemp;
dwReadSize = sizeof(ulTemp);
if (ERROR_SUCCESS == RegQueryValueEx(hk, REG_TRACE_IIS_LEVEL,
NULL, NULL,
(BYTE *) &ulTemp,
&dwReadSize)) {
g_dwDefaultErrorLevel = ulTemp;
}
DBGPRINTF((DBG_CONTEXT, "Enumerating module for : %s\n", REG_TRACE_IIS));
// Enumerate all the modules listed in the registry for IIS
while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hk, dwIndex,
szModuleName,
&dwSizeOfModuleName,
NULL, NULL, NULL, NULL))
{
// Then load the setting for this guid from the registry and
// set up any defaults that are needed
LoadGuidFromRegistry(szModuleName);
dwSizeOfModuleName = sizeof(szModuleName);
++dwIndex;
}
RegCloseKey(hk);
}
#ifdef DBG
else
if (g_ODSEnabled)
OutputDebugString("IISTRACE: Warning, could not find IIS trace entry in "
"the registry.\n\t Unable to initialize WMI tracing.\n");
#endif
} // PuLoadRegistryInfo()
LPDEBUG_PRINTS
PuCreateDebugPrintsObject(
IN const char * pszPrintLabel,
IN GUID * ControlGuid,
IN int * ErrorFlags,
IN int DefaultControlFlags
)
/*++
This function looks for and if necessary creates a new DEBUG_PRINTS object
for the required module
Arguments:
*pszPrintLabel pointer to null-terminated string containing the
label for program's debugging output
*ControlGuid the unique GUID used to identify this module
*ErrorFlags pointer to the error flags variable used by the
module to determine whether or not to log something
DefaultControlFlags default flags used by IF_DEBUG
Returns:
pointer to a new DEBUG_PRINTS object on success.
Returns NULL on failure.
--*/
{
DEBUG_PRINTS *pGEData = NULL;
LIST_ENTRY *pEntry;
SGuidList *pGE = NULL;
DBG_ASSERT(NULL != ErrorFlags);
DBG_ASSERT(0 != pszPrintLabel[0]);
// Look through the guid list for this module
EnterCriticalSection(&g_csGuidList);
// See if we have done the registry initialization stuff, if we have then
// we should have the guid list. The initialization need only be done once
if (!g_bHaveDoneInit) {
g_bHaveDoneInit = TRUE;
PuLoadRegistryInfo();
}
pGE = FindGuid(ControlGuid);
// If we don't have a pointer to the data member it means we could not
// find the guid, so add it to the list
if (NULL == pGE) {
pGE = (SGuidList *) LocalAlloc(LPTR, sizeof(SGuidList));
if (pGE) {
pGEData = &pGE->m_dpData;
// Set the SGuidList member variables
pGE->dwSig = TRACESIG;
pGE->m_iDefaultErrorLevel = g_dwDefaultErrorLevel;
// Set the SGuidList.m_dpData member variables
strncpy(pGEData->m_rgchLabel,
pszPrintLabel, MAX_LABEL_LENGTH - 1);
pGEData->m_rgchLabel[MAX_LABEL_LENGTH-1] = '\0';
pGEData->m_guidControl = *ControlGuid;
pGEData->m_bBreakOnAssert = TRUE; // Default - Break if Assert Fails
pGEData->m_iControlFlag = g_dwDefaultControlFlag;
// Add it to the head of the list. Put it at the head in case this
// is an object which is being loaded and unloaded continuosly.
InsertHeadList(&g_pGuidList, &pGE->m_leEntry);
// And now update the registry with the information about the guid
// so that we don't have to do this again. While we have already
// done a DBG_ASSERT above we should still verify that the module
// has a name before calling this in case of new components
if (pGEData->m_rgchLabel[0])
AddToRegistry(pGE);
}
}
else
pGEData = &pGE->m_dpData;
// And then if we have the member initialize its data members with the
// parameters supplied by the caller
if (NULL != pGEData) {
DBG_ASSERT(!strncmp(pGEData->m_rgchLabel,
pszPrintLabel,
MAX_LABEL_LENGTH - 1));
// Check to see if we had a deferred start waiting, this would happen
// if we received a WMI initialize during the main phase but we had not
// yet loaded this module
if (pGE->m_iInitializeFlags & DEBUG_FLAG_DEFERRED_START) {
pGE->m_iInitializeFlags &= ~(DEBUG_FLAG_DEFERRED_START | DEBUG_FLAG_INITIALIZE);
}
// Otherwise load the default control flags, but only if we are not
// waiting for initialization. When we are waiting, the registry
// entries we have already loaded are to take precedence
else if (!(pGE->m_iInitializeFlags & DEBUG_FLAG_INITIALIZE)) {
// Set the SGuidList.m_dpData member variables
pGEData->m_iControlFlag = DefaultControlFlags;
}
// Save the pointer to the error flags and set the level
pGEData->m_piErrorFlags = ErrorFlags;
SetLevel(pGE->m_iDefaultErrorLevel, pGEData->m_piErrorFlags);
}
if (NULL == pGEData) {
char szTemp[MAX_PATH];
_snprintf(szTemp, sizeof(szTemp),
"%16s:\t%s(%d), Unable to find in or add a Guid to the trace list, return code = %d\n",
pszPrintLabel, __FILE__, __LINE__,
GetLastError());
OutputDebugString(szTemp);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
// And finally ALWAYS or in the ODS flag if enabled
if (g_ODSEnabled && ErrorFlags)
*ErrorFlags |= DEBUG_FLAG_ODS;
LeaveCriticalSection(&g_csGuidList);
return pGEData;
} // PuCreateDebugPrintsObject()
VOID
PuDeleteDebugPrintsObject(
IN OUT LPDEBUG_PRINTS pDebugPrints
)
/*++
This function cleans up the pDebugPrints object and does an frees of
allocated memory that may be required.
Arguments:
pDebugPrints pointer to the DEBUG_PRINTS object.
Returns:
--*/
{
SGuidList *pGE = NULL;
EnterCriticalSection(&g_csGuidList);
if (NULL != pDebugPrints) {
// Since we no longer delete anything or bother to shut it down all we
// need to do here is clear the pointer to the error flag
pDebugPrints->m_piErrorFlags = NULL;
// we allocated an SGuidList that contained the DEBUG_PRINTS struct
pGE = CONTAINING_RECORD(pDebugPrints, SGuidList, m_dpData);
DBG_ASSERT(TRACESIG == pGE->dwSig);
if (pGE)
{
RemoveEntryList(&pGE->m_leEntry);
pGE->dwSig = 0;
LocalFree(pGE);
}
}
LeaveCriticalSection(&g_csGuidList);
} // PuDeleteDebugPrintsObject()
VOID
PuDbgPrintMain(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const BOOL bUnicodeRequest,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszFormat,
IN va_list argptr
)
/*++
Main function that examines the incoming message and works out what to do
with the header and the message.
Arguments:
pDebugPrints pointer to the DEBUG_PRINTS object.
pszFilePath null terminated string which is the file name
nLineNum the number of the line where the tracing is coming from
pszFormat & ... the format and arguments to be written out
Returns:
--*/
{
LPCSTR pszFileName = strrchr( pszFilePath, '\\');
WCHAR szwOutput[ MAX_PRINTF_OUTPUT + 2];
char *pszOutput = (char *) szwOutput;
char rgchLabel[MAX_LABEL_LENGTH] = "";
TRACEHANDLE hLogger = 0;
TRACE_INFO tiTraceInfo;
DWORD dwSequenceNumber;
BOOL bUseODS = FALSE;
int cchOutput;
int nLength;
// Save the current error state so that we don't disrupt it
DWORD dwErr = GetLastError();
// Skip the '\' and retain only the file name in pszFileName
if (pszFileName)
++pszFileName;
else
pszFileName = pszFilePath;
EnterCriticalSection(&g_csGuidList);
// Determine if we are to do an OutputDebugString
if (g_ODSEnabled &&
(pDebugPrints &&
pDebugPrints->m_piErrorFlags &&
(*pDebugPrints->m_piErrorFlags & DEBUG_FLAG_ODS)))
{
bUseODS = TRUE;
}
if (NULL != pDebugPrints) {
// Save local copies of the data needed for the ODS case
if (bUseODS) {
strncpy(rgchLabel, pDebugPrints->m_rgchLabel, sizeof(rgchLabel));
}
// Save local copies of the data needed for the WMI Tracing case
if (pDebugPrints->m_hLogger) {
dwSequenceNumber = ++g_dwSequenceNumber;
hLogger = pDebugPrints->m_hLogger;
// Initialize our traceinfo structure
memset(&tiTraceInfo, 0x00, sizeof(tiTraceInfo));
tiTraceInfo.TraceHeader.Guid = pDebugPrints->m_guidControl;
}
}
// All data is now local so we can leave the critical section
LeaveCriticalSection(&g_csGuidList);
if (hLogger) {
int pid = GetCurrentProcessId();
ULONG Status = ERROR_SUCCESS;
// Format the incoming message using vsnprintf() so that we don't exceed
// the buffer length
if (bUnicodeRequest) {
cchOutput = _vsnwprintf(szwOutput, MAX_PRINTF_OUTPUT, (WCHAR *) pszFormat, argptr);
// If the string is too long, we get back a length of -1, so just use the
// partial data that fits in the string
if (cchOutput == -1) {
// Terminate the string properly since _vsnprintf() does not terminate
// properly on failure.
cchOutput = MAX_PRINTF_OUTPUT;
szwOutput[ cchOutput] = '\0';
}
++cchOutput;
cchOutput *= sizeof(WCHAR);
}
else {
cchOutput = _vsnprintf(pszOutput, sizeof(szwOutput), pszFormat, argptr);
// If the string is too long, we get back a length of -1, so just use the
// partial data that fits in the string
if (cchOutput == -1) {
// Terminate the string properly since _vsnprintf() does not terminate
// properly on failure.
cchOutput = sizeof(szwOutput) - 1;
pszOutput[cchOutput] = '\0';
}
++cchOutput;
}
// Fill out the Tracing structure
tiTraceInfo.TraceHeader.Size = sizeof(TRACE_INFO);
tiTraceInfo.TraceHeader.Class.Type = EVENT_TRACE_TYPE_INFO;
tiTraceInfo.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
tiTraceInfo.TraceHeader.ThreadId = GetCurrentThreadId();
tiTraceInfo.MofFields[0].DataPtr = (ULONGLONG) &dwSequenceNumber;
tiTraceInfo.MofFields[0].Length = sizeof(int);
tiTraceInfo.MofFields[1].DataPtr = (ULONGLONG) &pid;
tiTraceInfo.MofFields[1].Length = sizeof(int);
tiTraceInfo.MofFields[2].DataPtr = (ULONGLONG) &nLineNum;
tiTraceInfo.MofFields[2].Length = sizeof(int);
tiTraceInfo.MofFields[3].DataPtr = (ULONGLONG) pszFileName;
tiTraceInfo.MofFields[3].Length = strlen(pszFileName) + 1;
tiTraceInfo.MofFields[4].DataPtr = (ULONGLONG) szwOutput;
tiTraceInfo.MofFields[4].Length = cchOutput;
// Send the trace information to the trace class
TraceEvent(hLogger, (PEVENT_TRACE_HEADER) &tiTraceInfo);
}
if (bUseODS) {
int cchPrologue;
// Create the prologue
if (bUnicodeRequest) {
WCHAR wszLabel[MAX_PRINTF_OUTPUT];
WCHAR wszFileName[MAX_PRINTF_OUTPUT];
int iLength = MAX_PRINTF_OUTPUT;
if (MultiByteToWideChar(CP_ACP, 0, rgchLabel, -1, wszLabel, iLength) &&
MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, wszFileName, iLength))
{
cchOutput = _snwprintf(szwOutput,
MAX_PRINTF_OUTPUT,
L"IISTRACE\t%s\t(%lu)\t[ %12s : %5d]\t",
wszLabel,
GetCurrentThreadId(),
wszFileName,
nLineNum);
cchOutput = _vsnwprintf(szwOutput + cchOutput,
MAX_PRINTF_OUTPUT - cchOutput - 1,
(WCHAR *) pszFormat,
argptr);
// If the string is too long, we get back -1. So we get the string
// length for partial data.
if (cchOutput == -1) {
// Terminate the string properly, since _vsnprintf() does not
// terminate properly on failure.
cchOutput = MAX_PRINTF_OUTPUT;
szwOutput[ cchOutput] = '\0';
}
OutputDebugStringW(szwOutput);
}
}
else {
int cchPrologue;
// Create the prologue
cchPrologue = _snprintf(pszOutput,
sizeof(szwOutput),
"IISTRACE\t%s\t(%lu)\t[ %12s : %5d]\t",
rgchLabel,
GetCurrentThreadId(),
pszFileName,
nLineNum);
// Format the incoming message using vsnprintf() so that the overflows are
// captured
cchOutput = _vsnprintf(pszOutput + cchPrologue,
sizeof(szwOutput) - cchPrologue - 1,
pszFormat,
argptr);
// If the string is too long, we get back -1. So we get the string
// length for partial data.
if (cchOutput == -1) {
// Terminate the string properly, since _vsnprintf() does not
// terminate properly on failure.
cchOutput = sizeof(szwOutput) - 1;
pszOutput[cchOutput] = '\0';
}
OutputDebugStringA(pszOutput);
}
}
// Restore the error state
SetLastError( dwErr );
} // PuDbgPrintMain()
VOID
PuDbgPrint(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszFormat,
...)
{
va_list argsList;
va_start(argsList, pszFormat);
PuDbgPrintMain(pDebugPrints, FALSE, pszFilePath, nLineNum, pszFormat, argsList);
va_end(argsList);
}
dllexp VOID
PuDbgPrintW(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const WCHAR * pszFormat,
...)
{
va_list argsList;
va_start(argsList, pszFormat);
PuDbgPrintMain(pDebugPrints, TRUE, pszFilePath, nLineNum, (char *) pszFormat, argsList);
va_end(argsList);
}
VOID
PuDbgDumpMain(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN BOOL bUnicodeRequest,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszDump
)
/*++
Function that write a dump of a buffer out to the trace file.
Arguments:
pDebugPrints pointer to the DEBUG_PRINTS object.
pszFilePath null terminated string which is the file name
nLineNum the number of the line where the tracing is coming from
pszDump the dump to be written out
Returns:
--*/
{
LPCSTR pszFileName = strrchr( pszFilePath, '\\');
TRACEHANDLE hLogger = 0;
TRACE_INFO tiTraceInfo;
DWORD dwSequenceNumber;
BOOL bUseODS = FALSE;
DWORD cbDump;
// Save the current error state so that we don't disrupt it
DWORD dwErr = GetLastError();
// Skip the complete path name and retain only the file name in pszFileName
if ( pszFileName)
++pszFileName;
else
pszFileName = pszFilePath;
EnterCriticalSection(&g_csGuidList);
// Determine if we are to do an OutputDebugString
if (g_ODSEnabled ||
(pDebugPrints &&
pDebugPrints->m_piErrorFlags &&
(*pDebugPrints->m_piErrorFlags & DEBUG_FLAG_ODS)))
{
bUseODS = TRUE;
}
// Save local copies of the data needed for the WMI Tracing case
if ((NULL != pDebugPrints) && pDebugPrints->m_hLogger) {
dwSequenceNumber = ++g_dwSequenceNumber;
hLogger = pDebugPrints->m_hLogger;
if (bUnicodeRequest)
cbDump = (wcslen((WCHAR *) pszDump) + 1) * sizeof(WCHAR);
else
cbDump = strlen( pszDump) + 1;
// Initialize our traceinfo structure
memset(&tiTraceInfo, 0x00, sizeof(tiTraceInfo));
tiTraceInfo.TraceHeader.Guid = pDebugPrints->m_guidControl;
}
// All data is now local so we can leave the critical section
LeaveCriticalSection(&g_csGuidList);
// Send the outputs to respective files.
if (hLogger) {
int pid = GetCurrentProcessId();
// Fill out the Tracing structure
tiTraceInfo.TraceHeader.Size = sizeof(TRACE_INFO);
tiTraceInfo.TraceHeader.Class.Type = EVENT_TRACE_TYPE_INFO;
tiTraceInfo.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
tiTraceInfo.TraceHeader.ThreadId = GetCurrentThreadId();
tiTraceInfo.MofFields[0].DataPtr = (ULONGLONG) &dwSequenceNumber;
tiTraceInfo.MofFields[0].Length = sizeof(int);
tiTraceInfo.MofFields[1].DataPtr = (ULONGLONG) &pid;
tiTraceInfo.MofFields[1].Length = sizeof(int);
tiTraceInfo.MofFields[2].DataPtr = (ULONGLONG) &nLineNum;
tiTraceInfo.MofFields[2].Length = sizeof(int);
tiTraceInfo.MofFields[3].DataPtr = (ULONGLONG) pszFileName;
tiTraceInfo.MofFields[3].Length = strlen(pszFileName) + 1;
tiTraceInfo.MofFields[4].DataPtr = (ULONGLONG) pszDump;
tiTraceInfo.MofFields[4].Length = cbDump;
// Send the trace information to the trace class
TraceEvent(hLogger, (PEVENT_TRACE_HEADER) &tiTraceInfo);
}
if (bUseODS) {
if (bUnicodeRequest)
OutputDebugStringW((WCHAR *) pszDump);
else
OutputDebugString(pszDump);
}
// Restore the error state
SetLastError( dwErr );
} // PuDbgDumpMain()
VOID
PuDbgDump(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszDump
)
{
PuDbgDumpMain(pDebugPrints, FALSE, pszFilePath, nLineNum, pszDump);
}
dllexp VOID
PuDbgDumpW(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const WCHAR * pszDump
)
{
PuDbgDumpMain(pDebugPrints, TRUE, pszFilePath, nLineNum, (char *) pszDump);
}
//
// N.B. For PuDbgCaptureContext() to work properly, the calling function
// *must* be __cdecl, and must have a "normal" stack frame. So, we decorate
// PuDbgAssertFailed() with the __cdecl modifier and disable the frame pointer
// omission (FPO) optimization.
//
#pragma optimize( "y", off ) // disable frame pointer omission (FPO)
VOID
__cdecl
PuDbgAssertFailed(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszExpression)
/*++
This function calls assertion failure and records assertion failure
in log file.
--*/
{
CONTEXT context;
PuDbgCaptureContext( &context );
PuDbgPrint(pDebugPrints, pszFilePath, nLineNum,
" Assertion (%s) Failed\n"
" use !cxr %p to dump context\n",
pszExpression,
&context);
if (( NULL == pDebugPrints) || (TRUE == pDebugPrints->m_bBreakOnAssert))
{
DebugBreak();
}
} // PuDbgAssertFailed()
#pragma optimize( "", on ) // restore frame pointer omission (FPO)
# else // !_NO_TRACING_
LPDEBUG_PRINTS
PuCreateDebugPrintsObject(
IN const char * pszPrintLabel,
IN DWORD dwOutputFlags)
/*++
This function creates a new DEBUG_PRINTS object for the required
program.
Arguments:
pszPrintLabel pointer to null-terminated string containing
the label for program's debugging output
dwOutputFlags DWORD containing the output flags to be used.
Returns:
pointer to a new DEBUG_PRINTS object on success.
Returns NULL on failure.
--*/
{
LPDEBUG_PRINTS pDebugPrints;
pDebugPrints = GlobalAlloc( GPTR, sizeof( DEBUG_PRINTS));
if ( pDebugPrints != NULL) {
if ( strlen( pszPrintLabel) < MAX_LABEL_LENGTH) {
strcpy( pDebugPrints->m_rgchLabel, pszPrintLabel);
} else {
strncpy( pDebugPrints->m_rgchLabel,
pszPrintLabel, MAX_LABEL_LENGTH - 1);
pDebugPrints->m_rgchLabel[MAX_LABEL_LENGTH-1] = '\0';
// terminate string
}
memset( pDebugPrints->m_rgchLogFilePath, 0, MAX_PATH);
memset( pDebugPrints->m_rgchLogFileName, 0, MAX_PATH);
pDebugPrints->m_LogFileHandle = INVALID_HANDLE_VALUE;
pDebugPrints->m_dwOutputFlags = dwOutputFlags;
pDebugPrints->m_StdErrHandle = GetStdHandle( STD_ERROR_HANDLE);
pDebugPrints->m_fInitialized = TRUE;
pDebugPrints->m_fBreakOnAssert= TRUE; // Default - Break if Assert Fails
}
return ( pDebugPrints);
} // PuCreateDebugPrintsObject()
LPDEBUG_PRINTS
PuDeleteDebugPrintsObject(
IN OUT LPDEBUG_PRINTS pDebugPrints)
/*++
This function cleans up the pDebugPrints object and
frees the allocated memory.
Arguments:
pDebugPrints poitner to the DEBUG_PRINTS object.
Returns:
NULL on success.
pDebugPrints() if the deallocation failed.
--*/
{
if ( pDebugPrints != NULL) {
DWORD dwError = PuCloseDbgPrintFile( pDebugPrints);
if ( dwError != NO_ERROR) {
SetLastError( dwError);
} else {
pDebugPrints = GlobalFree( pDebugPrints); // returns NULL on success
}
}
return ( pDebugPrints);
} // PuDeleteDebugPrintsObject()
dllexp VOID
PuSetDbgOutputFlags(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN DWORD dwFlags)
{
if ( pDebugPrints == NULL) {
SetLastError( ERROR_INVALID_PARAMETER);
} else {
pDebugPrints->m_dwOutputFlags = dwFlags;
}
return;
} // PuSetDbgOutputFlags()
dllexp DWORD
PuGetDbgOutputFlags(
IN const LPDEBUG_PRINTS pDebugPrints)
{
return ( pDebugPrints != NULL) ? pDebugPrints->m_dwOutputFlags : 0;
} // PuGetDbgOutputFlags()
DWORD
PuOpenDbgFileLocal(
IN OUT LPDEBUG_PRINTS pDebugPrints)
{
if ( pDebugPrints == NULL)
return ERROR_INVALID_PARAMETER;
if ( pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
//
// Silently return as a file handle exists.
//
return ( NO_ERROR);
}
pDebugPrints->m_LogFileHandle =
CreateFile( pDebugPrints->m_rgchLogFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ( pDebugPrints->m_LogFileHandle == INVALID_HANDLE_VALUE) {
CHAR pchBuffer[1024];
DWORD dwError = GetLastError();
wsprintfA( pchBuffer,
" Critical Error: Unable to Open File %s. Error = %d\n",
pDebugPrints->m_rgchLogFileName, dwError);
OutputDebugString( pchBuffer);
return ( dwError);
}
return ( NO_ERROR);
} // PuOpenDbgFileLocal()
dllexp DWORD
PuOpenDbgPrintFile(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFileName,
IN const char * pszPathForFile)
/*++
Opens a Debugging log file. This function can be called to set path
and name of the debugging file.
Arguments:
pszFileName pointer to null-terminated string containing
the name of the file.
pszPathForFile pointer to null-terminated string containing the
path for the given file.
If NULL, then the old place where dbg files were
stored is used or if none,
default windows directory will be used.
Returns:
Win32 error codes. NO_ERROR on success.
--*/
{
if ( pszFileName == NULL || pDebugPrints == NULL) {
return ( ERROR_INVALID_PARAMETER);
}
//
// Setup the Path information. if necessary.
//
if ( pszPathForFile != NULL) {
// Path is being changed.
if ( strlen( pszPathForFile) < MAX_PATH) {
strcpy( pDebugPrints->m_rgchLogFilePath, pszPathForFile);
} else {
return ( ERROR_INVALID_PARAMETER);
}
} else {
if ( pDebugPrints->m_rgchLogFilePath[0] == '\0' && // no old path
!GetWindowsDirectory( pDebugPrints->m_rgchLogFilePath, MAX_PATH)) {
//
// Unable to get the windows default directory. Use current dir
//
strcpy( pDebugPrints->m_rgchLogFilePath, ".");
}
}
//
// Should need be, we need to create this directory for storing file
//
//
// Form the complete Log File name and open the file.
//
if ( (strlen( pszFileName) + strlen( pDebugPrints->m_rgchLogFilePath))
>= MAX_PATH) {
return ( ERROR_NOT_ENOUGH_MEMORY);
}
// form the complete path
strcpy( pDebugPrints->m_rgchLogFileName, pDebugPrints->m_rgchLogFilePath);
if ( pDebugPrints->m_rgchLogFileName[ strlen(pDebugPrints->m_rgchLogFileName) - 1]
!= '\\') {
// Append a \ if necessary
strcat( pDebugPrints->m_rgchLogFileName, "\\");
};
strcat( pDebugPrints->m_rgchLogFileName, pszFileName);
return PuOpenDbgFileLocal( pDebugPrints);
} // PuOpenDbgPrintFile()
dllexp DWORD
PuReOpenDbgPrintFile(
IN OUT LPDEBUG_PRINTS pDebugPrints)
/*++
This function closes any open log file and reopens a new copy.
If necessary. It makes a backup copy of the file.
--*/
{
if ( pDebugPrints == NULL) {
return ( ERROR_INVALID_PARAMETER);
}
PuCloseDbgPrintFile( pDebugPrints); // close any existing file.
if ( pDebugPrints->m_dwOutputFlags & DbgOutputBackup) {
// MakeBkupCopy();
OutputDebugString( " Error: MakeBkupCopy() Not Yet Implemented\n");
}
return PuOpenDbgFileLocal( pDebugPrints);
} // PuReOpenDbgPrintFile()
dllexp DWORD
PuCloseDbgPrintFile(
IN OUT LPDEBUG_PRINTS pDebugPrints)
{
DWORD dwError = NO_ERROR;
if ( pDebugPrints == NULL ) {
dwError = ERROR_INVALID_PARAMETER;
} else {
if ( pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
FlushFileBuffers( pDebugPrints->m_LogFileHandle);
if ( !CloseHandle( pDebugPrints->m_LogFileHandle)) {
CHAR pchBuffer[1024];
dwError = GetLastError();
wsprintf( pchBuffer,
"CloseDbgPrintFile() : CloseHandle( %d) failed."
" Error = %d\n",
pDebugPrints->m_LogFileHandle,
dwError);
OutputDebugString( pchBuffer);
}
pDebugPrints->m_LogFileHandle = INVALID_HANDLE_VALUE;
}
}
return ( dwError);
} // DEBUG_PRINTS::CloseDbgPrintFile()
VOID
PuDbgPrint(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszFormat,
...)
/*++
Main function that examines the incoming message and prints out a header
and the message.
--*/
{
LPCSTR pszFileName = strrchr( pszFilePath, '\\');
char pszOutput[ MAX_PRINTF_OUTPUT + 2];
LPCSTR pszMsg = "";
INT cchOutput;
INT cchPrologue;
va_list argsList;
DWORD dwErr;
//
// Skip the complete path name and retain file name in pszName
//
if ( pszFileName== NULL) {
pszFileName = pszFilePath; // if skipping \\ yields nothing use whole path.
}
# ifdef _PRINT_REASONS_INCLUDED_
switch (pr) {
case PrintError:
pszMsg = "ERROR: ";
break;
case PrintWarning:
pszMsg = "WARNING: ";
break;
case PrintCritical:
pszMsg = "FATAL ERROR ";
break;
case PrintAssertion:
pszMsg = "ASSERTION Failed ";
break;
case PrintLog:
pfnPrintFunction = &DEBUG_PRINTS::DebugPrintNone;
default:
break;
} /* switch */
# endif // _PRINT_REASONS_INClUDED_
dwErr = GetLastError();
// Format the message header
cchPrologue = wsprintf( pszOutput, "IISTRACE\t%s\t(%lu)\t[ %12s : %05d]\t",
pDebugPrints ? pDebugPrints->m_rgchLabel : "??",
GetCurrentThreadId(),
pszFileName, nLineNum);
// Format the incoming message using vsnprintf() so that the overflows are
// captured
va_start( argsList, pszFormat);
cchOutput = _vsnprintf( pszOutput + cchPrologue,
MAX_PRINTF_OUTPUT - cchPrologue - 1,
pszFormat, argsList);
va_end( argsList);
//
// The string length is long, we get back -1.
// so we get the string length for partial data.
//
if ( cchOutput == -1 ) {
//
// terminate the string properly,
// since _vsnprintf() does not terminate properly on failure.
//
cchOutput = MAX_PRINTF_OUTPUT;
pszOutput[ cchOutput] = '\0';
}
//
// Send the outputs to respective files.
//
if ( pDebugPrints != NULL)
{
if ( pDebugPrints->m_dwOutputFlags & DbgOutputStderr) {
DWORD nBytesWritten;
( VOID) WriteFile( pDebugPrints->m_StdErrHandle,
pszOutput,
strlen( pszOutput),
&nBytesWritten,
NULL);
}
if ( pDebugPrints->m_dwOutputFlags & DbgOutputLogFile &&
pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
DWORD nBytesWritten;
//
// Truncation of log files. Not yet implemented.
( VOID) WriteFile( pDebugPrints->m_LogFileHandle,
pszOutput,
strlen( pszOutput),
&nBytesWritten,
NULL);
}
}
if ( pDebugPrints == NULL ||
pDebugPrints->m_dwOutputFlags & DbgOutputKdb)
{
OutputDebugString( pszOutput);
}
SetLastError( dwErr );
return;
} // PuDbgPrint()
VOID
PuDbgDump(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszDump
)
{
LPCSTR pszFileName = strrchr( pszFilePath, '\\');
LPCSTR pszMsg = "";
DWORD dwErr;
DWORD cbDump;
//
// Skip the complete path name and retain file name in pszName
//
if ( pszFileName== NULL) {
pszFileName = pszFilePath;
}
dwErr = GetLastError();
// No message header for this dump
cbDump = strlen( pszDump);
//
// Send the outputs to respective files.
//
if ( pDebugPrints != NULL)
{
if ( pDebugPrints->m_dwOutputFlags & DbgOutputStderr) {
DWORD nBytesWritten;
( VOID) WriteFile( pDebugPrints->m_StdErrHandle,
pszDump,
cbDump,
&nBytesWritten,
NULL);
}
if ( pDebugPrints->m_dwOutputFlags & DbgOutputLogFile &&
pDebugPrints->m_LogFileHandle != INVALID_HANDLE_VALUE) {
DWORD nBytesWritten;
//
// Truncation of log files. Not yet implemented.
( VOID) WriteFile( pDebugPrints->m_LogFileHandle,
pszDump,
cbDump,
&nBytesWritten,
NULL);
}
}
if ( pDebugPrints == NULL
|| pDebugPrints->m_dwOutputFlags & DbgOutputKdb)
{
OutputDebugString( pszDump);
}
SetLastError( dwErr );
return;
} // PuDbgDump()
//
// N.B. For PuDbgCaptureContext() to work properly, the calling function
// *must* be __cdecl, and must have a "normal" stack frame. So, we decorate
// PuDbgAssertFailed() with the __cdecl modifier and disable the frame pointer
// omission (FPO) optimization.
//
#pragma optimize( "y", off ) // disable frame pointer omission (FPO)
VOID
__cdecl
PuDbgAssertFailed(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum,
IN const char * pszExpression,
IN const char * pszMessage)
/*++
This function calls assertion failure and records assertion failure
in log file.
--*/
{
CONTEXT context;
PuDbgCaptureContext( &context );
PuDbgPrint( pDebugPrints, pszFilePath, nLineNum,
" Assertion (%s) Failed: %s\n"
" use !cxr %p to dump context\n",
pszExpression,
pszMessage,
&context);
if (( NULL == pDebugPrints) || (TRUE == pDebugPrints->m_fBreakOnAssert))
{
DebugBreak();
}
return;
} // PuDbgAssertFailed()
#pragma optimize( "", on ) // restore frame pointer omission (FPO)
dllexp VOID
PuDbgPrintCurrentTime(
IN OUT LPDEBUG_PRINTS pDebugPrints,
IN const char * pszFilePath,
IN int nLineNum
)
/*++
This function generates the current time and prints it out to debugger
for tracing out the path traversed, if need be.
Arguments:
pszFile pointer to string containing the name of the file
lineNum line number within the file where this function is called.
Returns:
NO_ERROR always.
--*/
{
PuDbgPrint( pDebugPrints, pszFilePath, nLineNum,
" TickCount = %u\n",
GetTickCount()
);
return;
} // PrintOutCurrentTime()
dllexp DWORD
PuLoadDebugFlagsFromReg(IN HKEY hkey, IN DWORD dwDefault, IN LPDEBUG_PRINTS pDebugPrints)
/*++
This function reads the debug flags assumed to be stored in
the location "DebugFlags" under given key.
If there is any error the default value is returned.
--*/
{
DWORD err;
DWORD dwDebug = dwDefault;
DWORD dwBuffer;
DWORD cbBuffer = sizeof(dwBuffer);
DWORD dwType;
if( hkey != NULL )
{
err = RegQueryValueExA( hkey,
DEBUG_FLAGS_REGISTRY_LOCATION_A,
NULL,
&dwType,
(LPBYTE)&dwBuffer,
&cbBuffer );
if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
{
dwDebug = dwBuffer;
}
cbBuffer = sizeof(DWORD);
if (pDebugPrints)
{
err = RegQueryValueExA( hkey,
DEBUG_BREAK_ENABLED_REGKEYNAME_A,
NULL,
&dwType,
(LPBYTE)&dwBuffer,
&cbBuffer );
if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
{
pDebugPrints->m_fBreakOnAssert = dwBuffer;
}
}
}
return dwDebug;
} // PuLoadDebugFlagsFromReg()
dllexp DWORD
PuLoadDebugFlagsFromRegStr(IN LPCSTR pszRegKey, IN DWORD dwDefault, IN LPDEBUG_PRINTS pDebugPrints)
/*++
Description:
This function reads the debug flags assumed to be stored in
the location "DebugFlags" under given key location in registry.
If there is any error the default value is returned.
Arguments:
pszRegKey - pointer to registry key location from where to read the key from
dwDefault - default values in case the read from registry fails
Returns:
Newly read value on success
If there is any error the dwDefault is returned.
--*/
{
HKEY hkey = NULL;
DWORD dwVal = dwDefault;
DWORD dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
pszRegKey,
0,
KEY_ALL_ACCESS,
&hkey);
if ( dwError == NO_ERROR) {
dwVal = PuLoadDebugFlagsFromReg( hkey, dwDefault, pDebugPrints);
RegCloseKey( hkey);
hkey = NULL;
}
return ( dwVal);
} // PuLoadDebugFlagsFromRegStr()
dllexp DWORD
PuSaveDebugFlagsInReg(IN HKEY hkey, IN DWORD dwDbg)
/*++
Saves the debug flags in registry. On failure returns the error code for
the operation that failed.
--*/
{
DWORD err;
if( hkey == NULL ) {
err = ERROR_INVALID_PARAMETER;
} else {
err = RegSetValueExA(hkey,
DEBUG_FLAGS_REGISTRY_LOCATION_A,
0,
REG_DWORD,
(LPBYTE)&dwDbg,
sizeof(dwDbg) );
}
return (err);
} // PuSaveDebugFlagsInReg()
#endif // !_NO_TRACING_
VOID
PuDbgCaptureContext (
OUT PCONTEXT ContextRecord
)
{
RtlCaptureContext(ContextRecord);
return;
} // PuDbgCaptureContext
/****************************** End of File ******************************/