/* * debspew.c - Debug spew functions module. */ /* Headers **********/ #include "project.h" #pragma hdrstop #ifdef DEBUG /* Types ********/ /* debug flags */ typedef enum _debugdebugflags { DEBUG_DFL_ENABLE_TRACE_MESSAGES = 0x0001, DEBUG_DFL_LOG_TRACE_MESSAGES = 0x0002, DEBUG_DFL_DUMP_THREAD_ID = 0x0004, ALL_DEBUG_DFLAGS = (DEBUG_DFL_ENABLE_TRACE_MESSAGES | DEBUG_DFL_LOG_TRACE_MESSAGES | DEBUG_DFL_DUMP_THREAD_ID) } DEBUGDEBUGFLAGS; /* Module Constants *******************/ #pragma data_seg(DATA_SEG_READ_ONLY) /* debug message output log file */ PRIVATE_DATA CCHAR s_cszLogFile[] = "debug.log"; #pragma data_seg() /* Global Variables *******************/ #pragma data_seg(DATA_SEG_PER_INSTANCE) /* parameters used by SpewOut() */ PUBLIC_DATA char SrgchSpewLeader[] = " "; PUBLIC_DATA DWORD g_dwSpewFlags = 0; PUBLIC_DATA UINT g_uSpewSev = 0; PUBLIC_DATA UINT g_uSpewLine = 0; PUBLIC_DATA PCSTR g_pcszSpewFile = NULL; #pragma data_seg() /* Module Variables *******************/ #pragma data_seg(DATA_SEG_PER_INSTANCE) /* TLS slot used to store stack depth for SpewOut() indentation */ PRIVATE_DATA DWORD s_dwStackDepthSlot = TLS_OUT_OF_INDEXES; /* hack stack depth counter used until s_dwStackDepthSlot is not available */ PRIVATE_DATA ULONG s_ulcHackStackDepth = 0; #pragma data_seg(DATA_SEG_SHARED) /* debug flags */ PRIVATE_DATA DWORD s_dwDebugModuleFlags = 0; #pragma data_seg(DATA_SEG_READ_ONLY) /* .ini file switch descriptions */ PRIVATE_DATA CBOOLINISWITCH s_cbisEnableTraceMessages = { IST_BOOL, "EnableTraceMessages", &s_dwDebugModuleFlags, DEBUG_DFL_ENABLE_TRACE_MESSAGES }; PRIVATE_DATA CBOOLINISWITCH s_cbisLogTraceMessages = { IST_BOOL, "LogTraceMessages", &s_dwDebugModuleFlags, DEBUG_DFL_LOG_TRACE_MESSAGES }; PRIVATE_DATA CBOOLINISWITCH s_cbisDumpThreadID = { IST_BOOL, "DumpThreadID", &s_dwDebugModuleFlags, DEBUG_DFL_DUMP_THREAD_ID }; PRIVATE_DATA const PCVOID s_rgcpcvisDebugModule[] = { &s_cbisLogTraceMessages, &s_cbisEnableTraceMessages, &s_cbisDumpThreadID }; #pragma data_seg() /***************************** Private Functions *****************************/ /* Module Prototypes ********************/ PRIVATE_CODE BOOL LogOutputDebugString(PCSTR); PRIVATE_CODE BOOL IsValidSpewSev(UINT); /* ** LogOutputDebugString() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL LogOutputDebugString(PCSTR pcsz) { BOOL bResult = FALSE; UINT ucb; char rgchLogFile[MAX_PATH_LEN]; ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR)); ucb = GetWindowsDirectory(rgchLogFile, sizeof(rgchLogFile)); if (ucb > 0 && ucb < sizeof(rgchLogFile)) { HANDLE hfLog; lstrcat(rgchLogFile, "\\"); lstrcat(rgchLogFile, s_cszLogFile); 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), &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(("IsValidSpewSev(): Invalid debug spew severity %u.", uSpewSev)); bResult = FALSE; break; } return(bResult); } /****************************** Public Functions *****************************/ /* ** SetDebugModuleIniSwitches() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL SetDebugModuleIniSwitches(void) { BOOL bResult; bResult = SetIniSwitches(s_rgcpcvisDebugModule, ARRAY_ELEMENTS(s_rgcpcvisDebugModule)); ASSERT(FLAGS_ARE_VALID(s_dwDebugModuleFlags, ALL_DEBUG_DFLAGS)); return(bResult); } /* ** InitDebugModule() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL InitDebugModule(void) { ASSERT(s_dwStackDepthSlot == TLS_OUT_OF_INDEXES); s_dwStackDepthSlot = TlsAlloc(); if (s_dwStackDepthSlot != TLS_OUT_OF_INDEXES) { EVAL(TlsSetValue(s_dwStackDepthSlot, IntToPtr(s_ulcHackStackDepth))); TRACE_OUT(("InitDebugModule(): Using thread local storage slot %lu for debug stack depth counter.", s_dwStackDepthSlot)); } else WARNING_OUT(("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 (s_dwStackDepthSlot != TLS_OUT_OF_INDEXES) { s_ulcHackStackDepth = PtrToUlong(TlsGetValue(s_dwStackDepthSlot)); /* Leave s_ulcHackStackDepth == 0 if TlsGetValue() fails. */ EVAL(TlsFree(s_dwStackDepthSlot)); s_dwStackDepthSlot = TLS_OUT_OF_INDEXES; } return; } /* ** StackEnter() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void StackEnter(void) { if (s_dwStackDepthSlot != TLS_OUT_OF_INDEXES) { ULONG ulcDepth; ulcDepth = PtrToUlong(TlsGetValue(s_dwStackDepthSlot)); ASSERT(ulcDepth < ULONG_MAX); EVAL(TlsSetValue(s_dwStackDepthSlot, IntToPtr(ulcDepth + 1))); } else { ASSERT(s_ulcHackStackDepth < ULONG_MAX); s_ulcHackStackDepth++; } return; } /* ** StackLeave() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void StackLeave(void) { if (s_dwStackDepthSlot != TLS_OUT_OF_INDEXES) { ULONG ulcDepth; ulcDepth = PtrToUlong(TlsGetValue(s_dwStackDepthSlot)); if (EVAL(ulcDepth > 0)) EVAL(TlsSetValue(s_dwStackDepthSlot, IntToPtr(ulcDepth - 1))); } else { if (EVAL(s_ulcHackStackDepth > 0)) s_ulcHackStackDepth--; } return; } /* ** GetStackDepth() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE ULONG GetStackDepth(void) { ULONG ulcDepth; if (s_dwStackDepthSlot != TLS_OUT_OF_INDEXES) ulcDepth = PtrToUlong(TlsGetValue(s_dwStackDepthSlot)); else ulcDepth = s_ulcHackStackDepth; 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 g_dwSpewFlags, g_uSpewSev, ** g_pcszSpewModule, g_pcszSpewFile, and g_pcszSpewLine are filled in. ** ** SpewOut() uses global variables to set the message parameters in order to ** permit printf()-style macro expansion. */ PUBLIC_CODE void SpewOut(PCSTR pcszFormat, ...) { ASSERT(IS_VALID_STRING_PTR(pcszFormat, CSTR)); ASSERT(FLAGS_ARE_VALID(g_dwSpewFlags, ALL_SPEW_FLAGS)); ASSERT(IsValidSpewSev(g_uSpewSev)); ASSERT(IS_FLAG_CLEAR(g_dwSpewFlags, SPEW_FL_SPEW_LOCATION) || (IS_VALID_STRING_PTR(g_pcszSpewFile, CSTR) && IS_VALID_STRING_PTR(g_pcszSpewModule, CSTR))); if (g_uSpewSev != SPEW_TRACE || IS_FLAG_SET(s_dwDebugModuleFlags, DEBUG_DFL_ENABLE_TRACE_MESSAGES)) { int nMsgLen; char rgchMsg[1024]; va_list nextArg; if (IS_FLAG_SET(g_dwSpewFlags, SPEW_FL_SPEW_PREFIX)) { ULONG ulcStackDepth; char chReplaced; PSTR pszSpewLeaderEnd; PCSTR pcszSpewPrefix; /* Build spew message space leader string. */ ulcStackDepth = GetStackDepth(); if (ulcStackDepth < sizeof(SrgchSpewLeader)) pszSpewLeaderEnd = SrgchSpewLeader + ulcStackDepth; else pszSpewLeaderEnd = SrgchSpewLeader + sizeof(SrgchSpewLeader) - 1; chReplaced = *pszSpewLeaderEnd; *pszSpewLeaderEnd = '\0'; /* Determine spew prefix. */ switch (g_uSpewSev) { case SPEW_TRACE: pcszSpewPrefix = "t"; break; case SPEW_WARNING: pcszSpewPrefix = "w"; break; case SPEW_ERROR: pcszSpewPrefix = "e"; break; case SPEW_FATAL: pcszSpewPrefix = "f"; break; default: pcszSpewPrefix = "u"; ERROR_OUT(("SpewOut(): Invalid g_uSpewSev %u.", g_uSpewSev)); break; } nMsgLen = wsprintf(rgchMsg, "%s%s %s ", SrgchSpewLeader, pcszSpewPrefix, g_pcszSpewModule); /* Restore spew leader. */ *pszSpewLeaderEnd = chReplaced; ASSERT(nMsgLen < sizeof(rgchMsg)); } else nMsgLen = 0; /* Append thread ID. */ if (IS_FLAG_SET(s_dwDebugModuleFlags, DEBUG_DFL_DUMP_THREAD_ID)) { nMsgLen += wsprintf(rgchMsg + nMsgLen, "%#lx ", GetCurrentThreadId()); ASSERT(nMsgLen < sizeof(rgchMsg)); } /* Build position string. */ if (IS_FLAG_SET(g_dwSpewFlags, SPEW_FL_SPEW_LOCATION)) { nMsgLen += wsprintf(rgchMsg + nMsgLen, "(%s line %u): ", g_pcszSpewFile, g_uSpewLine); ASSERT(nMsgLen < sizeof(rgchMsg)); } /* Append message string. */ va_start(nextArg, pcszFormat); nMsgLen += wvsprintf(rgchMsg + nMsgLen, pcszFormat, nextArg); va_end(nextArg); ASSERT(nMsgLen < sizeof(rgchMsg)); if (g_uSpewSev == SPEW_ERROR || g_uSpewSev == SPEW_FATAL) { nMsgLen += wsprintf(rgchMsg + nMsgLen, " (GetLastError() == %lu)", GetLastError()); ASSERT(nMsgLen < sizeof(rgchMsg)); } nMsgLen += wsprintf(rgchMsg + nMsgLen, "\r\n"); ASSERT(nMsgLen < sizeof(rgchMsg)); OutputDebugString(rgchMsg); if (IS_FLAG_SET(s_dwDebugModuleFlags, DEBUG_DFL_LOG_TRACE_MESSAGES)) LogOutputDebugString(rgchMsg); } /* Break here on errors and fatal errors. */ #ifndef MAINWIN if (g_uSpewSev == SPEW_ERROR || g_uSpewSev == SPEW_FATAL) DebugBreak(); #endif return; } #endif /* DEBUG */