Improve dbg code to work Assertion properly

This commit is contained in:
s1lentq 2023-12-14 02:16:59 +07:00
parent 806f5651de
commit f97c9d9f46
17 changed files with 1076 additions and 516 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.bat
*.log
*.lnk
*.aps
**/msvc/Debug*
**/msvc/Release*
**/msvc/Tests

View File

@ -161,6 +161,9 @@ set(SHARED_SRCS
"public/FileSystem.cpp"
"public/interface.cpp"
"public/MemPool.cpp"
"public/MemPool.cpp"
"public/tier0/dbg.cpp"
"public/tier0/platform_posix.cpp"
)
set(GAMEDLL_SRCS

View File

@ -228,8 +228,13 @@ void GameDLL_SwapTeams_f()
#endif // REGAMEDLL_ADD
SpewRetval_t GameDLL_SpewHandler(SpewType_t spewType, int level, const char *pMsg);
void EXT_FUNC GameDLLInit()
{
// By default, direct dbg reporting...
SpewOutputFunc(GameDLL_SpewHandler);
g_pskill = CVAR_GET_POINTER("skill");
g_psv_gravity = CVAR_GET_POINTER("sv_gravity");
g_psv_aim = CVAR_GET_POINTER("sv_aim");
@ -455,3 +460,28 @@ void EXT_FUNC GameDLLInit()
#endif
}
SpewRetval_t GameDLL_SpewHandler(SpewType_t spewType, int level, const char *pMsg)
{
bool bSpewPrint = (CVAR_GET_FLOAT("developer") >= level);
switch (spewType)
{
case SPEW_LOG:
case SPEW_MESSAGE:
if (bSpewPrint) UTIL_ServerPrint("%s", pMsg);
break;
case SPEW_WARNING:
if (bSpewPrint) UTIL_ServerPrint("Warning: %s", pMsg);
break;
case SPEW_ERROR:
Sys_Error("%s", pMsg);
return SPEW_ABORT; // fatal error, terminate it!
case SPEW_ASSERT:
UTIL_ServerPrint("Assert: %s", pMsg);
return SPEW_DEBUGGER; // assert always tries to debugger break
default:
break;
}
return SPEW_CONTINUE; // spew handled, continue on
}

View File

@ -16,22 +16,7 @@ C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pEnginefuncsTable, global
Regamedll_Game_Init();
}
#ifdef _WIN32
// DLL entry point
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
}
else if (fdwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}
#else // _WIN32
#if defined(_LINUX)
void __attribute__((constructor)) DllMainLoad()
{
@ -41,4 +26,4 @@ void __attribute__((destructor)) DllMainUnload()
{
}
#endif // _WIN32
#endif // _LINUX

View File

@ -1819,10 +1819,11 @@ void NORETURN Sys_Error(const char *error, ...)
CONSOLE_ECHO("FATAL ERROR (shutting down): %s\n", text);
//TerminateProcess(GetCurrentProcess(), 1);
int *null = 0;
*null = 0;
exit(-1);
#if defined(_WIN32)
MessageBoxA(NULL, text, "Fatal error", MB_ICONERROR | MB_OK);
#endif
exit(EXIT_FAILURE);
}
int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, int &playersInCount, int &playersOutCount, CPlayerInVolumeAdapter *pAdapter)

View File

@ -756,9 +756,8 @@ int Q_UnicodeConvertT(const SrcType *pIn, int nInChars, DstType *pOut, int nOutB
if (bErr)
{
#ifdef _DEBUG
AssertMsg(!(ePolicy & _STRINGCONVERTFLAG_ASSERT), "invalid Unicode byte sequence");
#endif
DbgAssertMsg(!(ePolicy & _STRINGCONVERTFLAG_ASSERT), "invalid Unicode byte sequence");
if (ePolicy & _STRINGCONVERTFLAG_SKIP)
{
nOut -= EncodeDstLen(uVal);
@ -793,9 +792,8 @@ int Q_UnicodeConvertT(const SrcType *pIn, int nInChars, DstType *pOut, int nOutB
nOut += EncodeDst(uVal, pOut + nOut);
if (bErr)
{
#ifdef _DEBUG
AssertMsg(!(ePolicy & _STRINGCONVERTFLAG_ASSERT), "invalid Unicode byte sequence");
#endif
DbgAssertMsg(!(ePolicy & _STRINGCONVERTFLAG_ASSERT), "invalid Unicode byte sequence");
if (ePolicy & _STRINGCONVERTFLAG_SKIP)
{
nOut -= EncodeDstLen(uVal);

View File

@ -540,6 +540,7 @@
<ClCompile Include="..\public\FileSystem.cpp" />
<ClCompile Include="..\public\interface.cpp" />
<ClCompile Include="..\public\MemPool.cpp" />
<ClCompile Include="..\public\tier0\assert_dialog.cpp" />
<ClCompile Include="..\public\tier0\dbg.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</ExcludedFromBuild>
@ -548,6 +549,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Play|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\public\tier0\platform_win32.cpp" />
<ClCompile Include="..\public\utlsymbol.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Play|Win32'">true</ExcludedFromBuild>
@ -787,6 +789,7 @@
<ClInclude Include="..\public\regamedll\regamedll_api.h" />
<ClInclude Include="..\public\tier0\dbg.h" />
<ClInclude Include="..\public\tier0\platform.h" />
<ClInclude Include="..\public\tier0\resource.h" />
<ClInclude Include="..\public\utlarray.h" />
<ClInclude Include="..\public\utlmemory.h" />
<ClInclude Include="..\public\utlrbtree.h" />
@ -810,6 +813,9 @@
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\public\tier0\assert_dialog.rc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{70A2B904-B7DB-4C48-8DE0-AF567360D572}</ProjectGuid>
<RootNamespace>ReGameDLL</RootNamespace>

View File

@ -555,6 +555,12 @@
<ClCompile Include="..\public\FileSystem.cpp">
<Filter>public</Filter>
</ClCompile>
<ClCompile Include="..\public\tier0\platform_win32.cpp">
<Filter>public\tier0</Filter>
</ClCompile>
<ClCompile Include="..\public\tier0\assert_dialog.cpp">
<Filter>public\tier0</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\version\version.h">
@ -1055,5 +1061,13 @@
<ClInclude Include="..\public\utlarray.h">
<Filter>public</Filter>
</ClInclude>
<ClInclude Include="..\public\tier0\resource.h">
<Filter>public\tier0</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\public\tier0\assert_dialog.rc">
<Filter>public\tier0</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,302 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "precompiled.h"
#include "resource.h"
class CDialogInitInfo
{
public:
const char *m_pFilename;
int m_iLine;
const char *m_pExpression;
};
class CAssertDisable
{
public:
char m_Filename[MAX_OSPATH];
// If these are not -1, then this CAssertDisable only disables asserts on lines between
// these values (inclusive).
int m_LineMin;
int m_LineMax;
// Decremented each time we hit this assert and ignore it, until it's 0.
// Then the CAssertDisable is removed.
// If this is -1, then we always ignore this assert.
int m_nIgnoreTimes;
CAssertDisable *m_pNext;
};
static CDialogInitInfo g_Info{};
static bool g_bAssertsEnabled = true;
static CAssertDisable *g_pAssertDisables = nullptr;
// Set to true if they want to break in the debugger
static bool g_bBreak = false;
// Internal functions
static HINSTANCE g_hTier0Instance = nullptr;
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
g_hTier0Instance = hinstDLL;
return true;
}
static bool IsDebugBreakEnabled()
{
static bool bResult = (Q_strstr(Plat_GetCommandLine(), "-debugbreak") != nullptr);
return bResult;
}
static bool AreAssertsDisabled()
{
static bool bResult = (Q_strstr(Plat_GetCommandLine(), "-noassert") != nullptr);
return bResult;
}
static bool AreAssertsEnabledInFileLine(const char *pFilename, int iLine)
{
CAssertDisable **pPrev = &g_pAssertDisables;
CAssertDisable *pNext = nullptr;
for (CAssertDisable *pCur = g_pAssertDisables; pCur; pCur = pNext)
{
pNext = pCur->m_pNext;
if (Q_stricmp(pFilename, pCur->m_Filename) == 0)
{
// Are asserts disabled in the whole file?
bool bAssertsEnabled = true;
if (pCur->m_LineMin == -1 && pCur->m_LineMax == -1)
bAssertsEnabled = false;
// Are asserts disabled on the specified line?
if (iLine >= pCur->m_LineMin && iLine <= pCur->m_LineMax)
bAssertsEnabled = false;
if (!bAssertsEnabled)
{
// If this assert is only disabled for the next N times, then countdown..
if (pCur->m_nIgnoreTimes > 0)
{
pCur->m_nIgnoreTimes--;
if (pCur->m_nIgnoreTimes == 0)
{
// Remove this one from the list.
*pPrev = pNext;
delete pCur;
continue;
}
}
return false;
}
}
pPrev = &pCur->m_pNext;
}
return true;
}
CAssertDisable *CreateNewAssertDisable(const char *pFilename)
{
CAssertDisable *pDisable = new CAssertDisable;
pDisable->m_pNext = g_pAssertDisables;
g_pAssertDisables = pDisable;
pDisable->m_LineMin = pDisable->m_LineMax = -1;
pDisable->m_nIgnoreTimes = -1;
Q_strlcpy(pDisable->m_Filename, g_Info.m_pFilename);
return pDisable;
}
void IgnoreAssertsInCurrentFile()
{
CreateNewAssertDisable(g_Info.m_pFilename);
}
CAssertDisable *IgnoreAssertsNearby(int nRange)
{
CAssertDisable *pDisable = CreateNewAssertDisable(g_Info.m_pFilename);
pDisable->m_LineMin = g_Info.m_iLine - nRange;
pDisable->m_LineMax = g_Info.m_iLine - nRange;
return pDisable;
}
static int g_iLastLineRange = 5;
static int g_nLastIgnoreNumTimes = 1;
int CALLBACK AssertDialogProc(
HWND hDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
SetDlgItemText(hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression);
SetDlgItemText(hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename);
SetDlgItemInt(hDlg, IDC_LINE_CONTROL, g_Info.m_iLine, false);
SetDlgItemInt(hDlg, IDC_IGNORE_NUMLINES, g_iLastLineRange, false);
SetDlgItemInt(hDlg, IDC_IGNORE_NUMTIMES, g_nLastIgnoreNumTimes, false);
// Center the dialog.
RECT rcDlg, rcDesktop;
GetWindowRect(hDlg, &rcDlg);
GetWindowRect(GetDesktopWindow(), &rcDesktop);
SetWindowPos(hDlg, HWND_TOP,
((rcDesktop.right - rcDesktop.left) - (rcDlg.right - rcDlg.left)) / 2,
((rcDesktop.bottom - rcDesktop.top) - (rcDlg.bottom - rcDlg.top)) / 2,
0, 0,
SWP_NOSIZE
);
return TRUE;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
// Ignore this assert N times
case IDC_IGNORE_THIS:
{
BOOL bTranslated = false;
UINT value = GetDlgItemInt(hDlg, IDC_IGNORE_NUMTIMES, &bTranslated, false);
if (bTranslated && value > 1)
{
CAssertDisable *pDisable = IgnoreAssertsNearby(0);
pDisable->m_nIgnoreTimes = value - 1;
g_nLastIgnoreNumTimes = value;
}
EndDialog(hDlg, 0);
return TRUE;
}
case IDC_IGNORE_NEARBY:
{
BOOL bTranslated = false;
UINT value = GetDlgItemInt(hDlg, IDC_IGNORE_NUMLINES, &bTranslated, false);
if (!bTranslated || value < 1)
return TRUE;
IgnoreAssertsNearby(value);
EndDialog(hDlg, 0);
return TRUE;
}
case IDC_IGNORE_FILE:
IgnoreAssertsInCurrentFile();
EndDialog(hDlg, 0);
return TRUE;
// Always ignore this assert
case IDC_IGNORE_ALWAYS:
IgnoreAssertsNearby(0);
EndDialog(hDlg, 0);
return TRUE;
case IDC_IGNORE_ALL:
g_bAssertsEnabled = false;
EndDialog(hDlg, 0);
return TRUE;
case IDC_BREAK:
g_bBreak = true;
EndDialog(hDlg, 0);
return TRUE;
}
case WM_KEYDOWN:
{
// Escape?
if (wParam == 2)
{
// Ignore this assert
EndDialog(hDlg, 0);
return TRUE;
}
break;
}
return TRUE;
}
}
return FALSE;
}
static HWND g_hBestParentWindow = nullptr;
static BOOL CALLBACK ParentWindowEnumProc(
HWND hWnd, // handle to parent window
LPARAM lParam // application-defined value
)
{
if (IsWindowVisible(hWnd))
{
DWORD procID;
GetWindowThreadProcessId(hWnd, &procID);
if (procID == (DWORD)lParam)
{
g_hBestParentWindow = hWnd;
return FALSE; // don't iterate any more.
}
}
return TRUE;
}
static HWND FindLikelyParentWindow()
{
// Enumerate top-level windows and take the first visible one with our processID.
g_hBestParentWindow = nullptr;
EnumWindows(ParentWindowEnumProc, GetCurrentProcessId());
return g_hBestParentWindow;
}
bool DoNewAssertDialog(const char *pFilename, int line, const char *pExpression)
{
if (AreAssertsDisabled())
return false;
// If they have the old mode enabled (always break immediately), then just break right into
// the debugger like we used to do.
if (IsDebugBreakEnabled())
return true;
// Have ALL Asserts been disabled?
if (!g_bAssertsEnabled)
return false;
// Has this specific Assert been disabled?
if (!AreAssertsEnabledInFileLine(pFilename, line))
return false;
// Now create the dialog.
g_Info.m_pFilename = pFilename;
g_Info.m_iLine = line;
g_Info.m_pExpression = pExpression;
g_bBreak = false;
HWND hParentWindow = FindLikelyParentWindow();
DialogBox(g_hTier0Instance, MAKEINTRESOURCE(IDD_ASSERT_DIALOG), hParentWindow, AssertDialogProc);
return g_bBreak;
}

View File

@ -0,0 +1,117 @@
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_ASSERT_DIALOG DIALOG DISCARDABLE 0, 0, 268, 158
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Assert"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "File:",IDC_NOID,7,7,23,9
LTEXT "c:/gsclient/src/blah.cpp",IDC_FILENAME_CONTROL,36,7,217,8
LTEXT "Line:",IDC_NOID,7,18,23,9
LTEXT "45",IDC_LINE_CONTROL,36,18,217,8
LTEXT "Assert:",IDC_NOID,7,29,23,9
CONTROL "ASSERT MESSAGE",IDC_ASSERT_MSG_CTRL,"Static",
SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,36,29,217,9
DEFPUSHBUTTON "&Break in Debugger",IDC_BREAK,7,49,98,14
PUSHBUTTON "&Ignore This Assert",IDC_IGNORE_THIS,7,66,98,14
EDITTEXT IDC_IGNORE_NUMTIMES,110,66,24,14,ES_AUTOHSCROLL |
ES_NUMBER
LTEXT "time(s).",IDC_NOID,138,68,23,8
PUSHBUTTON "Always Ignore &This Assert",IDC_IGNORE_ALWAYS,7,84,98,
14
PUSHBUTTON "Ignore &Nearby Asserts",IDC_IGNORE_NEARBY,7,102,98,14
LTEXT "within",IDC_NOID,109,105,19,8
EDITTEXT IDC_IGNORE_NUMLINES,131,102,40,14,ES_AUTOHSCROLL |
ES_NUMBER
LTEXT "lines.",IDC_NOID,175,105,17,8
PUSHBUTTON "Ignore Asserts in This &File",IDC_IGNORE_FILE,7,120,98,
14
PUSHBUTTON "Ignore &All Asserts",IDC_IGNORE_ALL,7,137,98,14
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_ASSERT_DIALOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 261
TOPMARGIN, 7
BOTTOMMARGIN, 151
END
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -1,50 +1,17 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "precompiled.h"
// Internal structures
enum
{
MAX_GROUP_NAME_LENGTH = 48
};
struct SpewGroup_t
{
char m_GroupName[MAX_GROUP_NAME_LENGTH];
int m_Level;
};
// Templates to assist in validating pointers:
void _AssertValidReadPtr(void *ptr, int count)
{
#ifdef _WIN32
#if defined(_WIN32)
Assert(!IsBadReadPtr(ptr, count));
#else
Assert(ptr);
@ -54,7 +21,7 @@ void _AssertValidReadPtr(void *ptr, int count)
void _AssertValidWritePtr(void *ptr, int count)
{
#ifdef _WIN32
#if defined(_WIN32)
Assert(!IsBadWritePtr(ptr, count));
#else
Assert(ptr);
@ -63,26 +30,29 @@ void _AssertValidWritePtr(void *ptr, int count)
void _AssertValidReadWritePtr(void *ptr, int count)
{
#ifdef _WIN32
#if defined(_WIN32)
Assert(!(IsBadWritePtr(ptr, count) || IsBadReadPtr(ptr, count)));
#else
Assert(ptr);
#endif
}
#if defined(DBGFLAG_ASSERT)
void AssertValidStringPtr(const char *ptr, int maxchar)
{
#ifdef _WIN32
#if defined(_WIN32)
Assert(!IsBadStringPtr(ptr, maxchar));
#else
Assert(ptr);
#endif
}
#endif // DBGFLAG_ASSERT
// Globals
SpewRetval_t DefaultSpewFunc(SpewType_t type, const char *pMsg)
SpewRetval_t DefaultSpewFunc(SpewType_t type, int level, const char *pMsg)
{
printf("%s", pMsg);
if (type == SPEW_ASSERT)
return SPEW_DEBUGGER;
else if (type == SPEW_ERROR)
@ -93,13 +63,10 @@ SpewRetval_t DefaultSpewFunc(SpewType_t type, const char *pMsg)
static SpewOutputFunc_t s_SpewOutputFunc = DefaultSpewFunc;
static const char *s_pFileName;
static int s_Line;
static SpewType_t s_SpewType;
static SpewGroup_t *s_pSpewGroups = 0;
static int s_GroupCount = 0;
static int s_DefaultLevel = 0;
static const char *s_pMessage = nullptr;
static const char *s_pFileName = nullptr;
static int s_Line = 0;
static SpewType_t s_SpewType = SPEW_MESSAGE;
// Spew output management.
void SpewOutputFunc(SpewOutputFunc_t func)
@ -119,12 +86,24 @@ SpewOutputFunc_t GetSpewOutputFunc()
}
}
void _ExitOnFatalAssert()
{
Msg("Fatal assert failed: %s, file %s line %d. Application exiting.\n", s_pMessage, s_pFileName, s_Line);
#if defined(WIN32)
TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); // die, die RIGHT NOW! (don't call exit() so destructors will not get run)
#else
exit(EXIT_FAILURE); // forcefully shutdown of the process without destructors running
#endif
}
// Spew functions
void _SpewInfo(SpewType_t type, const char *pFile, int line)
{
// Only grab the file name. Ignore the path.
const char *pSlash = strrchr(pFile, '\\');
const char *pSlash2 = strrchr(pFile, '/');
// Only grab the file name. Ignore the path
const char *pSlash = Q_strrchr(pFile, '\\');
const char *pSlash2 = Q_strrchr(pFile, '/');
if (pSlash < pSlash2) pSlash = pSlash2;
s_pFileName = pSlash ? pSlash + 1 : pFile;
@ -132,41 +111,67 @@ void _SpewInfo(SpewType_t type, const char *pFile, int line)
s_SpewType = type;
}
SpewRetval_t _SpewMessage(SpewType_t spewType, const char *pMsgFormat, va_list args)
SpewRetval_t _SpewMessageV(SpewType_t spewType, int level, const char *pMsgFormat, va_list args)
{
char pTempBuffer[1024];
if (level < 0) level = DBG_DEFAULT_LEVEL;
static char szTempBuffer[4096]{};
szTempBuffer[0] = '\0';
s_pMessage = szTempBuffer;
// check that we won't artifically truncate the string
assert(Q_strlen(pMsgFormat) < sizeof(szTempBuffer));
// Printf the file and line for warning + assert only...
int len = 0;
if ((spewType == SPEW_ASSERT))
if (spewType == SPEW_ASSERT)
{
len = sprintf(pTempBuffer, "%s (%d) : ", s_pFileName, s_Line);
len = Q_snprintf(szTempBuffer, sizeof(szTempBuffer), "%s (%d) : ", s_pFileName, s_Line);
}
if (len == -1)
{
return SPEW_ABORT;
}
// Create the message....
len += vsprintf(&pTempBuffer[len], pMsgFormat, args);
len += Q_vsnprintf(&szTempBuffer[len], sizeof(szTempBuffer) - len, pMsgFormat, args);
// Use normal assert here; to avoid recursion
assert(len < sizeof(szTempBuffer));
// Add \n for warning and assert
if ((spewType == SPEW_ASSERT))
{
len += sprintf(&pTempBuffer[len], "\n");
len += Q_snprintf(&szTempBuffer[len], sizeof(szTempBuffer) - len, "\n");
Plat_OutputDebugString(szTempBuffer);
}
assert(len < 1024); // use normal assert here; to avoid recursion.
// use normal assert here; to avoid recursion
assert((size_t)len < (sizeof(szTempBuffer) / sizeof(szTempBuffer[0]) - 1));
assert(s_SpewOutputFunc);
// direct it to the appropriate target(s)
SpewRetval_t ret = s_SpewOutputFunc(spewType, pTempBuffer);
SpewRetval_t ret = s_SpewOutputFunc(spewType, level, szTempBuffer);
switch (ret)
{
// Put the break into the macro so it would occur in the right place
//case SPEW_DEBUGGER:
// DebuggerBreak();
// break;
case SPEW_DEBUGGER:
{
if (spewType != SPEW_ASSERT)
DebuggerBreakIfDebugging();
break;
}
case SPEW_ABORT:
// MessageBox(nullptr, "Error in _SpewMessage", "Error", MB_OK);
exit(0);
{
#if defined(WIN32)
TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); // die, die RIGHT NOW! (don't call exit() so destructors will not get run)
#else
exit(EXIT_FAILURE); // forcefully shutdown of the process without destructors running
#endif
break;
}
default:
break;
}
@ -174,81 +179,52 @@ SpewRetval_t _SpewMessage(SpewType_t spewType, const char *pMsgFormat, va_list a
return ret;
}
SpewRetval_t _SpewMessage(const char *pMsgFormat, ...)
#if defined(_WIN32)
// Returns true if they want to break in the debugger
bool DoNewAssertDialog(const char *pFile, int line, const char *pExpression);
#endif
bool _SpewAssertDialog()
{
#if defined(_WIN32)
return DoNewAssertDialog(s_pFileName, s_Line, s_pMessage);
#else
return false;
#endif
}
SpewRetval_t _SpewAssert(const char *pFile, int line, int level, const char *pMsgFormat, ...)
{
_SpewInfo(SPEW_ASSERT, pFile, line);
va_list args;
va_start(args, pMsgFormat);
SpewRetval_t ret = _SpewMessage(s_SpewType, pMsgFormat, args);
SpewRetval_t ret = _SpewMessageV(s_SpewType, level, pMsgFormat, args);
va_end(args);
return ret;
}
SpewRetval_t _DSpewMessage(const char *pGroupName, int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive(pGroupName, level))
return SPEW_CONTINUE;
va_list args;
va_start(args, pMsgFormat);
SpewRetval_t ret = _SpewMessage(s_SpewType, pMsgFormat, args);
va_end(args);
return ret;
}
void Msg(const char *pMsgFormat, ...)
void _Msg(int level, const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_MESSAGE, pMsgFormat, args);
_SpewMessageV(SPEW_MESSAGE, level, pMsgFormat, args);
va_end(args);
}
void DMsg(const char *pGroupName, int level, const char *pMsgFormat, ...)
void _Warning(int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive(pGroupName, level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_MESSAGE, pMsgFormat, args);
_SpewMessageV(SPEW_WARNING, level, pMsgFormat, args);
va_end(args);
}
void Warning(const char *pMsgFormat, ...)
void _Log(int level, const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_WARNING, pMsgFormat, args);
va_end(args);
}
void DWarning(const char *pGroupName, int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive(pGroupName, level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_WARNING, pMsgFormat, args);
va_end(args);
}
void Log(const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_LOG, pMsgFormat, args);
va_end(args);
}
void DLog(const char *pGroupName, int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive(pGroupName, level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_LOG, pMsgFormat, args);
_SpewMessageV(SPEW_LOG, level, pMsgFormat, args);
va_end(args);
}
@ -256,146 +232,6 @@ void Error(const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_ERROR, pMsgFormat, args);
_SpewMessageV(SPEW_ERROR, DBG_DEFAULT_LEVEL, pMsgFormat, args);
va_end(args);
}
// A couple of super-common dynamic spew messages, here for convenience
// These looked at the "developer" group, print if it's level 1 or higher
void DevMsg(int level, char const *pMsgFormat, ...)
{
if (!IsSpewActive("developer", level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_MESSAGE, pMsgFormat, args);
va_end(args);
}
void DevWarning(int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive("developer", level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_WARNING, pMsgFormat, args);
va_end(args);
}
void DevLog(int level, const char *pMsgFormat, ...)
{
if (!IsSpewActive("developer", level))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_LOG, pMsgFormat, args);
va_end(args);
}
void DevMsg(const char *pMsgFormat, ...)
{
if (!IsSpewActive("developer", 1))
return;
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_MESSAGE, pMsgFormat, args);
va_end(args);
}
void DevWarning(const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_WARNING, pMsgFormat, args);
va_end(args);
}
void DevLog(const char *pMsgFormat, ...)
{
va_list args;
va_start(args, pMsgFormat);
_SpewMessage(SPEW_LOG, pMsgFormat, args);
va_end(args);
}
// Find a group, return true if found, false if not. Return in ind the
// index of the found group, or the index of the group right before where the
// group should be inserted into the list to maintain sorted order.
bool FindSpewGroup(const char *pGroupName, int *pInd)
{
int s = 0;
if (s_GroupCount)
{
int e = (int)(s_GroupCount - 1);
while (s <= e)
{
int m = (s + e) >> 1;
int cmp = Q_stricmp(pGroupName, s_pSpewGroups[m].m_GroupName);
if (!cmp)
{
*pInd = m;
return true;
}
if (cmp < 0)
e = m - 1;
else
s = m + 1;
}
}
*pInd = s;
return false;
}
// Sets the priority level for a spew group
void SpewActivate(const char *pGroupName, int level)
{
Assert(pGroupName);
// check for the default group first...
if ((pGroupName[0] == '*') && (pGroupName[1] == '\0'))
{
s_DefaultLevel = level;
return;
}
// Normal case, search in group list using binary search.
// If not found, grow the list of groups and insert it into the
// right place to maintain sorted order. Then set the level.
int ind;
if (!FindSpewGroup(pGroupName, &ind))
{
// not defined yet, insert an entry.
s_GroupCount++;
if (s_pSpewGroups)
{
s_pSpewGroups = (SpewGroup_t *)realloc(s_pSpewGroups, s_GroupCount * sizeof(SpewGroup_t));
// shift elements down to preserve order
int numToMove = s_GroupCount - ind - 1;
memmove(&s_pSpewGroups[ind + 1], &s_pSpewGroups[ind], numToMove * sizeof(SpewGroup_t));
}
else
s_pSpewGroups = (SpewGroup_t *)malloc(s_GroupCount * sizeof(SpewGroup_t));
Assert(strlen(pGroupName) < MAX_GROUP_NAME_LENGTH);
strcpy(s_pSpewGroups[ind].m_GroupName, pGroupName);
}
s_pSpewGroups[ind].m_Level = level;
}
// Tests to see if a particular spew is active
bool IsSpewActive(const char *pGroupName, int level)
{
// If we don't find the spew group, use the default level.
int ind;
if (FindSpewGroup(pGroupName, &ind))
return s_pSpewGroups[ind].m_Level >= level;
else
return s_DefaultLevel >= level;
}

View File

@ -1,30 +1,10 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#pragma once
@ -34,16 +14,7 @@
#include <stdio.h>
#include <stdarg.h>
// dll export stuff
#ifdef TIER0_DLL_EXPORT
#define DBG_INTERFACE DLL_EXPORT
#define DBG_OVERLOAD DLL_GLOBAL_EXPORT
#define DBG_CLASS DLL_CLASS_EXPORT
#else
#define DBG_INTERFACE DLL_IMPORT
#define DBG_OVERLOAD DLL_GLOBAL_IMPORT
#define DBG_CLASS DLL_CLASS_IMPORT
#endif
#define DBG_DEFAULT_LEVEL 0
// Usage model for the Dbg library
//
@ -87,22 +58,6 @@
// Msg("Isn't this exciting %d?", 5);
// Error("I'm just thrilled");
//
// Dynamic Spew messages
//
// It is possible to dynamically turn spew on and off. Dynamic spew is
// identified by a spew group and priority level. To turn spew on for a
// particular spew group, use SpewActivate("group", level). This will
// cause all spew in that particular group with priority levels <= the
// level specified in the SpewActivate function to be printed. Use DSpew
// to perform the spew:
//
// DWarning("group", level, "Oh I feel even yummier!\n");
//
// Priority level 0 means that the spew will *always* be printed, and group
// '*' is the default spew group. If a DWarning is encountered using a group
// whose priority has not been set, it will use the priority of the default
// group. The priority of the default group is initially set to 0.
//
// Spew output
//
// The output of the spew system can be redirected to an externally-supplied
@ -131,12 +86,6 @@
// }
// );
//
// Code can be activated based on the dynamic spew groups also. Use
//
// DBG_DCODE("group", level,
// { int x = 5; ++x; }
// );
//
// 3. Breaking into the debugger.
//
// To cause an unconditional break into the debugger in debug builds only, use DBG_BREAK
@ -170,7 +119,7 @@ enum SpewRetval_t
};
// Type of externally defined function used to display debug spew
typedef SpewRetval_t (*SpewOutputFunc_t)(SpewType_t spewType, const char *pMsg);
typedef SpewRetval_t (*SpewOutputFunc_t)(SpewType_t spewType, int level, const char *pMsg);
// Used to redirect spew output
void SpewOutputFunc(SpewOutputFunc_t func);
@ -178,109 +127,191 @@ void SpewOutputFunc(SpewOutputFunc_t func);
// Used ot get the current spew output function
SpewOutputFunc_t GetSpewOutputFunc();
// Used to manage spew groups and subgroups
void SpewActivate(const char *pGroupName, int level);
bool IsSpewActive(const char *pGroupName, int level);
// Used to display messages, should never be called directly.
void _SpewInfo(SpewType_t type, const char *pFile, int line);
SpewRetval_t _SpewMessage(const char *pMsg, ...);
SpewRetval_t _DSpewMessage(const char *pGroupName, int level, const char *pMsg, ...);
SpewRetval_t _SpewAssert(const char *pFile, int line, int level, const char *pMsg, ...);
void _ExitOnFatalAssert();
bool _SpewAssertDialog();
// Used to define macros, never use these directly.
#define _Assert(_exp) \
inline bool ShouldUseNewAssertDialog()
{
#if defined(DBGFLAG_ASSERTDLG)
return true; // always show an assert dialog
#else
return Plat_IsInDebugSession(); // only show an assert dialog if the process is being debugged
#endif // DBGFLAG_ASSERTDLG
}
#define _AssertMsg(_exp, _msg, _executeExp, _bFatal) \
do { \
if (!(_exp)) \
{ \
_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
if (_SpewMessage("Assertion Failed: " #_exp) == SPEW_DEBUGGER) \
SpewRetval_t ret = _SpewAssert(__FILE__, __LINE__, DBG_DEFAULT_LEVEL, _msg); \
if (ret == SPEW_DEBUGGER) \
{ \
DebuggerBreak(); \
if (!ShouldUseNewAssertDialog() || _SpewAssertDialog()) \
DebuggerBreakIfDebugging(); \
} \
_executeExp; \
if (_bFatal) \
_ExitOnFatalAssert(); \
} \
} while (0)
#define _AssertMsg(_exp, _msg) \
do { \
#define _AssertMsgWarn(_exp, _msg) \
if (!(_exp)) \
{ \
_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
if (_SpewMessage(_msg) == SPEW_DEBUGGER) \
{ \
DebuggerBreak(); \
Warning("%s (%d) : " _msg, __FILE__, __LINE__); \
} \
} \
} while (0)
#define _AssertFunc(_exp, _f) \
#define _AssertMsgOnce(_exp, _msg, _bFatal) \
do { \
if (!(_exp)) \
static bool fAsserted = false; \
if (!fAsserted && !(_exp)) \
{ \
_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
SpewRetval_t ret = _SpewMessage("Assertion Failed!" #_exp); \
_f; \
if (ret == SPEW_DEBUGGER) \
{ \
DebuggerBreak(); \
} \
} \
} while (0)
#define _AssertEquals(_exp, _expectedValue) \
do { \
if ((_exp) != (_expectedValue)) \
{ \
_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
SpewRetval_t ret = _SpewMessage("Expected %d but got %d!", (_expectedValue), (_exp)); \
if (ret == SPEW_DEBUGGER) \
{ \
DebuggerBreak(); \
} \
} \
} while (0)
#define _AssertFloatEquals(_exp, _expectedValue, _tol) \
do { \
if (fabs((_exp) - (_expectedValue)) > (_tol)) \
{ \
_SpewInfo(SPEW_ASSERT, __FILE__, __LINE__); \
SpewRetval_t ret = _SpewMessage("Expected %f but got %f!", (_expectedValue), (_exp)); \
if (ret == SPEW_DEBUGGER) \
{ \
DebuggerBreak(); \
} \
fAsserted = true; \
_AssertMsg(_exp, _msg, (fAsserted = true), _bFatal); \
} \
} while (0)
// Spew macros...
#ifdef _DEBUG
// AssertFatal macros
// AssertFatal is used to detect an unrecoverable error condition.
// If enabled, it may display an assert dialog (if DBGFLAG_ASSERTDLG is turned on or running under the debugger),
// and always terminates the application
#define Assert(_exp) _Assert(_exp)
#define AssertMsg(_exp, _msg) _AssertMsg(_exp, _msg)
#define AssertFunc(_exp, _f) _AssertFunc(_exp, _f)
#define AssertEquals(_exp, _expectedValue) _AssertEquals(_exp, _expectedValue)
#define AssertFloatEquals(_exp, _expectedValue, _tol) _AssertFloatEquals(_exp, _expectedValue, _tol)
#define Verify(_exp) _Assert(_exp)
#if defined(DBGFLAG_ASSERTFATAL)
#define AssertMsg1(_exp, _msg, a1) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1))
#define AssertMsg2(_exp, _msg, a1, a2) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2))
#define AssertMsg3(_exp, _msg, a1, a2, a3) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3))
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4))
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5))
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6))
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6))
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7))
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8))
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) _AssertMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8, a9))
#define AssertWarn(_exp) _AssertMsgWarn(_exp, "Assertion Failed: " #_exp)
#define AssertFatal(_exp) _AssertMsg(_exp, "Assertion Failed: " #_exp, ((void)0), true)
#define AssertFatalOnce(_exp) _AssertMsgOnce(_exp, "Assertion Failed: " #_exp, true)
#define AssertFatalMsg(_exp, _msg) _AssertMsg(_exp, _msg, ((void)0), true)
#define AssertFatalMsgOnce(_exp, _msg) _AssertMsgOnce(_exp, _msg, true)
#define AssertFatalFunc(_exp, _f) _AssertMsg(_exp, "Assertion Failed: " #_exp, _f, true)
#define AssertFatalEquals(_exp, _expectedValue) AssertFatalMsg2((_exp) == (_expectedValue), "Expected %d but got %d!", (_expectedValue), (_exp))
#define AssertFatalFloatEquals(_exp, _expectedValue, _tol) AssertFatalMsg2(fabs((_exp) - (_expectedValue)) <= (_tol), "Expected %f but got %f!", (_expectedValue), (_exp))
#define VerifyFatal(_exp) AssertFatal(_exp)
#define VerifyEqualsFatal(_exp, _expectedValue) AssertFatalEquals(_exp, _expectedValue)
#else // _DEBUG
#if defined(_DEBUG)
#define DbgVerifyFatal(_exp) AssertFatal(_exp)
#else
#define DbgVerifyFatal(_exp) ((void)0)
#endif
#define AssertFatalMsg1(_exp, _msg, a1) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1).ToString())
#define AssertFatalMsg2(_exp, _msg, a1, a2) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2).ToString())
#define AssertFatalMsg3(_exp, _msg, a1, a2, a3) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3).ToString())
#define AssertFatalMsg4(_exp, _msg, a1, a2, a3, a4) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4).ToString())
#define AssertFatalMsg5(_exp, _msg, a1, a2, a3, a4, a5) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5).ToString())
#define AssertFatalMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6).ToString())
#define AssertFatalMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7).ToString())
#define AssertFatalMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8).ToString())
#define AssertFatalMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) AssertFatalMsg(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8, a9).ToString())
#else // DBGFLAG_ASSERTFATAL
#define AssertWarn(_exp) ((void)0)
#define AssertFatal(_exp) ((void)0)
#define AssertFatalOnce(_exp) ((void)0)
#define AssertFatalMsg(_exp, _msg) ((void)0)
#define AssertFatalMsgOnce(_exp, _msg) ((void)0)
#define AssertFatalFunc(_exp, _f) ((void)0)
#define AssertFatalEquals(_exp, _expectedValue) ((void)0)
#define AssertFatalFloatEquals(_exp, _expectedValue, _tol) ((void)0)
#define VerifyFatal(_exp) (_exp)
#define VerifyEqualsFatal(_exp, _expectedValue) (_exp)
#define DbgVerifyFatal(_exp) (_exp)
#define AssertFatalMsg1(_exp, _msg, a1) ((void)0)
#define AssertFatalMsg2(_exp, _msg, a1, a2) ((void)0)
#define AssertFatalMsg3(_exp, _msg, a1, a2, a3) ((void)0)
#define AssertFatalMsg4(_exp, _msg, a1, a2, a3, a4) ((void)0)
#define AssertFatalMsg5(_exp, _msg, a1, a2, a3, a4, a5) ((void)0)
#define AssertFatalMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) ((void)0)
#define AssertFatalMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) ((void)0)
#define AssertFatalMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) ((void)0)
#define AssertFatalMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) ((void)0)
#endif // DBGFLAG_ASSERTFATAL
#if defined(DBGFLAG_ASSERT)
#define Assert(_exp) _AssertMsg(_exp, "Assertion Failed: " #_exp, ((void)0), false)
#define AssertMsg_(_exp, _msg) _AssertMsg(_exp, _msg, ((void)0), false)
#define AssertOnce(_exp) _AssertMsgOnce(_exp, "Assertion Failed: " #_exp, false)
#define AssertMsgOnce(_exp, _msg) _AssertMsgOnce(_exp, _msg, false)
#define AssertFunc(_exp, _f) _AssertMsg(_exp, "Assertion Failed: " #_exp, _f, false)
#define AssertEquals(_exp, _expectedValue) AssertMsg2((_exp) == (_expectedValue), "Expected %d but got %d!", (_expectedValue), (_exp))
#define AssertFloatEquals(_exp, _expectedValue, _tol) AssertMsg2(fabs((_exp) - (_expectedValue)) <= (_tol), "Expected %f but got %f!", (_expectedValue), (_exp))
#define Verify(_exp) (_exp)
#define VerifyEquals(_exp, _expectedValue) AssertEquals(_exp, _expectedValue)
#if defined(_DEBUG)
#define DbgVerify(_exp) (_exp)
#define DbgAssert(_exp) Assert(_exp)
#define DbgAssertMsg(_exp, _msg) AssertMsg(_exp, _msg)
#define DbgAssertMsg1(_exp, _msg, a1) AssertMsg1(_exp, _msg, a1)
#define DbgAssertMsg2(_exp, _msg, a1, a2) AssertMsg2(_exp, _msg, a1, a2)
#define DbgAssertMsg3(_exp, _msg, a1, a2, a3) AssertMsg3(_exp, _msg, a1, a2, a3)
#define DbgAssertMsg4(_exp, _msg, a1, a2, a3) AssertMsg4(_exp, _msg, a1, a2, a3, a4)
#define DbgAssertMsg5(_exp, _msg, a1, a2, a3) AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5)
#define DbgAssertMsg6(_exp, _msg, a1, a2, a3) AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6)
#define DbgAssertMsg7(_exp, _msg, a1, a2, a3) AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7)
#define DbgAssertMsg8(_exp, _msg, a1, a2, a3) AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8)
#define DbgAssertMsg9(_exp, _msg, a1, a2, a3) AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9)
#else
#define DbgVerify(_exp) ((void)0)
#define DbgAssert(_exp) ((void)0)
#define DbgAssertMsg(_exp, _msg) ((void)0)
#define DbgAssertMsg1(_exp, _msg, a1) ((void)0)
#define DbgAssertMsg2(_exp, _msg, a1, a2) ((void)0)
#define DbgAssertMsg3(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg4(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg5(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg6(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg7(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg8(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg9(_exp, _msg, a1, a2, a3) ((void)0)
#endif
#define AssertMsg(_exp, _msg) AssertMsg_(_exp, _msg)
#define AssertMsg1(_exp, _msg, a1) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1).ToString())
#define AssertMsg2(_exp, _msg, a1, a2) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2).ToString())
#define AssertMsg3(_exp, _msg, a1, a2, a3) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3).ToString())
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4).ToString())
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5).ToString())
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6).ToString())
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6).ToString())
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7).ToString())
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8).ToString())
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) AssertMsg_(_exp, CDbgFmtMsg(_msg, a1, a2, a3, a4, a5, a6, a7, a8, a9).ToString())
#else // DBGFLAG_ASSERT
#define Assert(_exp) ((void)0)
#define AssertMsg(_exp, _msg) ((void)0)
#define AssertOnce(_exp) ((void)0)
#define AssertMsgOnce(_exp, _msg) ((void)0)
#define AssertFunc(_exp, _f) ((void)0)
#define AssertEquals(_exp, _expectedValue) ((void)0)
#define AssertFloatEquals(_exp, _expectedValue, _tol) ((void)0)
#define Verify(_exp) (_exp)
#define Verify(_exp) ((void)0)
#define VerifyEquals(_exp, _expectedValue) ((void)0)
#define DbgVerify(_exp) ((void)0)
#define DbgAssert(_exp) ((void)0)
#define DbgAssertMsg(_exp, _msg) ((void)0)
#define DbgAssertMsg1(_exp, _msg, a1) ((void)0)
#define DbgAssertMsg2(_exp, _msg, a1, a2) ((void)0)
#define DbgAssertMsg3(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg4(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg5(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg6(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg7(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg8(_exp, _msg, a1, a2, a3) ((void)0)
#define DbgAssertMsg9(_exp, _msg, a1, a2, a3) ((void)0)
#define AssertMsg1(_exp, _msg, a1) ((void)0)
#define AssertMsg2(_exp, _msg, a1, a2) ((void)0)
@ -293,18 +324,22 @@ SpewRetval_t _DSpewMessage(const char *pGroupName, int level, const char *pMsg,
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) ((void)0)
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) ((void)0)
#endif // _DEBUG
#endif // DBGFLAG_ASSERT
// default level versions (level 0)
#define Msg(...) _Msg (0, __VA_ARGS__)
#define Warning(...) _Warning(0, __VA_ARGS__)
#define Log(...) _Log (0, __VA_ARGS__)
// These looked at the "developer" functions
#define DevMsg(...) _Msg (1, __VA_ARGS__)
#define DevWarning(...) _Warning(1, __VA_ARGS__)
#define DevLog(...) _Log (1, __VA_ARGS__)
// These are always compiled in
void Msg(const char *pMsg, ...);
void DMsg(const char *pGroupName, int level, const char *pMsg, ...);
void Warning(const char *pMsg, ...);
void DWarning(const char *pGroupName, int level, const char *pMsg, ...);
void Log(const char *pMsg, ...);
void DLog(const char *pGroupName, int level, const char *pMsg, ...);
void _Msg (int level, const char *pMsg, ...);
void _Warning(int level, const char *pMsg, ...);
void _Log (int level, const char *pMsg, ...);
void Error (const char *pMsg, ...);
// You can use this macro like a runtime assert macro.
@ -320,57 +355,39 @@ void Error(const char *pMsg, ...);
Error msg; \
}
// A couple of super-common dynamic spew messages, here for convenience
// These looked at the "developer" group
void DevMsg(int level, char const* pMsg, ...);
void DevWarning(int level, const char *pMsg, ...);
void DevLog(int level, const char *pMsg, ...);
// default level versions (level 1)
void DevMsg(char const* pMsg, ...);
void DevWarning(const char *pMsg, ...);
void DevLog(const char *pMsg, ...);
// Code macros, debugger interface
#ifdef _DEBUG
#if defined(_DEBUG)
#define DBG_CODE(_code) if (0) ; else { _code }
#define DBG_DCODE(_g, _l, _code) if (IsSpewActive(_g, _l)) { _code } else {}
#define DBG_BREAK() DebuggerBreak()
#define DBG_BREAK() DebuggerBreakIfDebugging()
#else // _DEBUG
#define DBG_CODE(_code) ((void)0)
#define DBG_DCODE(_g, _l, _code) ((void)0)
#define DBG_BREAK() ((void)0)
#endif // _DEBUG
// Macro to assist in asserting constant invariants during compilation
#define UID_PREFIX generated_id_
#define UID_CAT1(a, c) a ## c
#define UID_CAT2(a, c) UID_CAT1(a,c)
#define UNIQUE_ID UID_CAT2(UID_PREFIX, __LINE__)
#ifdef _DEBUG
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
#define ASSERT_INVARIANT(pred) static void UNIQUE_ID() { COMPILE_TIME_ASSERT(pred) }
#else
#define COMPILE_TIME_ASSERT(pred)
#define ASSERT_INVARIANT(pred)
#endif
// Templates to assist in validating pointers:
// Have to use these stubs so we don't have to include windows.h here.
void _AssertValidReadPtr(void *ptr, int count = 1);
void _AssertValidWritePtr(void *ptr, int count = 1);
void _AssertValidReadWritePtr(void *ptr, int count = 1);
#if defined(DBGFLAG_ASSERT)
void AssertValidStringPtr(const char *ptr, int maxchar = 0xFFFFFF);
template <class T> inline void AssertValidReadPtr(T *ptr, int count = 1) { _AssertValidReadPtr((void *)ptr, count); }
template <class T> inline void AssertValidWritePtr(T *ptr, int count = 1) { _AssertValidWritePtr((void *)ptr, count); }
template <class T> inline void AssertValidReadWritePtr(T *ptr, int count = 1) { _AssertValidReadWritePtr((void *)ptr, count); }
#define AssertValidThis() AssertValidReadWritePtr(this, sizeof(*this))
#else
#define AssertValidStringPtr(...) ((void)0)
#define AssertValidReadPtr(...) ((void)0)
#define AssertValidWritePtr(...) ((void)0)
#define AssertValidReadWritePtr(...) ((void)0)
#define AssertValidThis() ((void)0)
#endif // #if defined(DBGFLAG_ASSERT)
// Macro to protect functions that are not reentrant
#ifdef _DEBUG
#if defined(_DEBUG)
class CReentryGuard
{
public:
@ -406,6 +423,7 @@ public:
va_start(arg_ptr, pszFormat);
_vsnprintf(m_szBuf, sizeof(m_szBuf) - 1, pszFormat, arg_ptr);
va_end(arg_ptr);
m_szBuf[sizeof(m_szBuf) - 1] = '\0';
}
@ -414,14 +432,11 @@ public:
return m_szBuf;
}
const char *ToString() const
{
return m_szBuf;
}
private:
char m_szBuf[256];
};
// Embed debug info in each file.
//#ifdef _WIN32
//#ifdef _DEBUG
//#pragma comment(compiler)
//#pragma comment(exestr,"*** DEBUG file detected, Last Compile: " __DATE__ ", " __TIME__ " ***")
//#endif
//#endif

View File

@ -41,7 +41,14 @@
#endif
// Used to step into the debugger
#if defined(__GNUC__) && !defined(__clang__)
#define DebuggerBreak() __asm__ __volatile__("int3;")
#else
#define DebuggerBreak() __asm { int 3 }
#endif
#define DebuggerBreakIfDebugging() if (Plat_IsInDebugSession()) { DebuggerBreak(); }
#define DebuggerSegFault() { volatile int *null = 0; *null = 0; }
// C functions for external declarations that call the appropriate C++ methods
#ifndef EXPORT
@ -105,6 +112,37 @@
#endif
#endif
//
// Macro to assist in asserting constant invariants during compilation
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
// This implementation of compile time assert has zero cost (so it can safely be
// included in release builds) and can be used at file scope or function scope.
#define COMPILE_TIME_ASSERT(pred) static_assert(pred, "Compile time assert constraint is not true: " #pred)
// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at global
// scope. However the new COMPILE_TIME_ASSERT macro supports that by default.
#define ASSERT_INVARIANT(pred) COMPILE_TIME_ASSERT(pred)
// Macro to assist in asserting constant invariants during compilation
//
// If available use static_assert instead of weird language tricks. This
// leads to much more readable messages when compile time assert constraints
// are violated.
#if !defined(OSX) && (_MSC_VER > 1500 || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define PLAT_COMPILE_TIME_ASSERT(pred) static_assert(pred, "Compile time assert constraint is not true: " #pred)
#else
#define PLAT_COMPILE_TIME_ASSERT(pred) typedef int UNIQUE_ID[ (pred) ? 1 : -1]
#endif
bool Plat_IsInDebugSession();
void Plat_DebugString(const char *psz);
void Plat_OutputDebugString(const char *psz);
void Plat_OutputDebugStringRaw(const char *psz);
const char *Plat_GetCommandLine();
// Methods to invoke the constructor, copy constructor, and destructor
template <class T>
inline void Construct(T *pMemory)

View File

@ -0,0 +1,113 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
bool Plat_IsInDebugSession()
{
#if defined(OSX)
int mib[4];
struct kinfo_proc info;
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
info.kp_proc.p_flag = 0;
sysctl(mib, 4, &info, &size, NULL, 0);
bool result = ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
return result;
#elif defined(_LINUX)
char s[256];
snprintf(s, 256, "/proc/%d/cmdline", getppid());
FILE *fp = fopen(s, "r");
if (fp)
{
fread(s, 256, 1, fp);
fclose(fp);
return (0 == strncmp(s, "gdb", 3));
}
return false;
#endif
}
void Plat_OutputDebugStringRaw(const char *psz)
{
fprintf(stderr, "%s", psz);
}
void Plat_OutputDebugString(const char *psz)
{
fprintf(stderr, "%s", psz);
}
void Plat_DebugString(const char *psz)
{
fprintf(stderr, "%s", psz);
}
static char g_CmdLine[2048]{};
const char *Plat_GetCommandLine()
{
#if defined(_LINUX)
static bool commandline_initialized = false;
if (!commandline_initialized)
{
commandline_initialized = true;
FILE *fp = fopen("/proc/self/cmdline", "rb");
if (fp)
{
size_t nCharRead = 0;
// -1 to leave room for the '\0'
nCharRead = fread(g_CmdLine, sizeof(g_CmdLine[0]), ARRAYSIZE(g_CmdLine) - 1, fp);
if (feof(fp) && !ferror(fp)) // Should have read the whole command line without error
{
Assert(nCharRead < ARRAYSIZE(g_CmdLine));
for (int i = 0; i < nCharRead; i++)
{
if (!g_CmdLine[i])
g_CmdLine[i] = ' ';
}
g_CmdLine[nCharRead] = '\0';
}
fclose(fp);
}
Assert(g_CmdLine[0]);
}
#endif // LINUX
return g_CmdLine;
}

View File

@ -0,0 +1,57 @@
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
bool Plat_IsInDebugSession()
{
return IsDebuggerPresent() != FALSE;
}
void Plat_OutputDebugStringRaw(const char *psz)
{
OutputDebugString(psz);
}
void Plat_OutputDebugString(const char *psz)
{
static char buf[4096];
int len = Q_snprintf(buf, sizeof(buf), "%s", psz);
Assert(len > 0);
OutputDebugString(buf);
}
void Plat_DebugString(const char *psz)
{
Plat_OutputDebugString(psz);
}
const char *Plat_GetCommandLine()
{
return GetCommandLineA();
}

View File

@ -0,0 +1,44 @@
/*
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
*/
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by assert_dialog.rc
//
#define IDD_ASSERT_DIALOG 101
#define IDC_FILENAME_CONTROL 1000
#define IDC_LINE_CONTROL 1001
#define IDC_IGNORE_FILE 1002
#define IDC_IGNORE_NEARBY 1003
#define IDC_IGNORE_NUMLINES 1004
#define IDC_IGNORE_THIS 1005
#define IDC_BREAK 1006
#define IDC_IGNORE_ALL 1008
#define IDC_IGNORE_ALWAYS 1009
#define IDC_IGNORE_NUMTIMES 1010
#define IDC_ASSERT_MSG_CTRL 1011
#define IDC_NOID -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1005
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif