mini_dump: Address review feedback
Uses fmt::print as opposed to std::fprintf. Adds a missing return. static's a single-use function. Initializes structs as opposed to std::memset where possible. Fixes CMake linkage. Co-authored-by: Lioncash <mathew1800@gmail.com> mini_dump: Use a namespace Co-authored-by: Lioncash <mathew1800@gmail.com>
This commit is contained in:
parent
45b343d1d0
commit
12f7d42d32
4 changed files with 71 additions and 63 deletions
|
@ -214,7 +214,7 @@ if (WIN32 AND YUZU_CRASH_DUMPS)
|
||||||
mini_dump.h
|
mini_dump.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(yuzu PUBLIC ${DBGHELP_LIBRARY})
|
target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY})
|
||||||
target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
|
target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -4096,10 +4096,11 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
#ifdef YUZU_DBGHELP
|
#ifdef YUZU_DBGHELP
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
if (!is_child && Settings::values.create_crash_dumps.GetValue() && SpawnDebuggee(argv[0], pi)) {
|
if (!is_child && Settings::values.create_crash_dumps.GetValue() &&
|
||||||
|
MiniDump::SpawnDebuggee(argv[0], pi)) {
|
||||||
// Delete the config object so that it doesn't save when the program exits
|
// Delete the config object so that it doesn't save when the program exits
|
||||||
config.reset(nullptr);
|
config.reset(nullptr);
|
||||||
DebugDebuggee(pi);
|
MiniDump::DebugDebuggee(pi);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "yuzu/mini_dump.h"
|
#include "yuzu/mini_dump.h"
|
||||||
#include "yuzu/startup_checks.h"
|
#include "yuzu/startup_checks.h"
|
||||||
|
@ -12,6 +13,8 @@
|
||||||
// dbghelp.h must be included after windows.h
|
// dbghelp.h must be included after windows.h
|
||||||
#include <dbghelp.h>
|
#include <dbghelp.h>
|
||||||
|
|
||||||
|
namespace MiniDump {
|
||||||
|
|
||||||
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
|
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
|
||||||
EXCEPTION_POINTERS* pep) {
|
EXCEPTION_POINTERS* pep) {
|
||||||
char file_name[255];
|
char file_name[255];
|
||||||
|
@ -23,7 +26,7 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
|
||||||
if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
|
if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
|
||||||
std::fprintf(stderr, "CreateFileA failed. Error: %d\n", GetLastError());
|
fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +37,9 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_
|
||||||
dump_type, (pep != 0) ? info : 0, 0, 0);
|
dump_type, (pep != 0) ? info : 0, 0, 0);
|
||||||
|
|
||||||
if (write_dump_status) {
|
if (write_dump_status) {
|
||||||
std::fprintf(stderr, "MiniDump created: %s\n", file_name);
|
fmt::print(stderr, "MiniDump created: {}", file_name);
|
||||||
} else {
|
} else {
|
||||||
std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError());
|
fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the file
|
// Close the file
|
||||||
|
@ -48,15 +51,15 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
|
||||||
|
|
||||||
HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
|
HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
|
||||||
if (thread_handle == nullptr) {
|
if (thread_handle == nullptr) {
|
||||||
std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError());
|
fmt::print(stderr, "OpenThread failed ({})", GetLastError());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get child process context
|
// Get child process context
|
||||||
CONTEXT context;
|
CONTEXT context = {};
|
||||||
std::memset(&context, 0, sizeof(context));
|
|
||||||
context.ContextFlags = CONTEXT_ALL;
|
context.ContextFlags = CONTEXT_ALL;
|
||||||
if (!GetThreadContext(thread_handle, &context)) {
|
if (!GetThreadContext(thread_handle, &context)) {
|
||||||
std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError());
|
fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +76,7 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
|
||||||
CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
|
CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
|
||||||
|
|
||||||
if (CloseHandle(thread_handle) == 0) {
|
if (CloseHandle(thread_handle) == 0) {
|
||||||
std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", GetLastError());
|
fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,67 +89,22 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SpawnChild(arg0, &pi, 0)) {
|
if (!SpawnChild(arg0, &pi, 0)) {
|
||||||
std::fprintf(stderr, "warning: continuing without crash dumps\n");
|
fmt::print(stderr, "warning: continuing without crash dumps");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool can_debug = DebugActiveProcess(pi.dwProcessId);
|
const bool can_debug = DebugActiveProcess(pi.dwProcessId);
|
||||||
if (!can_debug) {
|
if (!can_debug) {
|
||||||
std::fprintf(stderr,
|
fmt::print(stderr,
|
||||||
"warning: DebugActiveProcess failed (%d), continuing without crash dumps\n",
|
"warning: DebugActiveProcess failed ({}), continuing without crash dumps",
|
||||||
GetLastError());
|
GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugDebuggee(PROCESS_INFORMATION& pi) {
|
static const char* ExceptionName(DWORD exception) {
|
||||||
DEBUG_EVENT deb_ev;
|
|
||||||
std::memset(&deb_ev, 0, sizeof(deb_ev));
|
|
||||||
|
|
||||||
while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
|
|
||||||
const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
|
|
||||||
if (!wait_success) {
|
|
||||||
std::fprintf(stderr, "error: WaitForDebugEvent failed (%d)\n", GetLastError());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (deb_ev.dwDebugEventCode) {
|
|
||||||
case OUTPUT_DEBUG_STRING_EVENT:
|
|
||||||
case CREATE_PROCESS_DEBUG_EVENT:
|
|
||||||
case CREATE_THREAD_DEBUG_EVENT:
|
|
||||||
case EXIT_PROCESS_DEBUG_EVENT:
|
|
||||||
case EXIT_THREAD_DEBUG_EVENT:
|
|
||||||
case LOAD_DLL_DEBUG_EVENT:
|
|
||||||
case RIP_EVENT:
|
|
||||||
case UNLOAD_DLL_DEBUG_EVENT:
|
|
||||||
// Continue on all other debug events
|
|
||||||
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
|
|
||||||
break;
|
|
||||||
case EXCEPTION_DEBUG_EVENT:
|
|
||||||
EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
|
|
||||||
|
|
||||||
// We want to generate a crash dump if we are seeing the same exception again.
|
|
||||||
if (!deb_ev.u.Exception.dwFirstChance) {
|
|
||||||
std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n",
|
|
||||||
record.ExceptionCode, ExceptionName(record.ExceptionCode));
|
|
||||||
DumpFromDebugEvent(deb_ev, pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue without handling the exception.
|
|
||||||
// Lets the debuggee use its own exception handler.
|
|
||||||
// - If one does not exist, we will see the exception once more where we make a minidump
|
|
||||||
// for. Then when it reaches here again, yuzu will probably crash.
|
|
||||||
// - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
|
|
||||||
// infinite loop of exceptions.
|
|
||||||
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ExceptionName(DWORD exception) {
|
|
||||||
switch (exception) {
|
switch (exception) {
|
||||||
case EXCEPTION_ACCESS_VIOLATION:
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
return "EXCEPTION_ACCESS_VIOLATION";
|
return "EXCEPTION_ACCESS_VIOLATION";
|
||||||
|
@ -193,6 +151,52 @@ const char* ExceptionName(DWORD exception) {
|
||||||
case EXCEPTION_INVALID_HANDLE:
|
case EXCEPTION_INVALID_HANDLE:
|
||||||
return "EXCEPTION_INVALID_HANDLE";
|
return "EXCEPTION_INVALID_HANDLE";
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return "unknown exception type";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugDebuggee(PROCESS_INFORMATION& pi) {
|
||||||
|
DEBUG_EVENT deb_ev = {};
|
||||||
|
|
||||||
|
while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
|
||||||
|
const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
|
||||||
|
if (!wait_success) {
|
||||||
|
fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (deb_ev.dwDebugEventCode) {
|
||||||
|
case OUTPUT_DEBUG_STRING_EVENT:
|
||||||
|
case CREATE_PROCESS_DEBUG_EVENT:
|
||||||
|
case CREATE_THREAD_DEBUG_EVENT:
|
||||||
|
case EXIT_PROCESS_DEBUG_EVENT:
|
||||||
|
case EXIT_THREAD_DEBUG_EVENT:
|
||||||
|
case LOAD_DLL_DEBUG_EVENT:
|
||||||
|
case RIP_EVENT:
|
||||||
|
case UNLOAD_DLL_DEBUG_EVENT:
|
||||||
|
// Continue on all other debug events
|
||||||
|
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
|
||||||
|
break;
|
||||||
|
case EXCEPTION_DEBUG_EVENT:
|
||||||
|
EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
|
||||||
|
|
||||||
|
// We want to generate a crash dump if we are seeing the same exception again.
|
||||||
|
if (!deb_ev.u.Exception.dwFirstChance) {
|
||||||
|
fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
|
||||||
|
record.ExceptionCode, ExceptionName(record.ExceptionCode));
|
||||||
|
DumpFromDebugEvent(deb_ev, pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue without handling the exception.
|
||||||
|
// Lets the debuggee use its own exception handler.
|
||||||
|
// - If one does not exist, we will see the exception once more where we make a minidump
|
||||||
|
// for. Then when it reaches here again, yuzu will probably crash.
|
||||||
|
// - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
|
||||||
|
// infinite loop of exceptions.
|
||||||
|
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace MiniDump
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
|
|
||||||
#include <dbghelp.h>
|
#include <dbghelp.h>
|
||||||
|
|
||||||
|
namespace MiniDump {
|
||||||
|
|
||||||
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
|
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
|
||||||
EXCEPTION_POINTERS* pep);
|
EXCEPTION_POINTERS* pep);
|
||||||
|
|
||||||
void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
|
void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
|
||||||
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
|
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
|
||||||
void DebugDebuggee(PROCESS_INFORMATION& pi);
|
void DebugDebuggee(PROCESS_INFORMATION& pi);
|
||||||
const char* ExceptionName(DWORD exception);
|
|
||||||
|
} // namespace MiniDump
|
||||||
|
|
Loading…
Reference in a new issue