Merge pull request #319 from dankmeme01/main

Display messages of unhandled c++ exceptions on Windows
This commit is contained in:
alk 2023-10-28 18:48:23 +03:00 committed by GitHub
commit d5000c29ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 15 deletions

View file

@ -20,6 +20,7 @@
#include <iostream>
#include <string>
#include <fmt/core.h>
#include "ehdata_structs.hpp"
using namespace geode::prelude;
@ -199,24 +200,61 @@ static std::string getRegisters(PCONTEXT context) {
static std::string getInfo(LPEXCEPTION_POINTERS info, Mod* faultyMod) {
std::stringstream stream;
stream << "Faulty Module: "
<< getModuleName(handleFromAddress(info->ExceptionRecord->ExceptionAddress), true)
<< "\n"
<< "Faulty Mod: " << (faultyMod ? faultyMod->getID() : "<Unknown>") << "\n"
<< "Exception Code: " << std::hex << info->ExceptionRecord->ExceptionCode << " ("
<< getExceptionCodeString(info->ExceptionRecord->ExceptionCode) << ")" << std::dec
<< "\n"
<< "Exception Flags: " << info->ExceptionRecord->ExceptionFlags << "\n"
<< "Exception Address: " << info->ExceptionRecord->ExceptionAddress << " (";
printAddr(stream, info->ExceptionRecord->ExceptionAddress, false);
stream << ")"
<< "\n"
<< "Number Parameters: " << info->ExceptionRecord->NumberParameters << "\n";
if (info->ExceptionRecord->ExceptionCode == EH_EXCEPTION_NUMBER) {
// This executes when a C++ exception was thrown and not handled.
// https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273
// since you can throw virtually anything, we need to figure out if it's an std::exception* or not
bool isStdException = false;
auto* exceptionRecord = info->ExceptionRecord;
auto exceptionObject = exceptionRecord->ExceptionInformation[1];
auto* throwInfo = reinterpret_cast<_ThrowInfo*>(exceptionRecord->ExceptionInformation[2]);
auto* catchableTypeArray = reinterpret_cast<_CatchableTypeArray*>(throwInfo->pCatchableTypeArray);
auto ctaSize = catchableTypeArray->nCatchableTypes;
for (int i = 0; i < ctaSize; i++) {
auto* catchableType = reinterpret_cast<_CatchableType*>(catchableTypeArray->arrayOfCatchableTypes[i]);
auto* ctDescriptor = reinterpret_cast<_TypeDescriptor*>(catchableType->pType);
const char* classname = ctDescriptor->name;
if (strcmp(classname, ".?AVexception@std@@") == 0) {
isStdException = true;
break;
}
}
if (isStdException) {
std::exception* excObject = reinterpret_cast<std::exception*>(exceptionObject);
// stream << "C++ Exception Type: " << typeid(excObject).name() << "\n"; // always const std::exception *
stream << "C++ Exception: " << excObject->what() << "\n";
} else {
stream << "C++ Exception: <Unknown type>\n";
}
stream << "Faulty Mod: " << (faultyMod ? faultyMod->getID() : "<Unknown>") << "\n";
} else {
stream << "Faulty Module: "
<< getModuleName(handleFromAddress(info->ExceptionRecord->ExceptionAddress), true)
<< "\n"
<< "Faulty Mod: " << (faultyMod ? faultyMod->getID() : "<Unknown>") << "\n"
<< "Exception Code: " << std::hex << info->ExceptionRecord->ExceptionCode << " ("
<< getExceptionCodeString(info->ExceptionRecord->ExceptionCode) << ")" << std::dec
<< "\n"
<< "Exception Flags: " << info->ExceptionRecord->ExceptionFlags << "\n"
<< "Exception Address: " << info->ExceptionRecord->ExceptionAddress << " (";
printAddr(stream, info->ExceptionRecord->ExceptionAddress, false);
stream << ")"
<< "\n"
<< "Number Parameters: " << info->ExceptionRecord->NumberParameters << "\n";
}
return stream.str();
}
static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
// init symbols so we can get some juicy debug info
@ -234,8 +272,17 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
return EXCEPTION_CONTINUE_SEARCH;
}
bool crashlog::setupPlatformHandler() {
static LONG WINAPI exceptionHandlerDummy(LPEXCEPTION_POINTERS info) {
SetUnhandledExceptionFilter(exceptionHandler);
return EXCEPTION_CONTINUE_SEARCH;
}
bool crashlog::setupPlatformHandler() {
// for some reason, on exceptions windows seems to clear SetUnhandledExceptionFilter
// so we attach a VE handler (which runs *earlier*) and inside set our crash handler
AddVectoredExceptionHandler(0, exceptionHandlerDummy);
SetUnhandledExceptionFilter(exceptionHandler);
auto lastCrashedFile = crashlog::getCrashLogDirectory() / "last-crashed";
if (ghc::filesystem::exists(lastCrashedFile)) {
g_lastLaunchCrashed = true;

View file

@ -0,0 +1,55 @@
#pragma once
// _ThrowInfo and all of those other structs are hardcoded into MSVC (the compiler itself, unavailable in any header),
// but don't exist in other compilers like Clang, causing <ehdata.h> to not compile.
//
// We define them manually in order to be able to use them.
// source: https://www.geoffchappell.com/studies/msvc/language/predefined/index.htm
#if defined(__GNUC__) || defined(__clang__) || defined(__INTELLISENSE__)
typedef struct _PMD
{
int mdisp;
int pdisp;
int vdisp;
} _PMD;
typedef void (*_PMFN) (void);
#pragma warning (disable:4200)
#pragma pack (push, _TypeDescriptor, 8)
typedef struct _TypeDescriptor
{
const void *pVFTable;
void *spare;
char name [];
} _TypeDescriptor;
#pragma pack (pop, _TypeDescriptor)
#pragma warning (default:4200)
typedef const struct _s__CatchableType {
unsigned int properties;
_TypeDescriptor *pType;
_PMD thisDisplacement;
int sizeOrOffset;
_PMFN copyFunction;
} _CatchableType;
#pragma warning (disable:4200)
typedef const struct _s__CatchableTypeArray {
int nCatchableTypes;
_CatchableType *arrayOfCatchableTypes [];
} _CatchableTypeArray;
#pragma warning (default:4200)
typedef const struct _s__ThrowInfo {
unsigned int attributes;
_PMFN pmfnUnwind;
int (__cdecl *pForwardCompat) (...);
_CatchableTypeArray *pCatchableTypeArray;
} _ThrowInfo;
#endif // defined(__GNUC__) || defined(__clang__) || defined(__INTELLISENSE__)
#include <ehdata.h> // for EH_EXCEPTION_NUMBER