//---------------------------------------------------------------------------- // // cppdbg.cpp // // C++-only debugging support. // // Copyright (C) Microsoft Corporation, 1997. // //---------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #if DBG #include "cppdbg.hpp" #ifdef _ALPHA_ // On Alpha va_list is a structure so it's not compatible with NULL. static va_list NULLVA; #else #define NULLVA NULL #endif static DebugModuleFlags g_FailureFlags[] = { DBG_DECLARE_MODFLAG(DBG_FAILURE, BREAK), DBG_DECLARE_MODFLAG(DBG_FAILURE, OUTPUT), DBG_DECLARE_MODFLAG(DBG_FAILURE, PROMPT), DBG_DECLARE_MODFLAG(DBG_FAILURE, FILENAME_ONLY), 0, NULL, }; static DebugModuleFlags g_OutputFlags[] = { DBG_DECLARE_MODFLAG(DBG_OUTPUT, SUPPRESS), DBG_DECLARE_MODFLAG(DBG_OUTPUT, ALL_MATCH), 0, NULL, }; static char *g_pFlagNames[] = { "AssertFlags", "HrFlags", "OutputFlags", "OutputMask", "UserFlags" }; //---------------------------------------------------------------------------- // // DebugModule::DebugModule // //---------------------------------------------------------------------------- DebugModule::DebugModule(char *pModule, char *pPrefix, DebugModuleFlags *pOutputMasks, UINT uOutputMask, DebugModuleFlags *pUserFlags, UINT uUserFlags) { m_pModule = pModule; m_iModuleStartCol = strlen(m_pModule) + 2; m_pPrefix = pPrefix; m_pModFlags[DBG_ASSERT_FLAGS] = g_FailureFlags; m_pModFlags[DBG_HR_FLAGS] = g_FailureFlags; m_pModFlags[DBG_OUTPUT_FLAGS] = g_OutputFlags; m_pModFlags[DBG_OUTPUT_MASK] = pOutputMasks; m_pModFlags[DBG_USER_FLAGS] = pUserFlags; m_uFlags[DBG_ASSERT_FLAGS] = DBG_FAILURE_OUTPUT | DBG_FAILURE_BREAK | DBG_FAILURE_FILENAME_ONLY; m_uFlags[DBG_HR_FLAGS] = DBG_FAILURE_OUTPUT | DBG_FAILURE_FILENAME_ONLY; m_uFlags[DBG_OUTPUT_FLAGS] = 0; m_uFlags[DBG_OUTPUT_MASK] = uOutputMask; m_uFlags[DBG_USER_FLAGS] = uUserFlags; ReadReg(); } //---------------------------------------------------------------------------- // // DebugModule::OutVa // // Base debug output method. // //---------------------------------------------------------------------------- void DebugModule::OutVa(UINT uMask, char *pFmt, va_list Args) { if (m_uFlags[DBG_OUTPUT_FLAGS] & DBG_OUTPUT_SUPPRESS) { return; } if ((uMask & DBG_MASK_NO_PREFIX) == 0) { OutputDebugStringA(m_pModule); OutputDebugStringA(": "); } char chMsg[1024]; _vsnprintf(chMsg, sizeof(chMsg), pFmt, Args); OutputDebugStringA(chMsg); } //---------------------------------------------------------------------------- // // DebugModule::Out // // Always-output debug output method. // //---------------------------------------------------------------------------- void DebugModule::Out(char *pFmt, ...) { va_list Args; va_start(Args, pFmt); OutVa(0, pFmt, Args); va_end(Args); } //---------------------------------------------------------------------------- // // DebugModule::AssertFailedVa // // Handles assertion failure output and interface. // //---------------------------------------------------------------------------- void DebugModule::AssertFailedVa(char *pFmt, va_list Args, BOOL bNewLine) { if (m_uFlags[DBG_ASSERT_FLAGS] & DBG_FAILURE_OUTPUT) { if (OutPathFile("Assertion failed", m_uFlags[DBG_ASSERT_FLAGS])) { OutVa(DBG_MASK_NO_PREFIX, ":\n ", NULLVA); } else { OutVa(DBG_MASK_NO_PREFIX, ": ", NULLVA); } OutVa(DBG_MASK_NO_PREFIX, pFmt, Args); if (bNewLine) { OutVa(DBG_MASK_NO_PREFIX, "\n", NULLVA); } } if (m_uFlags[DBG_ASSERT_FLAGS] & DBG_FAILURE_BREAK) { DebugBreak(); } else if (m_uFlags[DBG_ASSERT_FLAGS] & DBG_FAILURE_PROMPT) { Prompt(NULL); } } //---------------------------------------------------------------------------- // // DebugModule::AssertFailed // // Handles simple expression assertion failures. // //---------------------------------------------------------------------------- void DebugModule::AssertFailed(char *pExp) { AssertFailedVa(pExp, NULLVA, TRUE); } //---------------------------------------------------------------------------- // // DebugModule::AssertFailedMsg // // Handles assertion failures with arbitrary debug output. // //---------------------------------------------------------------------------- void DebugModule::AssertFailedMsg(char *pFmt, ...) { va_list Args; va_start(Args, pFmt); AssertFailedVa(pFmt, Args, FALSE); va_end(Args); } //---------------------------------------------------------------------------- // // DebugModule::HrFailure // // Handles HRESULT failures. // //---------------------------------------------------------------------------- void DebugModule::HrFailure(HRESULT hr, char *pPrefix) { if (m_uFlags[DBG_HR_FLAGS] & DBG_FAILURE_OUTPUT) { OutPathFile(pPrefix, m_uFlags[DBG_HR_FLAGS]); OutMask(DBG_MASK_FORCE_CONT, ": %s\n", HrString(hr)); } if (m_uFlags[DBG_HR_FLAGS] & DBG_FAILURE_BREAK) { DebugBreak(); } else if (m_uFlags[DBG_HR_FLAGS] & DBG_FAILURE_PROMPT) { Prompt(NULL); } } //---------------------------------------------------------------------------- // // DebugModule::HrStmtFailed // // Handles statement-style HRESULT failures. // //---------------------------------------------------------------------------- void DebugModule::HrStmtFailed(HRESULT hr) { HrFailure(hr, "HR test fail"); } //---------------------------------------------------------------------------- // // DebugModule::ReturnHr // // Handles expression-style HRESULT failures. // //---------------------------------------------------------------------------- HRESULT DebugModule::HrExpFailed(HRESULT hr) { HrFailure(hr, "HR expr fail"); return hr; } //---------------------------------------------------------------------------- // // DebugModule::Prompt // // Allows control over debug options via interactive input. // //---------------------------------------------------------------------------- void DebugModule::Prompt(char *pFmt, ...) { va_list Args; if (pFmt != NULL) { va_start(Args, pFmt); OutVa(0, pFmt, Args); va_end(Args); } #if 0 // ndef WIN95 // This is DEADCODE, that is can be potentially used on NT ONLY to // bring up a debugging prompt. It requires linking with NTDLL.LIB char szInput[512]; char *pIdx; int iIdx; static char szFlagCommands[] = "ahomu"; for (;;) { ULONG uLen; uLen = DbgPrompt("[bgaAFhHmMoOrRuU] ", szInput, sizeof(szInput) - 1); if (uLen < 2) { Out("DbgPrompt failed\n"); DebugBreak(); return; } // ATTENTION - Currently DbgPrompt returns a length that is two // greater than the actual number of characters. Presumably this // is an artifact of the Unicode/ANSI conversion and should // really only be one greater, so attempt to handle both. uLen -= 2; if (szInput[uLen] != 0) { uLen++; szInput[uLen] = 0; } if (uLen < 1) { Out("Empty command ignored\n"); continue; } switch(szInput[0]) { case 'b': DebugBreak(); break; case 'g': return; case 'r': WriteReg(); break; case 'R': ReadReg(); break; case 'a': case 'A': case 'h': case 'H': case 'm': case 'M': case 'o': case 'O': case 'u': case 'U': char chLower; if (szInput[0] >= 'A' && szInput[0] <= 'Z') { chLower = szInput[0] - 'A' + 'a'; } else { chLower = szInput[0]; } pIdx = strchr(szFlagCommands, chLower); if (pIdx == NULL) { // Should never happen. break; } iIdx = (int)((ULONG_PTR)(pIdx - szFlagCommands)); if (szInput[0] == chLower) { // Set. m_uFlags[iIdx] = ParseUint(szInput + 1, m_pModFlags[iIdx]); } // Set or Get. OutUint(g_pFlagNames[iIdx], m_pModFlags[iIdx], m_uFlags[iIdx]); break; case 'F': if (uLen < 2) { Out("'F' must be followed by a flag group specifier\n"); break; } pIdx = strchr(szFlagCommands, szInput[1]); if (pIdx == NULL) { Out("Unknown flag group '%c'\n", szInput[1]); } else { iIdx = (int)((ULONG_PTR)(pIdx - szFlagCommands)); ShowFlags(g_pFlagNames[iIdx], m_pModFlags[iIdx]); } break; default: Out("Unknown command '%c'\n", szInput[0]); break; } } #else OutUint("OutputMask", m_pModFlags[DBG_OUTPUT_MASK], m_uFlags[DBG_OUTPUT_MASK]); Out("Prompt not available\n"); DebugBreak(); #endif } //---------------------------------------------------------------------------- // // DebugModule::OpenDebugKey // // Opens the Direct3D\Debug\m_pModule key. // //---------------------------------------------------------------------------- HKEY DebugModule::OpenDebugKey(void) { HKEY hKey; char szKeyName[128]; strcpy(szKeyName, "Software\\Microsoft\\Direct3D\\Debug\\"); strcat(szKeyName, m_pModule); if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) { return NULL; } else { return hKey; } } //---------------------------------------------------------------------------- // // DebugModule::GetRegUint // // Gets a UINT value from the given key. // //---------------------------------------------------------------------------- UINT DebugModule::GetRegUint(HKEY hKey, char *pValue, UINT uDefault) { DWORD dwType, dwSize; DWORD dwVal; dwSize = sizeof(dwVal); if (RegQueryValueExA(hKey, pValue, NULL, &dwType, (BYTE *)&dwVal, &dwSize) != ERROR_SUCCESS || dwType != REG_DWORD) { return uDefault; } else { return (UINT)dwVal; } } //---------------------------------------------------------------------------- // // DebugModule::SetRegUint // // Sets a UINT value for the given key. // //---------------------------------------------------------------------------- BOOL DebugModule::SetRegUint(HKEY hKey, char *pValue, UINT uValue) { return RegSetValueExA(hKey, pValue, NULL, REG_DWORD, (BYTE *)&uValue, sizeof(uValue)) == ERROR_SUCCESS; } //---------------------------------------------------------------------------- // // DebugModule::ReadReg // // Reads settings from the registry. // //---------------------------------------------------------------------------- void DebugModule::ReadReg(void) { HKEY hKey; hKey = OpenDebugKey(); if (hKey != NULL) { int iIdx; for (iIdx = 0; iIdx < DBG_FLAGS_COUNT; iIdx++) { m_uFlags[iIdx] = GetRegUint(hKey, g_pFlagNames[iIdx], m_uFlags[iIdx]); } RegCloseKey(hKey); } } //---------------------------------------------------------------------------- // // DebugModule::WriteReg // // Writes values to the registry. // //---------------------------------------------------------------------------- void DebugModule::WriteReg(void) { HKEY hKey; hKey = OpenDebugKey(); if (hKey != NULL) { int iIdx; for (iIdx = 0; iIdx < DBG_FLAGS_COUNT; iIdx++) { if (!SetRegUint(hKey, g_pFlagNames[iIdx], m_uFlags[iIdx])) { OutputDebugStringA("Error writing registry information\n"); } } RegCloseKey(hKey); } } //---------------------------------------------------------------------------- // // DebugModule::ParseUint // // Parses a string for a numeric value or a set of flag strings. // //---------------------------------------------------------------------------- UINT DebugModule::ParseUint(char *pString, DebugModuleFlags *pFlags) { UINT uVal; uVal = 0; for (;;) { while (*pString != 0 && (*pString == ' ' || *pString == '\t')) { pString++; } if (*pString == 0) { break; } char *pEnd; int iStepAfter; pEnd = pString; while (*pEnd != 0 && *pEnd != ' ' && *pEnd != '\t') { pEnd++; } iStepAfter = *pEnd != 0 ? 1 : 0; *pEnd = 0; if (*pString >= '0' && *pString <= '9') { uVal |= strtoul(pString, &pString, 0); if (*pString != 0 && *pString != ' ' && *pString != '\t') { Out("Unrecognized characters '%s' after number\n", pString); } } else if (pFlags != NULL) { DebugModuleFlags *pFlag; for (pFlag = pFlags; pFlag->uFlag != 0; pFlag++) { if (!_stricmp(pString, pFlag->pName)) { break; } } if (pFlag->uFlag == 0) { Out("Unrecognized flag string '%s'\n", pString); } else { uVal |= pFlag->uFlag; } } else { Out("No flag definitions, unable to convert '%s'\n", pString); } pString = pEnd + iStepAfter; } return uVal; } //---------------------------------------------------------------------------- // // DebugModule::OutUint // // Displays a UINT as a set of flag strings. // //---------------------------------------------------------------------------- void DebugModule::OutUint(char *pName, DebugModuleFlags *pFlags, UINT uValue) { if (pFlags == NULL || uValue == 0) { Out("%s: 0x%08X\n", pName, uValue); return; } Out("%s:", pName); m_iStartCol = m_iModuleStartCol + strlen(pName) + 1; m_iCol = m_iStartCol; while (uValue != 0) { DebugModuleFlags *pFlag; for (pFlag = pFlags; pFlag->uFlag != 0; pFlag++) { if ((pFlag->uFlag & uValue) == pFlag->uFlag) { AdvanceCols(strlen(pFlag->pName) + 1); OutMask(DBG_MASK_FORCE_CONT, " %s", pFlag->pName); uValue &= ~pFlag->uFlag; break; } } if (pFlag->uFlag == 0) { AdvanceCols(11); OutMask(DBG_MASK_FORCE_CONT, " 0x%X", uValue); uValue = 0; } } OutVa(DBG_MASK_NO_PREFIX, "\n", NULLVA); } //---------------------------------------------------------------------------- // // DebugModule::AdvanceCols // // Determines if there's enough space on the current line for // the given number of columns. If not, a new line is started. // //---------------------------------------------------------------------------- void DebugModule::AdvanceCols(int iCols) { static char szSpaces[] = " "; m_iCol += iCols; if (m_iCol >= 79) { int iSpace; OutVa(DBG_MASK_NO_PREFIX, "\n", NULLVA); // Force a prefix to be printed to start the line. Out(""); m_iCol = m_iModuleStartCol; while (m_iCol < m_iStartCol) { iSpace = (int)min(sizeof(szSpaces) - 1, m_iStartCol - m_iCol); OutMask(DBG_MASK_FORCE_CONT, "%.*s", iSpace, szSpaces); m_iCol += iSpace; } } } //---------------------------------------------------------------------------- // // DebugModule::ShowFlags // // Shows the given flag set. // //---------------------------------------------------------------------------- void DebugModule::ShowFlags(char *pName, DebugModuleFlags *pFlags) { DebugModuleFlags *pFlag; Out("%s:\n", pName); if (pFlags == NULL) { Out(" None defined\n"); } else { for (pFlag = pFlags; pFlag->uFlag != 0; pFlag++) { Out(" 0x%08X - %s\n", pFlag->uFlag, pFlag->pName); } } } //---------------------------------------------------------------------------- // // DebugModule::PathFile // // Returns the trailing filename component or NULL if the path is // only a filename. // //---------------------------------------------------------------------------- char *DebugModule::PathFile(char *pPath) { char *pFile, *pSlash, *pBack, *pColon; pBack = strrchr(pPath, '\\'); pSlash = strrchr(pPath, '/'); pColon = strrchr(pPath, ':'); pFile = pBack; if (pSlash > pFile) { pFile = pSlash; } if (pColon > pFile) { pFile = pColon; } return pFile != NULL ? pFile + 1 : NULL; } //---------------------------------------------------------------------------- // // DebugModule::OutPathFile // // Outputs the given string plus a path and filename. // Returns whether the full path was output or not. // //---------------------------------------------------------------------------- BOOL DebugModule::OutPathFile(char *pPrefix, UINT uFailureFlags) { char *pFile; if (uFailureFlags & DBG_FAILURE_FILENAME_ONLY) { pFile = PathFile(m_pFile); } else { pFile = NULL; } if (pFile == NULL) { Out("%s %s(%d)", pPrefix, m_pFile, m_iLine); return TRUE; } else { Out("%s <>\\%s(%d)", pPrefix, pFile, m_iLine); return FALSE; } } //---------------------------------------------------------------------------- // // Global debug module. // //---------------------------------------------------------------------------- DBG_DECLARE_ONCE(Global, G, NULL, 0, NULL, 0); #endif // #if DBG