Dbgeng didn't work - let's try do it ourselves
This commit is contained in:
parent
8f180e3a3f
commit
ddffb4193a
1 changed files with 481 additions and 38 deletions
|
|
@ -31,9 +31,22 @@
|
|||
#ifdef _MSC_VER
|
||||
#include <dbghelp.h>
|
||||
#include <signal.h>
|
||||
#include <Dbgeng.h>
|
||||
|
||||
#if defined(USE_DBGENG)
|
||||
#include <Dbgeng.h>
|
||||
#pragma comment(lib, "dbgeng.lib")
|
||||
#else
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#endif
|
||||
|
||||
struct StackFrameList
|
||||
{
|
||||
enum { max_frames = 16 };
|
||||
int frame_count;
|
||||
void* frames[max_frames];
|
||||
};
|
||||
|
||||
enum { UserStackFrameListStream = LastReservedStream + 1 };
|
||||
|
||||
class CrashReporter
|
||||
{
|
||||
|
|
@ -53,6 +66,7 @@ private:
|
|||
int thread_id;
|
||||
PEXCEPTION_POINTERS exception_pointers;
|
||||
unsigned int exception_code;
|
||||
StackFrameList stack_frames;
|
||||
};
|
||||
|
||||
void load_dbg_help();
|
||||
|
|
@ -70,6 +84,8 @@ private:
|
|||
static void on_se_unhandled_exception(unsigned int exception_code, PEXCEPTION_POINTERS exception_pointers);
|
||||
static LONG WINAPI on_win32_unhandled_exception(PEXCEPTION_POINTERS exception_pointers);
|
||||
|
||||
static int CaptureStackTrace(int max_frames, void** out_frames);
|
||||
|
||||
typedef BOOL(WINAPI* MiniDumpWriteDumpPointer)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, CONST PMINIDUMP_EXCEPTION_INFORMATION, CONST PMINIDUMP_USER_STREAM_INFORMATION, CONST PMINIDUMP_CALLBACK_INFORMATION);
|
||||
HMODULE module_dbghlp;
|
||||
static MiniDumpWriteDumpPointer func_MiniDumpWriteDump;
|
||||
|
|
@ -177,6 +193,15 @@ void CrashReporter::create_dump(DumpParams* dump_params, bool launch_uploader)
|
|||
if (file == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
MINIDUMP_USER_STREAM userStream = {};
|
||||
userStream.Type = UserStackFrameListStream;
|
||||
userStream.BufferSize = sizeof(StackFrameList);
|
||||
userStream.Buffer = &dump_params->stack_frames;
|
||||
|
||||
MINIDUMP_USER_STREAM_INFORMATION userStreamList = {};
|
||||
userStreamList.UserStreamCount = 1;
|
||||
userStreamList.UserStreamArray = &userStream;
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION info;
|
||||
info.ThreadId = dump_params->thread_id;
|
||||
info.ExceptionPointers = dump_params->exception_pointers;
|
||||
|
|
@ -186,7 +211,7 @@ void CrashReporter::create_dump(DumpParams* dump_params, bool launch_uploader)
|
|||
MiniDumpWithProcessThreadData |
|
||||
MiniDumpWithFullMemoryInfo |
|
||||
MiniDumpWithThreadInfo);
|
||||
func_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, type, &info, 0, 0);
|
||||
func_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, type, &info, &userStreamList, 0);
|
||||
CloseHandle(file);
|
||||
|
||||
WCHAR log_filename[1024];
|
||||
|
|
@ -265,6 +290,56 @@ void CrashReporter::on_sigabort(int)
|
|||
invoke();
|
||||
}
|
||||
|
||||
int CrashReporter::CaptureStackTrace(int max_frames, void** out_frames)
|
||||
{
|
||||
memset(out_frames, 0, sizeof(void*) * max_frames);
|
||||
|
||||
#ifdef _WIN64
|
||||
// RtlCaptureStackBackTrace doesn't support RtlAddFunctionTable..
|
||||
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
UNWIND_HISTORY_TABLE history;
|
||||
memset(&history, 0, sizeof(UNWIND_HISTORY_TABLE));
|
||||
|
||||
ULONG64 establisherframe = 0;
|
||||
PVOID handlerdata = nullptr;
|
||||
|
||||
int frame;
|
||||
for (frame = 0; frame < max_frames; frame++)
|
||||
{
|
||||
ULONG64 imagebase;
|
||||
PRUNTIME_FUNCTION rtfunc = RtlLookupFunctionEntry(context.Rip, &imagebase, &history);
|
||||
|
||||
KNONVOLATILE_CONTEXT_POINTERS nvcontext;
|
||||
memset(&nvcontext, 0, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
|
||||
if (!rtfunc)
|
||||
{
|
||||
// Leaf function
|
||||
context.Rip = (ULONG64)(*(PULONG64)context.Rsp);
|
||||
context.Rsp += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
RtlVirtualUnwind(UNW_FLAG_NHANDLER, imagebase, context.Rip, rtfunc, &context, &handlerdata, &establisherframe, &nvcontext);
|
||||
}
|
||||
|
||||
if (!context.Rip)
|
||||
break;
|
||||
|
||||
out_frames[frame] = (void*)context.Rip;
|
||||
}
|
||||
return frame;
|
||||
|
||||
#elif defined(WIN32)
|
||||
// JIT isn't supported here, so just do nothing.
|
||||
return 0;//return RtlCaptureStackBackTrace(0, min(max_frames, 32), out_frames, nullptr);
|
||||
#else
|
||||
return backtrace(out_frames, max_frames);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrashReporter::on_se_unhandled_exception(unsigned int exception_code, PEXCEPTION_POINTERS exception_pointers)
|
||||
{
|
||||
// Ignore those bloody breakpoints!
|
||||
|
|
@ -276,6 +351,7 @@ void CrashReporter::on_se_unhandled_exception(unsigned int exception_code, PEXCE
|
|||
dump_params.thread_id = GetCurrentThreadId();
|
||||
dump_params.exception_pointers = exception_pointers;
|
||||
dump_params.exception_code = exception_code;
|
||||
dump_params.stack_frames.frame_count = CaptureStackTrace(StackFrameList::max_frames, dump_params.stack_frames.frames);
|
||||
|
||||
// Ensure we only get a dump of the first thread crashing - let other threads block here.
|
||||
std::unique_lock<std::recursive_mutex> mutex_lock(mutex);
|
||||
|
|
@ -296,6 +372,7 @@ LONG CrashReporter::on_win32_unhandled_exception(PEXCEPTION_POINTERS exception_p
|
|||
dump_params.thread_id = GetCurrentThreadId();
|
||||
dump_params.exception_pointers = exception_pointers;
|
||||
dump_params.exception_code = 0;
|
||||
dump_params.stack_frames.frame_count = CaptureStackTrace(StackFrameList::max_frames, dump_params.stack_frames.frames);
|
||||
|
||||
// Ensure we only get a dump of the first thread crashing - let other threads block here.
|
||||
std::unique_lock<std::recursive_mutex> mutex_lock(mutex);
|
||||
|
|
@ -409,42 +486,6 @@ const BYTE CrashReporter::patch_bytes[5] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 };
|
|||
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
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;
|
||||
};
|
||||
|
||||
static std::wstring GetExeFilename()
|
||||
{
|
||||
wchar_t buffer[1024] = {};
|
||||
|
|
@ -481,6 +522,44 @@ static bool IsPdbFileMissing()
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(USE_DBGENG)
|
||||
|
||||
template<typename T>
|
||||
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)
|
||||
{
|
||||
if (IsPdbFileMissing())
|
||||
|
|
@ -584,3 +663,367 @@ void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FStr
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct MinidumpStream
|
||||
{
|
||||
MINIDUMP_DIRECTORY* Directory = nullptr;
|
||||
void* Data = nullptr;
|
||||
ULONG Size = 0;
|
||||
};
|
||||
|
||||
class MinidumpFile
|
||||
{
|
||||
public:
|
||||
MinidumpFile(const wchar_t* filename)
|
||||
{
|
||||
FileHandle = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
|
||||
if (!FileHandle)
|
||||
I_FatalError("Could not open minidump file");
|
||||
|
||||
MappingHandle = CreateFileMapping(FileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
||||
if (!MappingHandle)
|
||||
{
|
||||
CloseHandle(FileHandle);
|
||||
I_FatalError("Could not create minidump file mapping");
|
||||
}
|
||||
|
||||
BaseOfDump = MapViewOfFile(MappingHandle, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!BaseOfDump)
|
||||
{
|
||||
CloseHandle(MappingHandle);
|
||||
CloseHandle(FileHandle);
|
||||
I_FatalError("Could not map minidump file");
|
||||
}
|
||||
}
|
||||
|
||||
~MinidumpFile()
|
||||
{
|
||||
UnmapViewOfFile(BaseOfDump);
|
||||
CloseHandle(MappingHandle);
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
|
||||
std::vector<MINIDUMP_THREAD> GetThreadList()
|
||||
{
|
||||
MinidumpStream s = ReadStream(ThreadListStream);
|
||||
MINIDUMP_THREAD_LIST* list = static_cast<MINIDUMP_THREAD_LIST*>(s.Data);
|
||||
std::vector<MINIDUMP_THREAD> threads(list->NumberOfThreads);
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
threads[i] = list->Threads[i];
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<MINIDUMP_THREAD_EX> GetThreadExList()
|
||||
{
|
||||
MinidumpStream s = ReadStream(ThreadExListStream);
|
||||
MINIDUMP_THREAD_EX_LIST* list = static_cast<MINIDUMP_THREAD_EX_LIST*>(s.Data);
|
||||
std::vector<MINIDUMP_THREAD_EX> threads(list->NumberOfThreads);
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
threads[i] = list->Threads[i];
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<MINIDUMP_MODULE> GetModuleList()
|
||||
{
|
||||
MinidumpStream s = ReadStream(ModuleListStream);
|
||||
MINIDUMP_MODULE_LIST* list = static_cast<MINIDUMP_MODULE_LIST*>(s.Data);
|
||||
std::vector<MINIDUMP_MODULE> modules(list->NumberOfModules);
|
||||
for (size_t i = 0; i < modules.size(); i++)
|
||||
modules[i] = list->Modules[i];
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<MINIDUMP_MEMORY_DESCRIPTOR> GetMemoryList()
|
||||
{
|
||||
MinidumpStream s = ReadStream(MemoryListStream);
|
||||
MINIDUMP_MEMORY_LIST* list = static_cast<MINIDUMP_MEMORY_LIST*>(s.Data);
|
||||
std::vector<MINIDUMP_MEMORY_DESCRIPTOR> descriptors(list->NumberOfMemoryRanges);
|
||||
for (size_t i = 0; i < descriptors.size(); i++)
|
||||
descriptors[i] = list->MemoryRanges[i];
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
std::vector<MINIDUMP_MEMORY_DESCRIPTOR64> GetMemory64List()
|
||||
{
|
||||
MinidumpStream s = ReadStream(Memory64ListStream);
|
||||
MINIDUMP_MEMORY64_LIST* list = static_cast<MINIDUMP_MEMORY64_LIST*>(s.Data);
|
||||
std::vector<MINIDUMP_MEMORY_DESCRIPTOR64> descriptors(list->NumberOfMemoryRanges);
|
||||
for (size_t i = 0; i < descriptors.size(); i++)
|
||||
descriptors[i] = list->MemoryRanges[i];
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
StackFrameList* GetUserStackFrameList()
|
||||
{
|
||||
MinidumpStream s = ReadStream(UserStackFrameListStream);
|
||||
if (s.Size == sizeof(StackFrameList))
|
||||
return static_cast<StackFrameList*>(s.Data);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MINIDUMP_EXCEPTION_STREAM GetException()
|
||||
{
|
||||
MinidumpStream s = ReadStream(ExceptionStream);
|
||||
return *static_cast<MINIDUMP_EXCEPTION_STREAM*>(s.Data);
|
||||
}
|
||||
|
||||
MINIDUMP_SYSTEM_INFO GetSystemInfo()
|
||||
{
|
||||
MinidumpStream s = ReadStream(SystemInfoStream);
|
||||
return *static_cast<MINIDUMP_SYSTEM_INFO*>(s.Data);
|
||||
}
|
||||
|
||||
MinidumpStream ReadStream(ULONG streamNumber)
|
||||
{
|
||||
MinidumpStream stream;
|
||||
BOOL result = MiniDumpReadDumpStream(BaseOfDump, streamNumber, &stream.Directory, &stream.Data, &stream.Size);
|
||||
if (!result)
|
||||
I_FatalError("MiniDumpReadDumpStream failed");
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* GetPtr(RVA rva)
|
||||
{
|
||||
return reinterpret_cast<T*>(static_cast<char*>(BaseOfDump) + rva);
|
||||
}
|
||||
|
||||
std::wstring GetString(RVA rva)
|
||||
{
|
||||
MINIDUMP_STRING* str = GetPtr<MINIDUMP_STRING>(rva);
|
||||
return std::wstring(str->Buffer, str->Length);
|
||||
}
|
||||
|
||||
HANDLE FileHandle = {};
|
||||
HANDLE MappingHandle = {};
|
||||
void* BaseOfDump = nullptr;
|
||||
|
||||
private:
|
||||
MinidumpFile(const MinidumpFile&) = delete;
|
||||
MinidumpFile& operator=(const MinidumpFile&) = delete;
|
||||
};
|
||||
|
||||
class MinidumpDebugger
|
||||
{
|
||||
public:
|
||||
MinidumpDebugger()
|
||||
{
|
||||
// We need an unique legal process handle for our debug session. Yes, this is really how Microsoft designed this API!
|
||||
HANDLE currentProcess = GetCurrentProcess();
|
||||
BOOL result = DuplicateHandle(currentProcess, currentProcess, currentProcess, &process, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
if (!result)
|
||||
I_FatalError("DuplicateHandle failed");
|
||||
|
||||
DWORD flags =
|
||||
SYMOPT_UNDNAME | // Return undecorated symbol names
|
||||
SYMOPT_DEFERRED_LOADS | // Delay loading of modules and pdb files until we need them
|
||||
SYMOPT_EXACT_SYMBOLS | // Only load modules that match the minidump
|
||||
SYMOPT_FAIL_CRITICAL_ERRORS | // Never show any UI dialogs to the user
|
||||
SYMOPT_LOAD_LINES; // Load symbol line info
|
||||
SymSetOptions(flags);
|
||||
|
||||
result = SymInitialize(process, nullptr, FALSE);
|
||||
if (!result)
|
||||
{
|
||||
CloseHandle(process);
|
||||
I_FatalError("SymInitialize failed");
|
||||
}
|
||||
}
|
||||
|
||||
~MinidumpDebugger()
|
||||
{
|
||||
for (auto& module : Modules)
|
||||
{
|
||||
if (module.BaseOfDll != 0)
|
||||
SymUnloadModule64(process, module.BaseOfDll);
|
||||
}
|
||||
|
||||
SymCleanup(process);
|
||||
CloseHandle(process);
|
||||
}
|
||||
|
||||
void Open(const wchar_t* filename)
|
||||
{
|
||||
minidump = std::make_unique<MinidumpFile>(filename);
|
||||
LoadModuleList();
|
||||
LoadExceptionInfo();
|
||||
LoadUserStackFrameList();
|
||||
}
|
||||
|
||||
const StackFrameList& GetStackFrameList() const { return stackFrameList; }
|
||||
|
||||
FString GetExceptionText()
|
||||
{
|
||||
switch (ExceptionInfo.Record.ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION: return "Access violation";
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded";
|
||||
case EXCEPTION_BREAKPOINT: return "Breakpoint";
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype misalignment";
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND: return "Floating point denormal operand";
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Floating point divide by zero";
|
||||
case EXCEPTION_FLT_INEXACT_RESULT: return "Floating point inexact result";
|
||||
case EXCEPTION_FLT_INVALID_OPERATION: return "Floating point invalid operation";
|
||||
case EXCEPTION_FLT_OVERFLOW: return "Floating point overflow";
|
||||
case EXCEPTION_FLT_STACK_CHECK: return "Floating point stack check";
|
||||
case EXCEPTION_FLT_UNDERFLOW: return "Floating point underflow";
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION: return "Floating point illegal instruction";
|
||||
case EXCEPTION_IN_PAGE_ERROR: return "In page error";
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer divide by zero";
|
||||
case EXCEPTION_INT_OVERFLOW: return "Integer overflow";
|
||||
case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition";
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception";
|
||||
case EXCEPTION_PRIV_INSTRUCTION: return "Priv instruction";
|
||||
case EXCEPTION_SINGLE_STEP: return "Single step";
|
||||
case EXCEPTION_STACK_OVERFLOW: return "Stack overflow";
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
||||
FString GetModuleName(void* frame)
|
||||
{
|
||||
IMAGEHLP_MODULE64 moduleInfo = { sizeof(IMAGEHLP_MODULE64) };
|
||||
SymGetModuleInfo64(process, (DWORD64)frame, &moduleInfo);
|
||||
return moduleInfo.ModuleName;
|
||||
}
|
||||
|
||||
FString GetCalledFromText(void* frame)
|
||||
{
|
||||
FString s;
|
||||
|
||||
unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + 128];
|
||||
IMAGEHLP_SYMBOL64* symbol64 = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
|
||||
memset(symbol64, 0, sizeof(IMAGEHLP_SYMBOL64) + 128);
|
||||
symbol64->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol64->MaxNameLength = 128;
|
||||
|
||||
DWORD64 displacement = 0;
|
||||
BOOL result = SymGetSymFromAddr64(process, (DWORD64)frame, &displacement, symbol64);
|
||||
if (result)
|
||||
{
|
||||
IMAGEHLP_LINE64 line64;
|
||||
DWORD displacement1 = 0;
|
||||
memset(&line64, 0, sizeof(IMAGEHLP_LINE64));
|
||||
line64.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
result = SymGetLineFromAddr64(process, (DWORD64)frame, &displacement1, &line64);
|
||||
if (result)
|
||||
{
|
||||
s.Format("Called from %s at %s, line %d\n", symbol64->Name, line64.FileName, (int)line64.LineNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Format("Called from %s\n", symbol64->Name);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Module
|
||||
{
|
||||
std::string ImageName;
|
||||
DWORD64 BaseOfDll = 0;
|
||||
};
|
||||
|
||||
std::vector<Module> Modules;
|
||||
|
||||
struct
|
||||
{
|
||||
int ThreadId = 0;
|
||||
CONTEXT* Context = nullptr;
|
||||
MINIDUMP_EXCEPTION Record = {};
|
||||
} ExceptionInfo;
|
||||
|
||||
void LoadModuleList()
|
||||
{
|
||||
for (const MINIDUMP_MODULE& module : minidump->GetModuleList())
|
||||
{
|
||||
Module m;
|
||||
m.ImageName = from_utf16(minidump->GetString(module.ModuleNameRva));
|
||||
m.BaseOfDll = SymLoadModuleEx(process, 0, m.ImageName.c_str(), nullptr, module.BaseOfImage, module.SizeOfImage, nullptr, 0);
|
||||
Modules.push_back(std::move(m));
|
||||
}
|
||||
}
|
||||
|
||||
void LoadExceptionInfo()
|
||||
{
|
||||
auto info = minidump->GetException();
|
||||
ExceptionInfo.ThreadId = info.ThreadId;
|
||||
if (info.ThreadContext.DataSize == sizeof(CONTEXT))
|
||||
ExceptionInfo.Context = minidump->GetPtr<CONTEXT>(info.ThreadContext.Rva);
|
||||
ExceptionInfo.Record = info.ExceptionRecord;
|
||||
}
|
||||
|
||||
void LoadUserStackFrameList()
|
||||
{
|
||||
StackFrameList* list = minidump->GetUserStackFrameList();
|
||||
if (list)
|
||||
stackFrameList = *list;
|
||||
}
|
||||
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
I_FatalError("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
I_FatalError("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<MinidumpFile> minidump;
|
||||
HANDLE process = 0;
|
||||
StackFrameList stackFrameList = {};
|
||||
};
|
||||
|
||||
void I_AddMinidumpCallstack(const FString& minidumpFilename, FString& text, FString& logText)
|
||||
{
|
||||
if (IsPdbFileMissing())
|
||||
logText.AppendFormat("\nWarning: could not find " GAMENAMELOWERCASE ".pdb - No call stack will be displayed for the crash.\n");
|
||||
|
||||
try
|
||||
{
|
||||
MinidumpDebugger debugger;
|
||||
debugger.Open(minidumpFilename.WideString().c_str());
|
||||
|
||||
FString exceptionText = debugger.GetExceptionText();
|
||||
if (!exceptionText.IsEmpty())
|
||||
{
|
||||
text = exceptionText;
|
||||
logText.AppendFormat("\n%s\n", exceptionText.GetChars());
|
||||
}
|
||||
|
||||
FString lastExternalCode;
|
||||
const StackFrameList& callstack = debugger.GetStackFrameList();
|
||||
for (int i = 1; i < callstack.frame_count; i++)
|
||||
{
|
||||
FString text = debugger.GetCalledFromText(callstack.frames[i]);
|
||||
if (!text.IsEmpty())
|
||||
{
|
||||
logText += text;
|
||||
lastExternalCode = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
FString moduleName = debugger.GetModuleName(callstack.frames[i]);
|
||||
if (!moduleName.IsEmpty() && moduleName != lastExternalCode)
|
||||
logText.AppendFormat("Called from external code [%s]\n", moduleName.GetChars());
|
||||
lastExternalCode = moduleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Just ignore errors for now.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue