Skip to content

Commit

Permalink
CvAssertDlg implementation (#11386)
Browse files Browse the repository at this point in the history
* Add CvAssertDlg

* move to CvGameCoreUtils

move to CvGameCoreUtils

* DISABLE_CVASSERT
  • Loading branch information
JohnsterID authored Dec 6, 2024
1 parent 8277aa7 commit aa008ad
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 4 deletions.
179 changes: 179 additions & 0 deletions CvGameCoreDLL_Expansion2/CvGameCoreUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,191 @@
#include "CvImprovementClasses.h"
#include "CvGlobals.h"
#include "psapi.h"
#include <time.h> // CvAssertDlg

#include "ICvDLLUserInterface.h"

// must be included after all other headers
#include "LintFree.h"

// CvAssertDlg implementation
#if defined(VPDEBUG) && !defined(DISABLE_CVASSERT)
#ifdef WIN32

// MessageBox constants
#define MB_OK 0x00000000L
#define MB_OKCANCEL 0x00000001L
#define MB_ABORTRETRYIGNORE 0x00000002L
#define MB_YESNOCANCEL 0x00000003L
#define MB_ICONERROR 0x00000010L
#define MB_TASKMODAL 0x00002000L

// MessageBox return values
#define IDOK 1
#define IDCANCEL 2
#define IDABORT 3
#define IDRETRY 4
#define IDIGNORE 5
#define IDYES 6
#define IDNO 7

// Windows functions
extern "C" {
__declspec(dllimport) int __stdcall MessageBoxA(void* hWnd, const char* lpText, const char* lpCaption, unsigned int uType);
__declspec(dllimport) unsigned long __stdcall GetTickCount(void);
}

// Assert tracking
struct AssertInfo {
unsigned int count;
unsigned int firstTime;
unsigned int lastTime;
unsigned int totalMemUsage;
bool isPermanentlyIgnored;

AssertInfo() : count(0), firstTime(0), lastTime(0), totalMemUsage(0), isPermanentlyIgnored(false) {}
};

static struct AssertTracker {
std::map<std::string, AssertInfo> asserts;
unsigned int sessionStart;
unsigned int totalAsserts;
FILE* logFile;

AssertTracker() {
sessionStart = GetTickCount();
totalAsserts = 0;
logFile = NULL;
fopen_s(&logFile, "CvAssert.log", "a");
}

~AssertTracker() {
if (logFile) {
fclose(logFile);
}
}

void LogAssert(const char* expr, const char* file, unsigned int line, const char* msg) {
if (!logFile) return;

time_t rawtime;
time(&rawtime);
char timeBuffer[26];
ctime_s(timeBuffer, sizeof(timeBuffer), &rawtime);
timeBuffer[24] = '\0'; // Remove newline

fprintf(logFile, "\n[%s] Assert in %s:%u\n", timeBuffer, file, line);
fprintf(logFile, "Expression: %s\n", expr);
if (msg && *msg) {
fprintf(logFile, "Message: %s\n", msg);
}
fflush(logFile);
}

std::string GetAssertKey(const char* file, unsigned int line, const char* expr) {
char key[512];
_snprintf_s(key, _countof(key), _TRUNCATE, "%s:%u:%s", file, line, expr);
return std::string(key);
}

void UpdateStats(const std::string& key) {
AssertInfo& info = asserts[key];
unsigned int currentTime = GetTickCount();

if (info.count == 0) {
info.firstTime = currentTime;
}

info.count++;
info.lastTime = currentTime;
totalAsserts++;
}

} g_AssertTracker;

bool CvAssertDlg(const char* expr, const char* szFile, unsigned int uiLine, bool& bIgnoreAlways, const char* msg)
{
if (!expr) return false;

// Get unique key for this assert
std::string assertKey = g_AssertTracker.GetAssertKey(szFile, uiLine, expr);

// Check if permanently ignored
if (g_AssertTracker.asserts[assertKey].isPermanentlyIgnored) {
return false;
}

// Check if ignored for this session
if (bIgnoreAlways) {
return false;
}

// Log the assert
g_AssertTracker.LogAssert(expr, szFile, uiLine, msg);

// Update statistics
g_AssertTracker.UpdateStats(assertKey);

// Get assert info
const AssertInfo& info = g_AssertTracker.asserts[assertKey];

// Calculate timing information
unsigned int timeSinceFirst = GetTickCount() - info.firstTime;
unsigned int timeSinceLast = GetTickCount() - info.lastTime;
unsigned int sessionTime = GetTickCount() - g_AssertTracker.sessionStart;

// Format the message
char szBuffer[4096];
_snprintf_s(szBuffer, _countof(szBuffer), _TRUNCATE,
"Assert Failed!\n"
"==================\n"
"Expression: %s\n"
"File: %s\n"
"Line: %u\n"
"%s%s%s\n"
"\nStatistics:\n"
"==================\n"
"This assert has fired %u time(s)\n"
"First occurrence: %.2f seconds ago\n"
"Last occurrence: %.2f seconds ago\n"
"Total asserts this session: %u\n"
"Session duration: %.2f seconds\n"
"\nOptions:\n"
"==================\n"
"Yes - Break into debugger\n"
"No - Continue execution\n"
"Cancel - Ignore this assert",
expr, szFile, uiLine,
msg ? "\nMessage: " : "", msg ? msg : "", msg ? "\n" : "",
info.count,
timeSinceFirst / 1000.0f,
timeSinceLast / 1000.0f,
g_AssertTracker.totalAsserts,
sessionTime / 1000.0f);

// Show dialog
int nResult = MessageBoxA(NULL, szBuffer, "Assertion Failed",
MB_YESNOCANCEL | MB_ICONERROR | MB_TASKMODAL);

// Handle result
switch (nResult)
{
case IDYES: // Break into debugger
return true;

case IDNO: // Continue execution
return false;

default: // Ignore always
g_AssertTracker.asserts[assertKey].isPermanentlyIgnored = true;
bIgnoreAlways = true;
return false;
}
}

#endif // WIN32
#endif // defined(VPDEBUG) && !defined(DISABLE_CVASSERT)

int RING_PLOTS[6] = {RING0_PLOTS,RING1_PLOTS,RING2_PLOTS,RING3_PLOTS,RING4_PLOTS,RING5_PLOTS};

int dxWrap(int iDX)
Expand Down
4 changes: 2 additions & 2 deletions CvGameCoreDLL_Expansion2/VoxPopuli.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>FXS_IS_DLL;WIN32;_WINDOWS;_USRDLL;EXTERNAL_PAUSING;STACKWALKER;CVGAMECOREDLL_EXPORTS;FINAL_RELEASE;_CRT_SECURE_NO_WARNINGS;VPDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>FXS_IS_DLL;WIN32;_WINDOWS;_USRDLL;EXTERNAL_PAUSING;STACKWALKER;CVGAMECOREDLL_EXPORTS;FINAL_RELEASE;_CRT_SECURE_NO_WARNINGS;VPDEBUG;DISABLE_CVASSERT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
Expand All @@ -97,7 +97,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>FXS_IS_DLL;WIN32;_WINDOWS;_USRDLL;EXTERNAL_PAUSING;STACKWALKER;CVGAMECOREDLL_EXPORTS;FINAL_RELEASE;_CRT_SECURE_NO_WARNINGS;STRONG_ASSUMPTIONS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>FXS_IS_DLL;WIN32;_WINDOWS;_USRDLL;EXTERNAL_PAUSING;STACKWALKER;CVGAMECOREDLL_EXPORTS;FINAL_RELEASE;_CRT_SECURE_NO_WARNINGS;STRONG_ASSUMPTIONS;NDEBUG;DISABLE_CVASSERT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
Expand Down
3 changes: 2 additions & 1 deletion build_vp_clang.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Config(Enum):
'FINAL_RELEASE',
'_CRT_SECURE_NO_WARNINGS',
'_WINDLL',
'DISABLE_CVASSERT',
]
RELEASE_PREDEFS = SHARED_PREDEFS + ['STRONG_ASSUMPTIONS', 'NDEBUG']
DEBUG_PREDEFS = SHARED_PREDEFS + ['VPDEBUG']
Expand Down Expand Up @@ -319,7 +320,7 @@ def build_cl_config_args(config: Config) -> list[str]:
return args

def build_link_config_args(config: Config) -> list[str]:
args = ['/MACHINE:x86', '/DLL', '/DEBUG', '/LTCG', '/DYNAMICBASE', '/NXCOMPAT', '/SUBSYSTEM:WINDOWS', '/MANIFEST:EMBED', f'/DEF:"{os.path.join(PROJECT_DIR, DEF_FILE)}"']
args = ['/MACHINE:x86', '/DLL', '/DEBUG', '/LTCG', '/DYNAMICBASE', '/NXCOMPAT', '/SUBSYSTEM:WINDOWS', '/MANIFEST:EMBED', '/FORCE:MULTIPLE', f'/DEF:"{os.path.join(PROJECT_DIR, DEF_FILE)}"']
if config == Config.Release:
args += ['/OPT:REF', '/OPT:ICF']
return args
Expand Down
3 changes: 2 additions & 1 deletion build_vp_clang_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Config(Enum):
'FINAL_RELEASE',
'_CRT_SECURE_NO_WARNINGS',
'_WINDLL',
'DISABLE_CVASSERT',
]
RELEASE_PREDEFS = SHARED_PREDEFS + ['STRONG_ASSUMPTIONS', 'NDEBUG']
DEBUG_PREDEFS = SHARED_PREDEFS + ['VPDEBUG']
Expand Down Expand Up @@ -342,7 +343,7 @@ def build_cl_config_args(config: Config) -> list[str]:
return args

def build_link_config_args(config: Config) -> list[str]:
args = ['/MACHINE:x86', '/DLL', '/DEBUG', '/LTCG', '/DYNAMICBASE', '/NXCOMPAT', '/SUBSYSTEM:WINDOWS', '/MANIFEST:EMBED', f'/DEF:"{os.path.join(PROJECT_DIR, DEF_FILE)}"']
args = ['/MACHINE:x86', '/DLL', '/DEBUG', '/LTCG', '/DYNAMICBASE', '/NXCOMPAT', '/SUBSYSTEM:WINDOWS', '/MANIFEST:EMBED', '/FORCE:MULTIPLE', f'/DEF:"{os.path.join(PROJECT_DIR, DEF_FILE)}"']
if config == Config.Release:
args += ['/OPT:REF', '/OPT:ICF']
return args
Expand Down

0 comments on commit aa008ad

Please sign in to comment.