diff options
| author | 2022-07-18 21:36:26 -0400 | |
|---|---|---|
| committer | 2022-09-04 21:36:05 -0400 | |
| commit | e339ec0e00905821dbb4fee8e45a2514555f5b0e (patch) | |
| tree | cd902de955922083625bd0ae49a367f75ba5a9a3 | |
| parent | mini_dump: Cleanup and add comments (diff) | |
| download | yuzu-e339ec0e00905821dbb4fee8e45a2514555f5b0e.tar.gz yuzu-e339ec0e00905821dbb4fee8e45a2514555f5b0e.tar.xz yuzu-e339ec0e00905821dbb4fee8e45a2514555f5b0e.zip | |
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
| -rw-r--r-- | src/yuzu/mini_dump.cpp | 97 | ||||
| -rw-r--r-- | src/yuzu/mini_dump.h | 3 |
2 files changed, 37 insertions, 63 deletions
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 1 | #include <cstdio> | 4 | #include <cstdio> |
| 2 | #include <cstring> | 5 | #include <cstring> |
| 3 | #include <ctime> | 6 | #include <ctime> |
| @@ -16,28 +19,28 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_ | |||
| 16 | std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); | 19 | std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); |
| 17 | 20 | ||
| 18 | // Open the file | 21 | // Open the file |
| 19 | HANDLE file_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, | 22 | HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, |
| 20 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); | 23 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); |
| 21 | |||
| 22 | if ((file_handle != nullptr) && (file_handle != INVALID_HANDLE_VALUE)) { | ||
| 23 | // Create the minidump | ||
| 24 | const MINIDUMP_TYPE dump_type = MiniDumpNormal; | ||
| 25 | 24 | ||
| 26 | const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, | 25 | if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { |
| 27 | dump_type, (pep != 0) ? info : 0, 0, 0); | 26 | std::fprintf(stderr, "CreateFileA failed. Error: %d\n", GetLastError()); |
| 27 | return; | ||
| 28 | } | ||
| 28 | 29 | ||
| 29 | if (!write_dump_status) { | 30 | // Create the minidump |
| 30 | std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); | 31 | const MINIDUMP_TYPE dump_type = MiniDumpNormal; |
| 31 | } else { | ||
| 32 | std::fprintf(stderr, "MiniDump created: %s\n", file_name); | ||
| 33 | } | ||
| 34 | 32 | ||
| 35 | // Close the file | 33 | const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, |
| 36 | CloseHandle(file_handle); | 34 | dump_type, (pep != 0) ? info : 0, 0, 0); |
| 37 | 35 | ||
| 36 | if (write_dump_status) { | ||
| 37 | std::fprintf(stderr, "MiniDump created: %s\n", file_name); | ||
| 38 | } else { | 38 | } else { |
| 39 | std::fprintf(stderr, "CreateFile failed. Error: %d\n", GetLastError()); | 39 | std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); |
| 40 | } | 40 | } |
| 41 | |||
| 42 | // Close the file | ||
| 43 | CloseHandle(file_handle); | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { | 46 | void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { |
| @@ -77,13 +80,13 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { | |||
| 77 | bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { | 80 | bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { |
| 78 | std::memset(&pi, 0, sizeof(pi)); | 81 | std::memset(&pi, 0, sizeof(pi)); |
| 79 | 82 | ||
| 80 | if (!SpawnChild(arg0, &pi, 0)) { | 83 | // Don't debug if we are already being debugged |
| 81 | std::fprintf(stderr, "warning: continuing without crash dumps\n"); | 84 | if (IsDebuggerPresent()) { |
| 82 | return false; | 85 | return false; |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | // Don't debug if we are already being debugged | 88 | if (!SpawnChild(arg0, &pi, 0)) { |
| 86 | if (IsDebuggerPresent()) { | 89 | std::fprintf(stderr, "warning: continuing without crash dumps\n"); |
| 87 | return false; | 90 | return false; |
| 88 | } | 91 | } |
| 89 | 92 | ||
| @@ -100,8 +103,7 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { | |||
| 100 | 103 | ||
| 101 | void DebugDebuggee(PROCESS_INFORMATION& pi) { | 104 | void DebugDebuggee(PROCESS_INFORMATION& pi) { |
| 102 | DEBUG_EVENT deb_ev; | 105 | DEBUG_EVENT deb_ev; |
| 103 | const std::time_t start_time = std::time(nullptr); | 106 | std::memset(&deb_ev, 0, sizeof(deb_ev)); |
| 104 | //~ bool seen_nonzero_thread_exit = false; | ||
| 105 | 107 | ||
| 106 | while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { | 108 | while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { |
| 107 | const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); | 109 | const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); |
| @@ -119,59 +121,28 @@ void DebugDebuggee(PROCESS_INFORMATION& pi) { | |||
| 119 | case LOAD_DLL_DEBUG_EVENT: | 121 | case LOAD_DLL_DEBUG_EVENT: |
| 120 | case RIP_EVENT: | 122 | case RIP_EVENT: |
| 121 | case UNLOAD_DLL_DEBUG_EVENT: | 123 | case UNLOAD_DLL_DEBUG_EVENT: |
| 124 | // Continue on all other debug events | ||
| 122 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); | 125 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); |
| 123 | break; | 126 | break; |
| 124 | //~ case EXIT_THREAD_DEBUG_EVENT: { | 127 | case EXCEPTION_DEBUG_EVENT: |
| 125 | //~ const DWORD& exit_code = deb_ev.u.ExitThread.dwExitCode; | ||
| 126 | |||
| 127 | //~ // Generate a crash dump on the first abnormal thread exit. | ||
| 128 | //~ // We don't want to generate on every abnormal thread exit since ALL the other | ||
| 129 | // threads ~ // in the application will follow by exiting with the same code. ~ if | ||
| 130 | //(!seen_nonzero_thread_exit && exit_code != 0) { ~ seen_nonzero_thread_exit = true; ~ | ||
| 131 | // std::fprintf(stderr, ~ "Creating MiniDump on first non-zero thread exit: code | ||
| 132 | // 0x%08x\n", ~ exit_code); | ||
| 133 | //~ DumpFromDebugEvent(deb_ev, pi); | ||
| 134 | //~ } | ||
| 135 | //~ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); | ||
| 136 | //~ break; | ||
| 137 | //~ } | ||
| 138 | case EXCEPTION_DEBUG_EVENT: { | ||
| 139 | EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; | 128 | EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; |
| 140 | const std::time_t now = std::time(nullptr); | 129 | |
| 141 | const std::time_t delta = now - start_time; | ||
| 142 | |||
| 143 | if (ExceptionName(record.ExceptionCode) == nullptr) { | ||
| 144 | int record_count = 0; | ||
| 145 | EXCEPTION_RECORD* next_record = &deb_ev.u.Exception.ExceptionRecord; | ||
| 146 | while (next_record != nullptr) { | ||
| 147 | std::fprintf(stderr, | ||
| 148 | "[%d] code(%d): 0x%08x\n\tflags: %08x %s\n\taddress: " | ||
| 149 | "0x%08x\n\tparameters: %d\n", | ||
| 150 | delta, record_count, next_record->ExceptionCode, | ||
| 151 | next_record->ExceptionFlags, | ||
| 152 | next_record->ExceptionFlags == EXCEPTION_NONCONTINUABLE | ||
| 153 | ? "noncontinuable" | ||
| 154 | : "", | ||
| 155 | next_record->ExceptionAddress, next_record->NumberParameters); | ||
| 156 | for (int i = 0; i < static_cast<int>(next_record->NumberParameters); i++) { | ||
| 157 | std::fprintf(stderr, "\t\t%0d: 0x%08x\n", i, | ||
| 158 | next_record->ExceptionInformation[i]); | ||
| 159 | } | ||
| 160 | |||
| 161 | record_count++; | ||
| 162 | next_record = next_record->ExceptionRecord; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | // We want to generate a crash dump if we are seeing the same exception again. | 130 | // We want to generate a crash dump if we are seeing the same exception again. |
| 166 | if (!deb_ev.u.Exception.dwFirstChance) { | 131 | if (!deb_ev.u.Exception.dwFirstChance) { |
| 167 | std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n", | 132 | std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n", |
| 168 | record.ExceptionCode, ExceptionName(record.ExceptionCode)); | 133 | record.ExceptionCode, ExceptionName(record.ExceptionCode)); |
| 169 | DumpFromDebugEvent(deb_ev, pi); | 134 | DumpFromDebugEvent(deb_ev, pi); |
| 170 | } | 135 | } |
| 136 | |||
| 137 | // Continue without handling the exception. | ||
| 138 | // Lets the debuggee use its own exception handler. | ||
| 139 | // - If one does not exist, we will see the exception once more where we make a minidump | ||
| 140 | // for. Then when it reaches here again, yuzu will probably crash. | ||
| 141 | // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an | ||
| 142 | // infinite loop of exceptions. | ||
| 171 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); | 143 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); |
| 172 | break; | 144 | break; |
| 173 | } | 145 | } |
| 174 | } | ||
| 175 | } | 146 | } |
| 176 | } | 147 | } |
| 177 | 148 | ||
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h index f6f5dc2c7..2052e5248 100644 --- a/src/yuzu/mini_dump.h +++ b/src/yuzu/mini_dump.h | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 1 | #pragma once | 4 | #pragma once |
| 2 | 5 | ||
| 3 | #include <windows.h> | 6 | #include <windows.h> |