From f958cbc737542332ed4de9cf503fa4a8d1106564 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sun, 10 Jul 2022 11:29:10 -0400 Subject: yuzu: Use a debugger to generate minidumps yuzu: Move mini_dump out of core startup_checks: Better exception handling --- src/yuzu/mini_dump.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/yuzu/mini_dump.cpp (limited to 'src/yuzu/mini_dump.cpp') diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp new file mode 100644 index 000000000..ad8a4f607 --- /dev/null +++ b/src/yuzu/mini_dump.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include "common/logging/log.h" +#include "yuzu/mini_dump.h" +#include "yuzu/startup_checks.h" + +// dbghelp.h must be included after windows.h +#include + +void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, + EXCEPTION_POINTERS* pep) { + LOG_INFO(Core, "called"); + + char file_name[255]; + const std::time_t the_time = std::time(nullptr); + std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); + + // Open the file + HANDLE file_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if ((file_handle != nullptr) && (file_handle != INVALID_HANDLE_VALUE)) { + // Create the minidump + const MINIDUMP_TYPE dump_type = MiniDumpNormal; + + const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, + dump_type, (pep != 0) ? info : 0, 0, 0); + + if (!write_dump_status) { + LOG_ERROR(Core, "MiniDumpWriteDump failed. Error: {}", GetLastError()); + } else { + LOG_INFO(Core, "Minidump created."); + } + + // Close the file + CloseHandle(file_handle); + + } else { + LOG_ERROR(Core, "CreateFile failed. Error: {}", GetLastError()); + } +} + +bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { + std::memset(&pi, 0, sizeof(pi)); + + if (!SpawnChild(arg0, &pi, 0)) { + std::fprintf(stderr, "warning: continuing without crash dumps\n"); + return false; + } + + // Don't debug if we are already being debugged + if (IsDebuggerPresent()) { + return false; + } + + const bool can_debug = DebugActiveProcess(pi.dwProcessId); + if (!can_debug) { + std::fprintf(stderr, + "warning: DebugActiveProcess failed (%d), continuing without crash dumps\n", + GetLastError()); + return false; + } + + return true; +} + +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) { + 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: + ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); + break; + case EXCEPTION_DEBUG_EVENT: + EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; + + std::fprintf(stderr, "ExceptionCode: 0x%08x %s\n", record.ExceptionCode, + ExceptionName(record.ExceptionCode)); + if (!deb_ev.u.Exception.dwFirstChance) { + HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, deb_ev.dwThreadId); + if (thread_handle == nullptr) { + std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); + } + if (SuspendThread(thread_handle) == (DWORD)-1) { + std::fprintf(stderr, "SuspendThread failed (%d)\n", GetLastError()); + } + + CONTEXT context; + std::memset(&context, 0, sizeof(context)); + context.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(thread_handle, &context)) { + std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); + break; + } + + EXCEPTION_POINTERS ep; + ep.ExceptionRecord = &record; + ep.ContextRecord = &context; + + MINIDUMP_EXCEPTION_INFORMATION info; + info.ThreadId = deb_ev.dwThreadId; + info.ExceptionPointers = &ep; + info.ClientPointers = false; + + CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); + + std::fprintf(stderr, "previous thread suspend count: %d\n", + ResumeThread(thread_handle)); + if (CloseHandle(thread_handle) == 0) { + std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", + GetLastError()); + } + } + ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); + break; + } + } +} + +const char* ExceptionName(DWORD exception) { + switch (exception) { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_BREAKPOINT: + return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_SINGLE_STEP: + return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: + return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: + return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: + return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: + return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_PRIV_INSTRUCTION: + return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: + return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: + return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_GUARD_PAGE: + return "EXCEPTION_GUARD_PAGE"; + case EXCEPTION_INVALID_HANDLE: + return "EXCEPTION_INVALID_HANDLE"; + default: + return nullptr; + } +} -- cgit v1.2.3 From 3dbaafe1f3364db2721a3318e0cde66fa0c81a5e Mon Sep 17 00:00:00 2001 From: lat9nq Date: Wed, 13 Jul 2022 12:14:48 -0400 Subject: mini_dump: Cleanup and add comments Removes some unnecessary code. wip --- src/yuzu/mini_dump.cpp | 128 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 43 deletions(-) (limited to 'src/yuzu/mini_dump.cpp') diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp index ad8a4f607..e60456d9b 100644 --- a/src/yuzu/mini_dump.cpp +++ b/src/yuzu/mini_dump.cpp @@ -1,8 +1,8 @@ #include +#include #include #include #include -#include "common/logging/log.h" #include "yuzu/mini_dump.h" #include "yuzu/startup_checks.h" @@ -11,8 +11,6 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, EXCEPTION_POINTERS* pep) { - LOG_INFO(Core, "called"); - char file_name[255]; const std::time_t the_time = std::time(nullptr); std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); @@ -29,16 +27,50 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_ dump_type, (pep != 0) ? info : 0, 0, 0); if (!write_dump_status) { - LOG_ERROR(Core, "MiniDumpWriteDump failed. Error: {}", GetLastError()); + std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); } else { - LOG_INFO(Core, "Minidump created."); + std::fprintf(stderr, "MiniDump created: %s\n", file_name); } // Close the file CloseHandle(file_handle); } else { - LOG_ERROR(Core, "CreateFile failed. Error: {}", GetLastError()); + std::fprintf(stderr, "CreateFile failed. Error: %d\n", GetLastError()); + } +} + +void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { + EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; + + HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); + if (thread_handle == nullptr) { + std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); + } + + // Get child process context + CONTEXT context; + std::memset(&context, 0, sizeof(context)); + context.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(thread_handle, &context)) { + std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); + return; + } + + // Create exception pointers for minidump + EXCEPTION_POINTERS ep; + ep.ExceptionRecord = &record; + ep.ContextRecord = &context; + + MINIDUMP_EXCEPTION_INFORMATION info; + info.ThreadId = deb_ev.dwThreadId; + info.ExceptionPointers = &ep; + info.ClientPointers = false; + + CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); + + if (CloseHandle(thread_handle) == 0) { + std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", GetLastError()); } } @@ -68,6 +100,8 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { void DebugDebuggee(PROCESS_INFORMATION& pi) { DEBUG_EVENT deb_ev; + const std::time_t start_time = std::time(nullptr); + //~ bool seen_nonzero_thread_exit = false; while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); @@ -87,49 +121,57 @@ void DebugDebuggee(PROCESS_INFORMATION& pi) { case UNLOAD_DLL_DEBUG_EVENT: ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); break; - case EXCEPTION_DEBUG_EVENT: + //~ case EXIT_THREAD_DEBUG_EVENT: { + //~ const DWORD& exit_code = deb_ev.u.ExitThread.dwExitCode; + + //~ // Generate a crash dump on the first abnormal thread exit. + //~ // We don't want to generate on every abnormal thread exit since ALL the other + // threads ~ // in the application will follow by exiting with the same code. ~ if + //(!seen_nonzero_thread_exit && exit_code != 0) { ~ seen_nonzero_thread_exit = true; ~ + // std::fprintf(stderr, ~ "Creating MiniDump on first non-zero thread exit: code + // 0x%08x\n", ~ exit_code); + //~ DumpFromDebugEvent(deb_ev, pi); + //~ } + //~ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); + //~ break; + //~ } + case EXCEPTION_DEBUG_EVENT: { EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - - std::fprintf(stderr, "ExceptionCode: 0x%08x %s\n", record.ExceptionCode, - ExceptionName(record.ExceptionCode)); - if (!deb_ev.u.Exception.dwFirstChance) { - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, deb_ev.dwThreadId); - if (thread_handle == nullptr) { - std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); - } - if (SuspendThread(thread_handle) == (DWORD)-1) { - std::fprintf(stderr, "SuspendThread failed (%d)\n", GetLastError()); - } - - CONTEXT context; - std::memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_ALL; - if (!GetThreadContext(thread_handle, &context)) { - std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); - break; - } - - EXCEPTION_POINTERS ep; - ep.ExceptionRecord = &record; - ep.ContextRecord = &context; - - MINIDUMP_EXCEPTION_INFORMATION info; - info.ThreadId = deb_ev.dwThreadId; - info.ExceptionPointers = &ep; - info.ClientPointers = false; - - CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); - - std::fprintf(stderr, "previous thread suspend count: %d\n", - ResumeThread(thread_handle)); - if (CloseHandle(thread_handle) == 0) { - std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", - GetLastError()); + const std::time_t now = std::time(nullptr); + const std::time_t delta = now - start_time; + + if (ExceptionName(record.ExceptionCode) == nullptr) { + int record_count = 0; + EXCEPTION_RECORD* next_record = &deb_ev.u.Exception.ExceptionRecord; + while (next_record != nullptr) { + std::fprintf(stderr, + "[%d] code(%d): 0x%08x\n\tflags: %08x %s\n\taddress: " + "0x%08x\n\tparameters: %d\n", + delta, record_count, next_record->ExceptionCode, + next_record->ExceptionFlags, + next_record->ExceptionFlags == EXCEPTION_NONCONTINUABLE + ? "noncontinuable" + : "", + next_record->ExceptionAddress, next_record->NumberParameters); + for (int i = 0; i < static_cast(next_record->NumberParameters); i++) { + std::fprintf(stderr, "\t\t%0d: 0x%08x\n", i, + next_record->ExceptionInformation[i]); + } + + record_count++; + next_record = next_record->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); + } ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); break; } + } } } -- cgit v1.2.3 From e339ec0e00905821dbb4fee8e45a2514555f5b0e Mon Sep 17 00:00:00 2001 From: lat9nq Date: Mon, 18 Jul 2022 21:36:26 -0400 Subject: mini_dump: Check for debugger before spawning a child mini_dump: Clean up mini_dump: Fix MSVC error mini_dump: Silence MSVC warning C4700 Zero initialize deb_ev. mini_dump: Add license info --- src/yuzu/mini_dump.cpp | 97 ++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 63 deletions(-) (limited to 'src/yuzu/mini_dump.cpp') diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp index e60456d9b..b25067c10 100644 --- a/src/yuzu/mini_dump.cpp +++ b/src/yuzu/mini_dump.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include #include @@ -16,28 +19,28 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_ std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); // Open the file - HANDLE file_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - if ((file_handle != nullptr) && (file_handle != INVALID_HANDLE_VALUE)) { - // Create the minidump - const MINIDUMP_TYPE dump_type = MiniDumpNormal; + HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, - dump_type, (pep != 0) ? info : 0, 0, 0); + if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { + std::fprintf(stderr, "CreateFileA failed. Error: %d\n", GetLastError()); + return; + } - if (!write_dump_status) { - std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); - } else { - std::fprintf(stderr, "MiniDump created: %s\n", file_name); - } + // Create the minidump + const MINIDUMP_TYPE dump_type = MiniDumpNormal; - // Close the file - CloseHandle(file_handle); + const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, + dump_type, (pep != 0) ? info : 0, 0, 0); + if (write_dump_status) { + std::fprintf(stderr, "MiniDump created: %s\n", file_name); } else { - std::fprintf(stderr, "CreateFile failed. Error: %d\n", GetLastError()); + std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); } + + // Close the file + CloseHandle(file_handle); } void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { @@ -77,13 +80,13 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { std::memset(&pi, 0, sizeof(pi)); - if (!SpawnChild(arg0, &pi, 0)) { - std::fprintf(stderr, "warning: continuing without crash dumps\n"); + // Don't debug if we are already being debugged + if (IsDebuggerPresent()) { return false; } - // Don't debug if we are already being debugged - if (IsDebuggerPresent()) { + if (!SpawnChild(arg0, &pi, 0)) { + std::fprintf(stderr, "warning: continuing without crash dumps\n"); return false; } @@ -100,8 +103,7 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { void DebugDebuggee(PROCESS_INFORMATION& pi) { DEBUG_EVENT deb_ev; - const std::time_t start_time = std::time(nullptr); - //~ bool seen_nonzero_thread_exit = false; + std::memset(&deb_ev, 0, sizeof(deb_ev)); while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); @@ -119,59 +121,28 @@ void DebugDebuggee(PROCESS_INFORMATION& pi) { 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 EXIT_THREAD_DEBUG_EVENT: { - //~ const DWORD& exit_code = deb_ev.u.ExitThread.dwExitCode; - - //~ // Generate a crash dump on the first abnormal thread exit. - //~ // We don't want to generate on every abnormal thread exit since ALL the other - // threads ~ // in the application will follow by exiting with the same code. ~ if - //(!seen_nonzero_thread_exit && exit_code != 0) { ~ seen_nonzero_thread_exit = true; ~ - // std::fprintf(stderr, ~ "Creating MiniDump on first non-zero thread exit: code - // 0x%08x\n", ~ exit_code); - //~ DumpFromDebugEvent(deb_ev, pi); - //~ } - //~ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); - //~ break; - //~ } - case EXCEPTION_DEBUG_EVENT: { + case EXCEPTION_DEBUG_EVENT: EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - const std::time_t now = std::time(nullptr); - const std::time_t delta = now - start_time; - - if (ExceptionName(record.ExceptionCode) == nullptr) { - int record_count = 0; - EXCEPTION_RECORD* next_record = &deb_ev.u.Exception.ExceptionRecord; - while (next_record != nullptr) { - std::fprintf(stderr, - "[%d] code(%d): 0x%08x\n\tflags: %08x %s\n\taddress: " - "0x%08x\n\tparameters: %d\n", - delta, record_count, next_record->ExceptionCode, - next_record->ExceptionFlags, - next_record->ExceptionFlags == EXCEPTION_NONCONTINUABLE - ? "noncontinuable" - : "", - next_record->ExceptionAddress, next_record->NumberParameters); - for (int i = 0; i < static_cast(next_record->NumberParameters); i++) { - std::fprintf(stderr, "\t\t%0d: 0x%08x\n", i, - next_record->ExceptionInformation[i]); - } - - record_count++; - next_record = next_record->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; } - } } } -- cgit v1.2.3 From 12f7d42d32511955ee27875d42b6e8e3cda9e523 Mon Sep 17 00:00:00 2001 From: lat9nq Date: Sat, 30 Jul 2022 10:23:14 -0400 Subject: 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 mini_dump: Use a namespace Co-authored-by: Lioncash --- src/yuzu/mini_dump.cpp | 122 +++++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 59 deletions(-) (limited to 'src/yuzu/mini_dump.cpp') diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp index b25067c10..a34dc6a9c 100644 --- a/src/yuzu/mini_dump.cpp +++ b/src/yuzu/mini_dump.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "yuzu/mini_dump.h" #include "yuzu/startup_checks.h" @@ -12,6 +13,8 @@ // dbghelp.h must be included after windows.h #include +namespace MiniDump { + void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, EXCEPTION_POINTERS* pep) { char file_name[255]; @@ -23,7 +26,7 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 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; } @@ -34,9 +37,9 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_ dump_type, (pep != 0) ? info : 0, 0, 0); if (write_dump_status) { - std::fprintf(stderr, "MiniDump created: %s\n", file_name); + fmt::print(stderr, "MiniDump created: {}", file_name); } else { - std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); + fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError()); } // 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); if (thread_handle == nullptr) { - std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); + fmt::print(stderr, "OpenThread failed ({})", GetLastError()); + return; } // Get child process context - CONTEXT context; - std::memset(&context, 0, sizeof(context)); + CONTEXT context = {}; context.ContextFlags = CONTEXT_ALL; if (!GetThreadContext(thread_handle, &context)) { - std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); + fmt::print(stderr, "GetThreadContext failed ({})", GetLastError()); return; } @@ -73,7 +76,7 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); 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)) { - std::fprintf(stderr, "warning: continuing without crash dumps\n"); + fmt::print(stderr, "warning: continuing without crash dumps"); return false; } const bool can_debug = DebugActiveProcess(pi.dwProcessId); if (!can_debug) { - std::fprintf(stderr, - "warning: DebugActiveProcess failed (%d), continuing without crash dumps\n", - GetLastError()); + fmt::print(stderr, + "warning: DebugActiveProcess failed ({}), continuing without crash dumps", + GetLastError()); return false; } return true; } -void DebugDebuggee(PROCESS_INFORMATION& pi) { - 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) { +static const char* ExceptionName(DWORD exception) { switch (exception) { case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; @@ -193,6 +151,52 @@ const char* ExceptionName(DWORD exception) { case EXCEPTION_INVALID_HANDLE: return "EXCEPTION_INVALID_HANDLE"; 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 -- cgit v1.2.3