570 lines
11 KiB
C
570 lines
11 KiB
C
/*
|
|
* debug.c - Debug functions module.
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/* Constants
|
|
************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define LOG_FILE_NAME TEXT("debug.log")
|
|
|
|
#endif
|
|
|
|
|
|
/* Types
|
|
********/
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* debug flags */
|
|
|
|
typedef enum _debugdebugflags
|
|
{
|
|
DEBUG_DFL_ENABLE_TRACE_MESSAGES = 0x0001,
|
|
|
|
DEBUG_DFL_LOG_TRACE_MESSAGES = 0x0002,
|
|
|
|
DEBUG_DFL_DUMP_THREAD_ID = 0x0004,
|
|
|
|
DEBUG_DFL_DUMP_LAST_ERROR = 0x0008,
|
|
|
|
ALL_DEBUG_DFLAGS = (DEBUG_DFL_ENABLE_TRACE_MESSAGES |
|
|
DEBUG_DFL_LOG_TRACE_MESSAGES |
|
|
DEBUG_DFL_DUMP_THREAD_ID |
|
|
DEBUG_DFL_DUMP_LAST_ERROR)
|
|
}
|
|
DEBUGDEBUGFLAGS;
|
|
|
|
#endif
|
|
|
|
|
|
/* Global Variables
|
|
*******************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
#pragma data_seg(DATA_SEG_PER_INSTANCE)
|
|
|
|
/* parameters used by SpewOut() */
|
|
|
|
PUBLIC_DATA DWORD GdwSpewFlags = 0;
|
|
PUBLIC_DATA UINT GuSpewSev = 0;
|
|
PUBLIC_DATA UINT GuSpewLine = 0;
|
|
PUBLIC_DATA LPCTSTR GpcszSpewFile = NULL;
|
|
|
|
#pragma data_seg()
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* Module Variables
|
|
*******************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
#pragma data_seg(DATA_SEG_PER_INSTANCE)
|
|
|
|
/* TLS slot used to store stack depth for SpewOut() indentation */
|
|
|
|
PRIVATE_DATA DWORD MdwStackDepthSlot = TLS_OUT_OF_INDEXES;
|
|
|
|
/* hack stack depth counter used until MdwStackDepthSlot is not available */
|
|
|
|
PRIVATE_DATA ULONG MulcHackStackDepth = 0;
|
|
|
|
#pragma data_seg(DATA_SEG_SHARED)
|
|
|
|
/* debug flags */
|
|
|
|
PRIVATE_DATA DWORD MdwDebugModuleFlags = 0;
|
|
|
|
#pragma data_seg(DATA_SEG_READ_ONLY)
|
|
|
|
/* .ini file switch descriptions */
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisEnableTraceMessages =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("EnableTraceMessages"),
|
|
&MdwDebugModuleFlags,
|
|
DEBUG_DFL_ENABLE_TRACE_MESSAGES
|
|
};
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisLogTraceMessages =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("LogTraceMessages"),
|
|
&MdwDebugModuleFlags,
|
|
DEBUG_DFL_LOG_TRACE_MESSAGES
|
|
};
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisDumpThreadID =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("DumpThreadID"),
|
|
&MdwDebugModuleFlags,
|
|
DEBUG_DFL_DUMP_THREAD_ID
|
|
};
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisDumpLastError =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("DumpLastError"),
|
|
&MdwDebugModuleFlags,
|
|
DEBUG_DFL_DUMP_LAST_ERROR
|
|
};
|
|
|
|
PRIVATE_DATA const PCVOID MrgcpcvisDebugModule[] =
|
|
{
|
|
&cbisLogTraceMessages,
|
|
&cbisEnableTraceMessages,
|
|
&cbisDumpThreadID,
|
|
&cbisDumpLastError
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
PRIVATE_CODE BOOL LogOutputDebugString(LPCTSTR);
|
|
PRIVATE_CODE BOOL IsValidSpewSev(UINT);
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** LogOutputDebugString()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL LogOutputDebugString(LPCTSTR pcsz)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
UINT ucb;
|
|
TCHAR rgchLogFile[MAX_PATH_LEN];
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR));
|
|
|
|
ucb = GetWindowsDirectory(rgchLogFile, ARRAYSIZE(rgchLogFile));
|
|
|
|
if (ucb > 0 && ucb < ARRAYSIZE(rgchLogFile))
|
|
{
|
|
HANDLE hfLog;
|
|
|
|
lstrcat(rgchLogFile, TEXT("\\"));
|
|
lstrcat(rgchLogFile, LOG_FILE_NAME);
|
|
|
|
hfLog = CreateFile(rgchLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
|
|
FILE_FLAG_WRITE_THROUGH, NULL);
|
|
|
|
if (hfLog != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (SetFilePointer(hfLog, 0, NULL, FILE_END) != INVALID_SEEK_POSITION)
|
|
{
|
|
DWORD dwcbWritten;
|
|
|
|
bResult = WriteFile(hfLog, pcsz, lstrlen(pcsz)*SIZEOF(TCHAR), &dwcbWritten, NULL);
|
|
|
|
if (! CloseHandle(hfLog) && bResult)
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidSpewSev()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidSpewSev(UINT uSpewSev)
|
|
{
|
|
BOOL bResult;
|
|
|
|
switch (uSpewSev)
|
|
{
|
|
case SPEW_TRACE:
|
|
case SPEW_WARNING:
|
|
case SPEW_ERROR:
|
|
case SPEW_FATAL:
|
|
bResult = TRUE;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT((TEXT("IsValidSpewSev(): Invalid debug spew severity %u."),
|
|
uSpewSev));
|
|
bResult = FALSE;
|
|
break;
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** SetDebugModuleIniSwitches()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL SetDebugModuleIniSwitches(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = SetIniSwitches(MrgcpcvisDebugModule,
|
|
ARRAY_ELEMENTS(MrgcpcvisDebugModule));
|
|
|
|
ASSERT(FLAGS_ARE_VALID(MdwDebugModuleFlags, ALL_DEBUG_DFLAGS));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** InitDebugModule()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL InitDebugModule(void)
|
|
{
|
|
ASSERT(MdwStackDepthSlot == TLS_OUT_OF_INDEXES);
|
|
|
|
MdwStackDepthSlot = TlsAlloc();
|
|
|
|
if (MdwStackDepthSlot != TLS_OUT_OF_INDEXES)
|
|
{
|
|
EVAL(TlsSetValue(MdwStackDepthSlot, (PVOID)MulcHackStackDepth));
|
|
|
|
TRACE_OUT((TEXT("InitDebugModule(): Using thread local storage slot %lu for debug stack depth counter."),
|
|
MdwStackDepthSlot));
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("InitDebugModule(): TlsAlloc() failed to allocate thread local storage for debug stack depth counter.")));
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
** ExitDebugModule()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void ExitDebugModule(void)
|
|
{
|
|
if (MdwStackDepthSlot != TLS_OUT_OF_INDEXES)
|
|
{
|
|
MulcHackStackDepth = (ULONG)TlsGetValue(MdwStackDepthSlot);
|
|
|
|
/* Leave MulcHackStackDepth == 0 if TlsGetValue() fails. */
|
|
|
|
EVAL(TlsFree(MdwStackDepthSlot));
|
|
MdwStackDepthSlot = TLS_OUT_OF_INDEXES;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** StackEnter()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void StackEnter(void)
|
|
{
|
|
if (MdwStackDepthSlot != TLS_OUT_OF_INDEXES)
|
|
{
|
|
ULONG ulcDepth;
|
|
|
|
ulcDepth = (ULONG)TlsGetValue(MdwStackDepthSlot);
|
|
|
|
ASSERT(ulcDepth < ULONG_MAX);
|
|
|
|
EVAL(TlsSetValue(MdwStackDepthSlot, (PVOID)(ulcDepth + 1)));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(MulcHackStackDepth < ULONG_MAX);
|
|
MulcHackStackDepth++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** StackLeave()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void StackLeave(void)
|
|
{
|
|
if (MdwStackDepthSlot != TLS_OUT_OF_INDEXES)
|
|
{
|
|
ULONG ulcDepth;
|
|
|
|
ulcDepth = (ULONG)TlsGetValue(MdwStackDepthSlot);
|
|
|
|
if (EVAL(ulcDepth > 0))
|
|
EVAL(TlsSetValue(MdwStackDepthSlot, (PVOID)(ulcDepth - 1)));
|
|
}
|
|
else
|
|
{
|
|
if (EVAL(MulcHackStackDepth > 0))
|
|
MulcHackStackDepth--;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** GetStackDepth()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE ULONG GetStackDepth(void)
|
|
{
|
|
ULONG ulcDepth;
|
|
|
|
if (MdwStackDepthSlot != TLS_OUT_OF_INDEXES)
|
|
ulcDepth = (ULONG)TlsGetValue(MdwStackDepthSlot);
|
|
else
|
|
ulcDepth = MulcHackStackDepth;
|
|
|
|
return(ulcDepth);
|
|
}
|
|
|
|
|
|
/*
|
|
** SpewOut()
|
|
**
|
|
** Spews out a formatted message to the debug terminal.
|
|
**
|
|
** Arguments: pcszFormat - pointer to wvsprintf() format string
|
|
** ... - formatting arguments ala wvsprintf()
|
|
**
|
|
** Returns: void
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** N.b., this function assumes the global variables GdwSpewFlags, GuSpewSev,
|
|
** GpcszSpewModule, GpcszSpewFile, and GpcszSpewLine are filled in.
|
|
**
|
|
** SpewOut() uses global variables to set the message parameters in order to
|
|
** permit printf()-style macro expansion.
|
|
*/
|
|
PUBLIC_CODE void __cdecl SpewOut(LPCTSTR pcszFormat, ...)
|
|
{
|
|
va_list arglist;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pcszFormat, CSTR));
|
|
|
|
ASSERT(FLAGS_ARE_VALID(GdwSpewFlags, ALL_SPEW_FLAGS));
|
|
ASSERT(IsValidSpewSev(GuSpewSev));
|
|
ASSERT(IS_FLAG_CLEAR(GdwSpewFlags, SPEW_FL_SPEW_LOCATION) ||
|
|
(IS_VALID_STRING_PTR(GpcszSpewFile, CSTR) &&
|
|
IS_VALID_STRING_PTR(GpcszSpewModule, CSTR)));
|
|
|
|
if (GuSpewSev != SPEW_TRACE || IS_FLAG_SET(MdwDebugModuleFlags, DEBUG_DFL_ENABLE_TRACE_MESSAGES))
|
|
{
|
|
int nMsgLen;
|
|
TCHAR rgchMsg[1024];
|
|
|
|
if (IS_FLAG_SET(GdwSpewFlags, SPEW_FL_SPEW_PREFIX))
|
|
{
|
|
#pragma data_seg(DATA_SEG_SHARED)
|
|
static TCHAR SrgchSpewLeader[] = TEXT(" ");
|
|
#pragma data_seg()
|
|
ULONG ulcStackDepth;
|
|
TCHAR chReplaced;
|
|
LPTSTR pszSpewLeaderEnd;
|
|
LPCTSTR pcszSpewPrefix;
|
|
|
|
/* Build spew message space leader string. */
|
|
|
|
ulcStackDepth = GetStackDepth();
|
|
|
|
if (ulcStackDepth < ARRAYSIZE(SrgchSpewLeader))
|
|
pszSpewLeaderEnd = SrgchSpewLeader + ulcStackDepth;
|
|
else
|
|
pszSpewLeaderEnd = SrgchSpewLeader + ARRAYSIZE(SrgchSpewLeader) - 1;
|
|
|
|
chReplaced = *pszSpewLeaderEnd;
|
|
*pszSpewLeaderEnd = TEXT('\0');
|
|
|
|
/* Determine spew prefix. */
|
|
|
|
switch (GuSpewSev)
|
|
{
|
|
case SPEW_TRACE:
|
|
pcszSpewPrefix = TEXT("t");
|
|
break;
|
|
|
|
case SPEW_WARNING:
|
|
pcszSpewPrefix = TEXT("w");
|
|
break;
|
|
|
|
case SPEW_ERROR:
|
|
pcszSpewPrefix = TEXT("e");
|
|
break;
|
|
|
|
case SPEW_FATAL:
|
|
pcszSpewPrefix = TEXT("f");
|
|
break;
|
|
|
|
default:
|
|
pcszSpewPrefix = TEXT("u");
|
|
ERROR_OUT((TEXT("SpewOut(): Invalid GuSpewSev %u."),
|
|
GuSpewSev));
|
|
break;
|
|
}
|
|
|
|
nMsgLen = wsprintf(rgchMsg, TEXT("%s%s %s "), SrgchSpewLeader, pcszSpewPrefix, GpcszSpewModule);
|
|
|
|
/* Restore spew leader. */
|
|
|
|
*pszSpewLeaderEnd = chReplaced;
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
}
|
|
else
|
|
nMsgLen = 0;
|
|
|
|
/* Append thread ID. */
|
|
|
|
if (IS_FLAG_SET(MdwDebugModuleFlags, DEBUG_DFL_DUMP_THREAD_ID))
|
|
{
|
|
nMsgLen += wsprintf(rgchMsg + nMsgLen, TEXT("%#lx "), GetCurrentThreadId());
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
}
|
|
|
|
/* Build position string. */
|
|
|
|
if (IS_FLAG_SET(GdwSpewFlags, SPEW_FL_SPEW_LOCATION))
|
|
{
|
|
nMsgLen += wsprintf(rgchMsg + nMsgLen, TEXT("(%s line %u): "), GpcszSpewFile, GuSpewLine);
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
}
|
|
|
|
/* Append message string. */
|
|
|
|
va_start(arglist,pcszFormat);
|
|
nMsgLen += wvsprintf(rgchMsg + nMsgLen, pcszFormat, arglist);
|
|
va_end(arglist);
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
|
|
if (IS_FLAG_SET(GdwSpewFlags, DEBUG_DFL_DUMP_THREAD_ID))
|
|
{
|
|
if (GuSpewSev == SPEW_ERROR ||
|
|
GuSpewSev == SPEW_FATAL)
|
|
{
|
|
nMsgLen += wsprintf(rgchMsg + nMsgLen, TEXT(" (GetLastError() == %lu)"), GetLastError());
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
}
|
|
}
|
|
|
|
nMsgLen += wsprintf(rgchMsg + nMsgLen, TEXT("\r\n"));
|
|
|
|
ASSERT(nMsgLen < ARRAYSIZE(rgchMsg));
|
|
|
|
OutputDebugString(rgchMsg);
|
|
|
|
if (IS_FLAG_SET(MdwDebugModuleFlags, DEBUG_DFL_LOG_TRACE_MESSAGES))
|
|
{
|
|
LogOutputDebugString(rgchMsg);
|
|
LogOutputDebugString(TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
/* Break here on errors and fatal errors. */
|
|
|
|
if (GuSpewSev == SPEW_ERROR || GuSpewSev == SPEW_FATAL)
|
|
DebugBreak();
|
|
|
|
return;
|
|
}
|
|
|
|
#endif /* DEBUG */
|