diff options
| author | 2022-07-13 12:14:48 -0400 | |
|---|---|---|
| committer | 2022-09-04 21:36:05 -0400 | |
| commit | 3dbaafe1f3364db2721a3318e0cde66fa0c81a5e (patch) | |
| tree | a5384cb6a2566728ba8daa95817dafe68c6fd5e8 | |
| parent | yuzu: Use a debugger to generate minidumps (diff) | |
| download | yuzu-3dbaafe1f3364db2721a3318e0cde66fa0c81a5e.tar.gz yuzu-3dbaafe1f3364db2721a3318e0cde66fa0c81a5e.tar.xz yuzu-3dbaafe1f3364db2721a3318e0cde66fa0c81a5e.zip | |
mini_dump: Cleanup and add comments
Removes some unnecessary code.
wip
| -rw-r--r-- | src/yuzu/main.cpp | 1 | ||||
| -rw-r--r-- | src/yuzu/mini_dump.cpp | 128 | ||||
| -rw-r--r-- | src/yuzu/mini_dump.h | 1 |
3 files changed, 87 insertions, 43 deletions
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ca3f4da70..ff59c64c3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -4097,6 +4097,7 @@ int main(int argc, char* argv[]) { | |||
| 4097 | #ifdef YUZU_DBGHELP | 4097 | #ifdef YUZU_DBGHELP |
| 4098 | PROCESS_INFORMATION pi; | 4098 | PROCESS_INFORMATION pi; |
| 4099 | if (!is_child && Settings::values.create_crash_dumps.GetValue() && SpawnDebuggee(argv[0], pi)) { | 4099 | if (!is_child && Settings::values.create_crash_dumps.GetValue() && SpawnDebuggee(argv[0], pi)) { |
| 4100 | // Delete the config object so that it doesn't save when the program exits | ||
| 4100 | config.reset(nullptr); | 4101 | config.reset(nullptr); |
| 4101 | DebugDebuggee(pi); | 4102 | DebugDebuggee(pi); |
| 4102 | return 0; | 4103 | return 0; |
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 @@ | |||
| 1 | #include <cstdio> | 1 | #include <cstdio> |
| 2 | #include <cstring> | ||
| 2 | #include <ctime> | 3 | #include <ctime> |
| 3 | #include <filesystem> | 4 | #include <filesystem> |
| 4 | #include <windows.h> | 5 | #include <windows.h> |
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "yuzu/mini_dump.h" | 6 | #include "yuzu/mini_dump.h" |
| 7 | #include "yuzu/startup_checks.h" | 7 | #include "yuzu/startup_checks.h" |
| 8 | 8 | ||
| @@ -11,8 +11,6 @@ | |||
| 11 | 11 | ||
| 12 | void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, | 12 | void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, |
| 13 | EXCEPTION_POINTERS* pep) { | 13 | EXCEPTION_POINTERS* pep) { |
| 14 | LOG_INFO(Core, "called"); | ||
| 15 | |||
| 16 | char file_name[255]; | 14 | char file_name[255]; |
| 17 | const std::time_t the_time = std::time(nullptr); | 15 | const std::time_t the_time = std::time(nullptr); |
| 18 | std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); | 16 | 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_ | |||
| 29 | dump_type, (pep != 0) ? info : 0, 0, 0); | 27 | dump_type, (pep != 0) ? info : 0, 0, 0); |
| 30 | 28 | ||
| 31 | if (!write_dump_status) { | 29 | if (!write_dump_status) { |
| 32 | LOG_ERROR(Core, "MiniDumpWriteDump failed. Error: {}", GetLastError()); | 30 | std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); |
| 33 | } else { | 31 | } else { |
| 34 | LOG_INFO(Core, "Minidump created."); | 32 | std::fprintf(stderr, "MiniDump created: %s\n", file_name); |
| 35 | } | 33 | } |
| 36 | 34 | ||
| 37 | // Close the file | 35 | // Close the file |
| 38 | CloseHandle(file_handle); | 36 | CloseHandle(file_handle); |
| 39 | 37 | ||
| 40 | } else { | 38 | } else { |
| 41 | LOG_ERROR(Core, "CreateFile failed. Error: {}", GetLastError()); | 39 | std::fprintf(stderr, "CreateFile failed. Error: %d\n", GetLastError()); |
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { | ||
| 44 | EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; | ||
| 45 | |||
| 46 | HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); | ||
| 47 | if (thread_handle == nullptr) { | ||
| 48 | std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); | ||
| 49 | } | ||
| 50 | |||
| 51 | // Get child process context | ||
| 52 | CONTEXT context; | ||
| 53 | std::memset(&context, 0, sizeof(context)); | ||
| 54 | context.ContextFlags = CONTEXT_ALL; | ||
| 55 | if (!GetThreadContext(thread_handle, &context)) { | ||
| 56 | std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | // Create exception pointers for minidump | ||
| 61 | EXCEPTION_POINTERS ep; | ||
| 62 | ep.ExceptionRecord = &record; | ||
| 63 | ep.ContextRecord = &context; | ||
| 64 | |||
| 65 | MINIDUMP_EXCEPTION_INFORMATION info; | ||
| 66 | info.ThreadId = deb_ev.dwThreadId; | ||
| 67 | info.ExceptionPointers = &ep; | ||
| 68 | info.ClientPointers = false; | ||
| 69 | |||
| 70 | CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); | ||
| 71 | |||
| 72 | if (CloseHandle(thread_handle) == 0) { | ||
| 73 | std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", GetLastError()); | ||
| 42 | } | 74 | } |
| 43 | } | 75 | } |
| 44 | 76 | ||
| @@ -68,6 +100,8 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { | |||
| 68 | 100 | ||
| 69 | void DebugDebuggee(PROCESS_INFORMATION& pi) { | 101 | void DebugDebuggee(PROCESS_INFORMATION& pi) { |
| 70 | DEBUG_EVENT deb_ev; | 102 | DEBUG_EVENT deb_ev; |
| 103 | const std::time_t start_time = std::time(nullptr); | ||
| 104 | //~ bool seen_nonzero_thread_exit = false; | ||
| 71 | 105 | ||
| 72 | while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { | 106 | while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { |
| 73 | const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); | 107 | const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); |
| @@ -87,49 +121,57 @@ void DebugDebuggee(PROCESS_INFORMATION& pi) { | |||
| 87 | case UNLOAD_DLL_DEBUG_EVENT: | 121 | case UNLOAD_DLL_DEBUG_EVENT: |
| 88 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); | 122 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); |
| 89 | break; | 123 | break; |
| 90 | case EXCEPTION_DEBUG_EVENT: | 124 | //~ case EXIT_THREAD_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: { | ||
| 91 | EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; | 139 | EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; |
| 92 | 140 | const std::time_t now = std::time(nullptr); | |
| 93 | std::fprintf(stderr, "ExceptionCode: 0x%08x %s\n", record.ExceptionCode, | 141 | const std::time_t delta = now - start_time; |
| 94 | ExceptionName(record.ExceptionCode)); | 142 | |
| 95 | if (!deb_ev.u.Exception.dwFirstChance) { | 143 | if (ExceptionName(record.ExceptionCode) == nullptr) { |
| 96 | HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, false, deb_ev.dwThreadId); | 144 | int record_count = 0; |
| 97 | if (thread_handle == nullptr) { | 145 | EXCEPTION_RECORD* next_record = &deb_ev.u.Exception.ExceptionRecord; |
| 98 | std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); | 146 | while (next_record != nullptr) { |
| 99 | } | 147 | std::fprintf(stderr, |
| 100 | if (SuspendThread(thread_handle) == (DWORD)-1) { | 148 | "[%d] code(%d): 0x%08x\n\tflags: %08x %s\n\taddress: " |
| 101 | std::fprintf(stderr, "SuspendThread failed (%d)\n", GetLastError()); | 149 | "0x%08x\n\tparameters: %d\n", |
| 102 | } | 150 | delta, record_count, next_record->ExceptionCode, |
| 103 | 151 | next_record->ExceptionFlags, | |
| 104 | CONTEXT context; | 152 | next_record->ExceptionFlags == EXCEPTION_NONCONTINUABLE |
| 105 | std::memset(&context, 0, sizeof(context)); | 153 | ? "noncontinuable" |
| 106 | context.ContextFlags = CONTEXT_ALL; | 154 | : "", |
| 107 | if (!GetThreadContext(thread_handle, &context)) { | 155 | next_record->ExceptionAddress, next_record->NumberParameters); |
| 108 | std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); | 156 | for (int i = 0; i < static_cast<int>(next_record->NumberParameters); i++) { |
| 109 | break; | 157 | std::fprintf(stderr, "\t\t%0d: 0x%08x\n", i, |
| 110 | } | 158 | next_record->ExceptionInformation[i]); |
| 111 | 159 | } | |
| 112 | EXCEPTION_POINTERS ep; | 160 | |
| 113 | ep.ExceptionRecord = &record; | 161 | record_count++; |
| 114 | ep.ContextRecord = &context; | 162 | next_record = next_record->ExceptionRecord; |
| 115 | |||
| 116 | MINIDUMP_EXCEPTION_INFORMATION info; | ||
| 117 | info.ThreadId = deb_ev.dwThreadId; | ||
| 118 | info.ExceptionPointers = &ep; | ||
| 119 | info.ClientPointers = false; | ||
| 120 | |||
| 121 | CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); | ||
| 122 | |||
| 123 | std::fprintf(stderr, "previous thread suspend count: %d\n", | ||
| 124 | ResumeThread(thread_handle)); | ||
| 125 | if (CloseHandle(thread_handle) == 0) { | ||
| 126 | std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", | ||
| 127 | GetLastError()); | ||
| 128 | } | 163 | } |
| 129 | } | 164 | } |
| 165 | // We want to generate a crash dump if we are seeing the same exception again. | ||
| 166 | if (!deb_ev.u.Exception.dwFirstChance) { | ||
| 167 | std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n", | ||
| 168 | record.ExceptionCode, ExceptionName(record.ExceptionCode)); | ||
| 169 | DumpFromDebugEvent(deb_ev, pi); | ||
| 170 | } | ||
| 130 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); | 171 | ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); |
| 131 | break; | 172 | break; |
| 132 | } | 173 | } |
| 174 | } | ||
| 133 | } | 175 | } |
| 134 | } | 176 | } |
| 135 | 177 | ||
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h index 59ba615e4..f6f5dc2c7 100644 --- a/src/yuzu/mini_dump.h +++ b/src/yuzu/mini_dump.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, | 7 | void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, |
| 8 | EXCEPTION_POINTERS* pep); | 8 | EXCEPTION_POINTERS* pep); |
| 9 | 9 | ||
| 10 | void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi); | ||
| 10 | bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); | 11 | bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); |
| 11 | void DebugDebuggee(PROCESS_INFORMATION& pi); | 12 | void DebugDebuggee(PROCESS_INFORMATION& pi); |
| 12 | const char* ExceptionName(DWORD exception); | 13 | const char* ExceptionName(DWORD exception); |