-
Notifications
You must be signed in to change notification settings - Fork 145
bugfix(system): Update crash message #2069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
85a17f6
33b4aad
81bc3fd
a316c10
5a2ee72
5460dd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -368,6 +368,15 @@ void DebugInit(int flags) | |||
|
|
||||
| theMainThreadID = GetCurrentThreadId(); | ||||
|
|
||||
| #if defined(DEBUG_STACKTRACE) || defined(IG_DEBUG_STACKTRACE) | ||||
| // TheSuperHackers @bugfix JohnsterID 12/01/2026 Initialize g_LastErrorDump at startup | ||||
| // This ensures isEmpty() works reliably across all build configs. Without explicit | ||||
| // initialization, different STL implementations (VC6/STLPort vs Win32) may leave | ||||
| // garbage/uninitialized data in the global AsciiString, causing extractCrashLocation | ||||
| // to extract invalid data when ReleaseCrash is called without exception context. | ||||
| g_LastErrorDump.clear(); | ||||
| #endif | ||||
|
|
||||
| #ifdef DEBUG_LOGGING | ||||
|
|
||||
| // TheSuperHackers @info Debug initialization can happen very early. | ||||
|
|
@@ -746,6 +755,53 @@ static void TriggerMiniDump() | |||
| } | ||||
|
|
||||
|
|
||||
| // TheSuperHackers @bugfix JohnsterID 06/01/2025 Helper function to extract crash location from stack trace | ||||
| static void extractCrashLocation(char* outBuffer, size_t bufferSize) | ||||
| { | ||||
| // TheSuperHackers @bugfix JohnsterID 12/01/2026 g_LastErrorDump is explicitly initialized | ||||
| // in DebugInit(), so isEmpty() now works reliably across all build configs. | ||||
| if (bufferSize == 0 || g_LastErrorDump.isEmpty()) { | ||||
| return; | ||||
| } | ||||
|
|
||||
| outBuffer[0] = '\0'; | ||||
|
|
||||
| const char* stackStr = g_LastErrorDump.str(); | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you show a sample for what
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The function extracts only the first line (most relevant crash location) to display in the crash dialog though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Can we simplify this function to its most basic requirement of just grabbing first line of multi-line text? Right now, this functions looks very complicated. Also, if first line is Maybe even print the first 5 lines or so. |
||||
|
|
||||
| // Defensive check for null or empty string | ||||
| if (!stackStr || !*stackStr) { | ||||
| return; | ||||
| } | ||||
|
|
||||
| // Skip leading whitespace/newlines | ||||
| while (*stackStr && isspace(static_cast<unsigned char>(*stackStr))) { | ||||
| stackStr++; | ||||
| } | ||||
|
|
||||
| // Skip if no content or first line starts with "<Unknown>" | ||||
| // Only check the first line (crash location) rather than entire stack trace, | ||||
| // so we can still show useful info even if deeper frames lack symbols. | ||||
| if (!*stackStr || strncmp(stackStr, "<Unknown>", 9) == 0) { | ||||
| return; | ||||
| } | ||||
|
|
||||
| // Find end of first line | ||||
| const char* lineEnd = stackStr; | ||||
| // Limit to 400 chars to fit in 512 byte buffer with safety margin | ||||
| const size_t maxLineLength = bufferSize < 401 ? bufferSize - 1 : 400; | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this 400 char limit come to be?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added code comment (512 byte buffer - 400 chars = 111 byte safety margin) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not understand the rationale behind this clamp. We pass a 512 buffer but only get max 400. Why? If our buffer is 512, why can we not have max 512 (-1)? |
||||
|
|
||||
| while (*lineEnd && *lineEnd != '\n' && *lineEnd != '\r' && (size_t)(lineEnd - stackStr) < maxLineLength) { | ||||
| lineEnd++; | ||||
| } | ||||
|
|
||||
| size_t len = lineEnd - stackStr; | ||||
| if (len > 0) { | ||||
| strncpy(outBuffer, stackStr, len); | ||||
| outBuffer[len] = '\0'; | ||||
| } | ||||
| } | ||||
|
|
||||
|
|
||||
| void ReleaseCrash(const char *reason) | ||||
| { | ||||
| /// do additional reporting on the crash, if possible | ||||
|
|
@@ -806,23 +862,50 @@ void ReleaseCrash(const char *reason) | |||
| } | ||||
| } | ||||
|
|
||||
| // TheSuperHackers @bugfix JohnsterID 06/01/2025 Update crash message to show crash report locations | ||||
| // and point users to repo. Removes outdated EA forum references. | ||||
| // Also shows crash location from stack trace when debug symbols are available. | ||||
| char crashInfoPath[_MAX_PATH]; | ||||
| strlcpy(crashInfoPath, TheGlobalData->getPath_UserData().str(), ARRAY_SIZE(crashInfoPath)); | ||||
| strlcat(crashInfoPath, RELEASECRASH_FILE_NAME, ARRAY_SIZE(crashInfoPath)); | ||||
|
|
||||
| #ifdef RTS_ENABLE_CRASHDUMP | ||||
| char crashDumpDir[_MAX_PATH]; | ||||
| strlcpy(crashDumpDir, TheGlobalData->getPath_UserData().str(), ARRAY_SIZE(crashDumpDir)); | ||||
| strlcat(crashDumpDir, "CrashDumps\\", ARRAY_SIZE(crashDumpDir)); | ||||
| #endif | ||||
|
|
||||
| char crashLocation[512]; | ||||
| extractCrashLocation(crashLocation, ARRAY_SIZE(crashLocation)); | ||||
|
|
||||
| char buff[2560]; | ||||
| char* p = buff; | ||||
| char* end = buff + sizeof(buff); | ||||
|
|
||||
| p += snprintf(p, end - p, "The game encountered a critical error and needs to close.\n\n"); | ||||
|
|
||||
| #if defined(RTS_DEBUG) | ||||
| /* static */ char buff[8192]; // not so static so we can be threadsafe | ||||
| snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason); | ||||
| ::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR); | ||||
| #else | ||||
| // crash error messaged changed 3/6/03 BGC | ||||
| // ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR); | ||||
| // ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR); | ||||
| if (reason && *reason) { | ||||
| p += snprintf(p, end - p, "Error: %s\n", reason); | ||||
| } | ||||
| #endif | ||||
|
|
||||
| // crash error message changed again 8/22/03 M Lorenzen... made this message box modal to the system so it will appear on top of any task-modal windows, splash-screen, etc. | ||||
| ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", | ||||
| "Technical Difficulties...", | ||||
| MB_OK|MB_SYSTEMMODAL|MB_ICONERROR); | ||||
| if (crashLocation[0] != '\0') { | ||||
| p += snprintf(p, end - p, "Location:\n%s\n\n", crashLocation); | ||||
| } else if (p > buff && *(p - 1) != '\n') { | ||||
| p += snprintf(p, end - p, "\n"); | ||||
| } | ||||
|
|
||||
| p += snprintf(p, end - p, "Crash report saved to:\n%s\n", crashInfoPath); | ||||
|
|
||||
| #ifdef RTS_ENABLE_CRASHDUMP | ||||
| p += snprintf(p, end - p, "\nMinidump files saved to:\n%s\n", crashDumpDir); | ||||
| #endif | ||||
|
|
||||
| snprintf(p, end - p, "\nPlease report the issue:\nhttps://github.com/TheSuperHackers/GeneralsCrashReports/issues"); | ||||
|
|
||||
| ::MessageBox(NULL, buff, "Game Crash", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR); | ||||
|
|
||||
| _exit(1); | ||||
| } | ||||
|
|
||||
|
|
@@ -848,9 +931,31 @@ void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m) | |||
| } | ||||
| } | ||||
|
|
||||
| // TheSuperHackers @bugfix JohnsterID 06/01/2025 Append crash file locations to localized error message | ||||
| char crashInfoPath[_MAX_PATH]; | ||||
xezon marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
| strlcpy(crashInfoPath, TheGlobalData->getPath_UserData().str(), ARRAY_SIZE(crashInfoPath)); | ||||
| strlcat(crashInfoPath, RELEASECRASH_FILE_NAME, ARRAY_SIZE(crashInfoPath)); | ||||
|
|
||||
| char crashInfoAppendix[1024]; | ||||
| snprintf(crashInfoAppendix, sizeof(crashInfoAppendix), | ||||
| "\n\nCrash report: %s" | ||||
| #ifdef RTS_ENABLE_CRASHDUMP | ||||
| "\nMinidump files: %sCrashDumps\\" | ||||
| #endif | ||||
| "\n\nReport issue: https://github.com/TheSuperHackers/GeneralsCrashReports/issues", | ||||
| crashInfoPath | ||||
| #ifdef RTS_ENABLE_CRASHDUMP | ||||
| , TheGlobalData->getPath_UserData().str() | ||||
| #endif | ||||
| ); | ||||
|
|
||||
| if (TheSystemIsUnicode) | ||||
| { | ||||
| ::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR); | ||||
| UnicodeString appendix; | ||||
| appendix.translate(crashInfoAppendix); | ||||
| UnicodeString fullMessage = mesg; | ||||
| fullMessage.concat(appendix); | ||||
| ::MessageBoxW(NULL, fullMessage.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR); | ||||
| } | ||||
| else | ||||
| { | ||||
|
|
@@ -859,6 +964,7 @@ void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m) | |||
| AsciiString promptA, mesgA; | ||||
| promptA.translate(prompt); | ||||
| mesgA.translate(mesg); | ||||
| mesgA.concat(crashInfoAppendix); | ||||
| //Make sure main window is not TOP_MOST | ||||
| ::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE); | ||||
| ::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR); | ||||
|
|
||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps clarify that this is relevant because crash can occur before static initialization occurs? I assume that is the reason that triggers the issue?