Win32: offer to restart application on fatal errors.
This changes the assertion failure behavior to be the same in debug and release builds: to show the complete failure message, and to offer to restart the application or defer to Windows Error Reporting to generate a backtrace. Contrary to popular belief, WER is not useless, and since SolveSpace publishes pdb files, WER-generated reports can be symbolized. This commit also addresses the long-standing problem where showing a dialog on fatal error would re-enter the application code, thus causing another error or a crash that is more fatal than the current one.pull/307/merge
parent
8192c965ea
commit
975b49f520
|
@ -9,6 +9,13 @@
|
|||
|
||||
namespace Platform {
|
||||
|
||||
// Handling fatal errors.
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void FatalError(std::string message);
|
||||
extern bool handlingFatalError;
|
||||
|
||||
// UTF-8 ⟷ UTF-16 conversion, for Windows.
|
||||
#if defined(WIN32)
|
||||
std::string Narrow(const wchar_t *s);
|
||||
|
|
|
@ -27,10 +27,10 @@ void dbp(const char *str, ...)
|
|||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void assert_failure(const char *file, unsigned line, const char *function,
|
||||
const char *condition, const char *message) {
|
||||
fprintf(stderr, "File %s, line %u, function %s:\n", file, line, function);
|
||||
fprintf(stderr, "Assertion '%s' failed: ((%s) == false).\n", message, condition);
|
||||
namespace Platform {
|
||||
|
||||
void FatalError(std::string message) {
|
||||
fprintf(stderr, "%s", message.c_str());
|
||||
|
||||
#if !defined(LIBRARY) && defined(HAVE_BACKTRACE)
|
||||
static void *ptrs[1024] = {};
|
||||
|
@ -54,6 +54,8 @@ void assert_failure(const char *file, unsigned line, const char *function,
|
|||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
||||
// since fragmentation is less of a concern, and it also makes it possible
|
||||
|
|
|
@ -559,6 +559,8 @@ static void MouseWheel(int thisDelta) {
|
|||
|
||||
LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if(Platform::handlingFatalError) return 1;
|
||||
|
||||
switch (msg) {
|
||||
case WM_ERASEBKGND:
|
||||
break;
|
||||
|
@ -955,6 +957,8 @@ bool SolveSpace::GraphicsEditControlIsVisible()
|
|||
LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
if(Platform::handlingFatalError) return 1;
|
||||
|
||||
switch (msg) {
|
||||
case WM_ERASEBKGND:
|
||||
break;
|
||||
|
|
|
@ -35,15 +35,33 @@ void dbp(const char *str, ...)
|
|||
#endif
|
||||
}
|
||||
|
||||
void assert_failure(const char *file, unsigned line, const char *function,
|
||||
const char *condition, const char *message) {
|
||||
dbp("File %s, line %u, function %s:", file, line, function);
|
||||
dbp("Assertion '%s' failed: ((%s) == false).", message, condition);
|
||||
#ifdef NDEBUG
|
||||
_exit(1);
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
namespace Platform {
|
||||
|
||||
bool handlingFatalError = false;
|
||||
|
||||
void FatalError(std::string message) {
|
||||
// Indicate that we're handling a fatal error, to avoid re-entering application code
|
||||
// and potentially crashing even harder.
|
||||
handlingFatalError = true;
|
||||
|
||||
message += "\nGenerate debug report?";
|
||||
switch(MessageBoxW(NULL, Platform::Widen(message).c_str(),
|
||||
L"Fatal error — SolveSpace",
|
||||
MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND|MB_TOPMOST|
|
||||
MB_OKCANCEL|MB_DEFBUTTON2)) {
|
||||
case IDOK:
|
||||
abort();
|
||||
|
||||
case IDCANCEL:
|
||||
default: {
|
||||
WCHAR appPath[MAX_PATH] = {};
|
||||
GetModuleFileNameW(NULL, appPath, sizeof(appPath));
|
||||
ShellExecuteW(NULL, L"open", appPath, NULL, NULL, SW_SHOW);
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -85,15 +103,9 @@ void vl() {
|
|||
}
|
||||
|
||||
std::vector<std::string> InitPlatform(int argc, char **argv) {
|
||||
// Create the heap used for long-lived stuff (that gets freed piecewise).
|
||||
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
||||
// Create the heap that we use to store Exprs and other temp stuff.
|
||||
FreeAllTemporary();
|
||||
|
||||
#if !defined(LIBRARY) && defined(_MSC_VER)
|
||||
// Don't display the abort message; it is aggravating in CLI binaries
|
||||
// and results in infinite WndProc recursion in GUI binaries.
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG);
|
||||
// We display our own message on abort; just call ReportFault.
|
||||
_set_abort_behavior(_CALL_REPORTFAULT, _WRITE_ABORT_MSG|_CALL_REPORTFAULT);
|
||||
int crtReportTypes[] = {_CRT_WARN, _CRT_ERROR, _CRT_ASSERT};
|
||||
for(int crtReportType : crtReportTypes) {
|
||||
_CrtSetReportMode(crtReportType, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
|
@ -101,6 +113,11 @@ std::vector<std::string> InitPlatform(int argc, char **argv) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Create the heap used for long-lived stuff (that gets freed piecewise).
|
||||
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
||||
// Create the heap that we use to store Exprs and other temp stuff.
|
||||
FreeAllTemporary();
|
||||
|
||||
// Extract the command-line arguments; the ones from main() are ignored,
|
||||
// since they are in the OEM encoding.
|
||||
int argcW;
|
||||
|
|
|
@ -56,7 +56,7 @@ typedef struct _cairo cairo_t;
|
|||
#define ssassert(condition, message) \
|
||||
do { \
|
||||
if(__builtin_expect((condition), true) == false) { \
|
||||
SolveSpace::assert_failure(__FILE__, __LINE__, __func__, #condition, message); \
|
||||
SolveSpace::AssertFailure(__FILE__, __LINE__, __func__, #condition, message); \
|
||||
__builtin_unreachable(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
@ -64,7 +64,7 @@ typedef struct _cairo cairo_t;
|
|||
#define ssassert(condition, message) \
|
||||
do { \
|
||||
if((condition) == false) { \
|
||||
SolveSpace::assert_failure(__FILE__, __LINE__, __func__, #condition, message); \
|
||||
SolveSpace::AssertFailure(__FILE__, __LINE__, __func__, #condition, message); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
@ -83,8 +83,8 @@ using std::swap;
|
|||
#if defined(__GNUC__)
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void assert_failure(const char *file, unsigned line, const char *function,
|
||||
const char *condition, const char *message);
|
||||
void AssertFailure(const char *file, unsigned line, const char *function,
|
||||
const char *condition, const char *message);
|
||||
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((__format__ (__printf__, 1, 2)))
|
||||
|
|
|
@ -6,6 +6,15 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void SolveSpace::AssertFailure(const char *file, unsigned line, const char *function,
|
||||
const char *condition, const char *message) {
|
||||
std::string formattedMsg;
|
||||
formattedMsg += ssprintf("File %s, line %u, function %s:\n", file, line, function);
|
||||
formattedMsg += ssprintf("Assertion failed: %s.\n", condition);
|
||||
formattedMsg += ssprintf("Message: %s.\n", message);
|
||||
SolveSpace::Platform::FatalError(formattedMsg);
|
||||
}
|
||||
|
||||
std::string SolveSpace::ssprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
|
Loading…
Reference in New Issue