diff --git a/src/common/platform/posix/cocoa/i_system.mm b/src/common/platform/posix/cocoa/i_system.mm index 267bac31d..d4c28624c 100644 --- a/src/common/platform/posix/cocoa/i_system.mm +++ b/src/common/platform/posix/cocoa/i_system.mm @@ -195,3 +195,6 @@ void I_OpenShellFolder(const char* folder) [filemgr changeCurrentDirectoryPath:currentpath]; } +void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FString& logText) +{ +} diff --git a/src/common/platform/posix/sdl/i_system.cpp b/src/common/platform/posix/sdl/i_system.cpp index 2c28368b0..3f2dd6a8c 100644 --- a/src/common/platform/posix/sdl/i_system.cpp +++ b/src/common/platform/posix/sdl/i_system.cpp @@ -388,3 +388,6 @@ void I_OpenShellFolder(const char* infolder) free(curdir); } +void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FString& logText) +{ +} diff --git a/src/common/platform/win32/i_crash.cpp b/src/common/platform/win32/i_crash.cpp index e8f77cc76..2cca66e96 100644 --- a/src/common/platform/win32/i_crash.cpp +++ b/src/common/platform/win32/i_crash.cpp @@ -30,6 +30,9 @@ #ifdef _MSC_VER #include #include +#include + +#pragma comment(lib, "dbgeng.lib") class CrashReporter { @@ -404,3 +407,123 @@ const BYTE CrashReporter::patch_bytes[5] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; #endif #endif + +template +class DbgPtr +{ +public: + DbgPtr() { Ptr = nullptr; } + DbgPtr(const DbgPtr& other) { Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); } + DbgPtr(DbgPtr&& move) { Ptr = move.Ptr; move.Ptr = nullptr; } + ~DbgPtr() { reset(); } + + void reset() { if (Ptr) Ptr->Release(); Ptr = nullptr; } + T* get() { return Ptr; } + + static IID GetIID() { return __uuidof(T); } + + void** InitPtr() { return (void**)TypedInitPtr(); } + T** TypedInitPtr() { reset(); return &Ptr; } + + DbgPtr& operator=(const DbgPtr& other) + { + if (this != &other) + { + if (Ptr) + Ptr->Release(); + Ptr = other.Ptr; + if (Ptr) + Ptr->AddRef(); + } + return *this; + } + + operator T* () const { return Ptr; } + T* operator ->() const { return Ptr; } + + T* Ptr; +}; + +void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FString& logText) +{ + DbgPtr client; + HRESULT result = DebugCreate(client.GetIID(), client.InitPtr()); + if (FAILED(result)) + return; + + DbgPtr control; + result = client->QueryInterface(control.GetIID(), control.InitPtr()); + if (FAILED(result)) + return; + + DbgPtr symbols; + result = client->QueryInterface(symbols.GetIID(), symbols.InitPtr()); + if (FAILED(result)) + return; + + result = client->OpenDumpFileWide(minidumpFilename.WideString().c_str(), 0); + if (FAILED(result)) + return; + + result = control->WaitForEvent(0, INFINITE); + if (FAILED(result)) + return; + + ULONG eventType, processId = 0, threadId = 0, descriptionSize = 0; + char descriptionText[1024] = {}; + result = control->GetLastEventInformation(&eventType, &processId, &threadId, nullptr, 0, nullptr, descriptionText, 1024, &descriptionSize); + if (SUCCEEDED(result) && descriptionSize > 0) + { + std::string description(descriptionText, descriptionSize - 1); + text = description; + logText.AppendFormat("\n%s\n", description.c_str()); + } + + DEBUG_STACK_FRAME frames[20] = {}; + ULONG filled = 0; + result = control->GetStackTrace(0, 0, 0, frames, 20, &filled); + if (FAILED(result)) + return; + + for (ULONG i = 0; i < filled; i++) + { + char nameBuffer[1024] = {}; + ULONG nameSize = 0; + ULONG64 displacement = 0; + result = symbols->GetNameByOffset(frames[i].InstructionOffset, nameBuffer, 1024, &nameSize, &displacement); + if (SUCCEEDED(result) && nameSize > 0) + { + std::string name(nameBuffer, nameSize - 1); + std::string module; + size_t moduleend = name.find('!'); + if (moduleend != std::string::npos) + { + module = name.substr(0, moduleend); + name = name.substr(moduleend + 1); + } + + ULONG line = 0; + char fileBuffer[1024] = {}; + ULONG fileSize = 0; + ULONG64 displacement = 0; + result = symbols->GetLineByOffset(frames[i].InstructionOffset, &line, fileBuffer, 1024, &fileSize, &displacement); + if (SUCCEEDED(result) && fileSize > 0) + { + std::string file(fileBuffer, fileSize - 1); + size_t lastpart = file.find_last_of("/\\"); + if (lastpart != std::string::npos) + file = file.substr(lastpart + 1); + + logText.AppendFormat("Called from %s at %s, line %d\n", name.c_str(), file.c_str(), line); + } + else + { + // logText.AppendFormat("Called from %s in module %s\n", name.c_str(), module.c_str()); + } + } + else + { + // logText.AppendFormat("Called from %d\n", frames[i].InstructionOffset); + } + } +} diff --git a/src/common/platform/win32/i_system.h b/src/common/platform/win32/i_system.h index b70efb78b..bd9e5a74f 100644 --- a/src/common/platform/win32/i_system.h +++ b/src/common/platform/win32/i_system.h @@ -84,3 +84,5 @@ FString I_GetCWD(); bool I_ChDir(const char* path); #endif + +void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FString& logText); diff --git a/src/d_main.cpp b/src/d_main.cpp index 8e6cf8633..4c91a2319 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -3745,6 +3745,9 @@ static int D_DoomMain_Internal (void) if (Args->CheckParm("-showcrashreport")) { + FString text; + text.Format("%s has performed an illegal operation", GAMENAME); + FString minidumpFilename = Args->GetArg(2); FString logFilename = Args->GetArg(3); @@ -3766,15 +3769,18 @@ static int D_DoomMain_Internal (void) if (fr.OpenFile(minidumpFilename.GetChars())) { minidump.resize(fr.GetLength()); - if (fr.Read(minidump.data(), minidump.size()) != (FileReader::Size)minidump.size()) + if (fr.Read(minidump.data(), minidump.size()) == (FileReader::Size)minidump.size()) + { + fr.Close(); + I_AddMinidumpCallstack(minidumpFilename, text, logText); + } + else { minidump.clear(); } } } - FString text; - text.Format("%s fatally crashed!", GAMENAME); ErrorWindow::ExecModal(text.GetChars(), logText.GetChars(), std::move(minidump)); // Crash reporter only uses -showcrashreport on Windows at the moment and there seems to be no abstraction available