summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/main.cpp5
-rw-r--r--src/yuzu/mini_dump.cpp122
-rw-r--r--src/yuzu/mini_dump.h5
4 files changed, 71 insertions, 63 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index df0f64b83..29d506c47 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -214,7 +214,7 @@ if (WIN32 AND YUZU_CRASH_DUMPS)
214 mini_dump.h 214 mini_dump.h
215 ) 215 )
216 216
217 target_link_libraries(yuzu PUBLIC ${DBGHELP_LIBRARY}) 217 target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY})
218 target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) 218 target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
219endif() 219endif()
220 220
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ff59c64c3..bda9986e1 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -4096,10 +4096,11 @@ int main(int argc, char* argv[]) {
4096 4096
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() &&
4100 MiniDump::SpawnDebuggee(argv[0], pi)) {
4100 // Delete the config object so that it doesn't save when the program exits 4101 // Delete the config object so that it doesn't save when the program exits
4101 config.reset(nullptr); 4102 config.reset(nullptr);
4102 DebugDebuggee(pi); 4103 MiniDump::DebugDebuggee(pi);
4103 return 0; 4104 return 0;
4104 } 4105 }
4105#endif 4106#endif
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 @@
5#include <cstring> 5#include <cstring>
6#include <ctime> 6#include <ctime>
7#include <filesystem> 7#include <filesystem>
8#include <fmt/format.h>
8#include <windows.h> 9#include <windows.h>
9#include "yuzu/mini_dump.h" 10#include "yuzu/mini_dump.h"
10#include "yuzu/startup_checks.h" 11#include "yuzu/startup_checks.h"
@@ -12,6 +13,8 @@
12// dbghelp.h must be included after windows.h 13// dbghelp.h must be included after windows.h
13#include <dbghelp.h> 14#include <dbghelp.h>
14 15
16namespace MiniDump {
17
15void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, 18void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
16 EXCEPTION_POINTERS* pep) { 19 EXCEPTION_POINTERS* pep) {
17 char file_name[255]; 20 char file_name[255];
@@ -23,7 +26,7 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_
23 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 26 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
24 27
25 if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { 28 if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
26 std::fprintf(stderr, "CreateFileA failed. Error: %d\n", GetLastError()); 29 fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
27 return; 30 return;
28 } 31 }
29 32
@@ -34,9 +37,9 @@ void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_
34 dump_type, (pep != 0) ? info : 0, 0, 0); 37 dump_type, (pep != 0) ? info : 0, 0, 0);
35 38
36 if (write_dump_status) { 39 if (write_dump_status) {
37 std::fprintf(stderr, "MiniDump created: %s\n", file_name); 40 fmt::print(stderr, "MiniDump created: {}", file_name);
38 } else { 41 } else {
39 std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError()); 42 fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
40 } 43 }
41 44
42 // Close the file 45 // Close the file
@@ -48,15 +51,15 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
48 51
49 HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); 52 HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
50 if (thread_handle == nullptr) { 53 if (thread_handle == nullptr) {
51 std::fprintf(stderr, "OpenThread failed (%d)\n", GetLastError()); 54 fmt::print(stderr, "OpenThread failed ({})", GetLastError());
55 return;
52 } 56 }
53 57
54 // Get child process context 58 // Get child process context
55 CONTEXT context; 59 CONTEXT context = {};
56 std::memset(&context, 0, sizeof(context));
57 context.ContextFlags = CONTEXT_ALL; 60 context.ContextFlags = CONTEXT_ALL;
58 if (!GetThreadContext(thread_handle, &context)) { 61 if (!GetThreadContext(thread_handle, &context)) {
59 std::fprintf(stderr, "GetThreadContext failed (%d)\n", GetLastError()); 62 fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
60 return; 63 return;
61 } 64 }
62 65
@@ -73,7 +76,7 @@ void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
73 CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); 76 CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
74 77
75 if (CloseHandle(thread_handle) == 0) { 78 if (CloseHandle(thread_handle) == 0) {
76 std::fprintf(stderr, "error: CloseHandle(thread_handle) failed (%d)\n", GetLastError()); 79 fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
77 } 80 }
78} 81}
79 82
@@ -86,67 +89,22 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
86 } 89 }
87 90
88 if (!SpawnChild(arg0, &pi, 0)) { 91 if (!SpawnChild(arg0, &pi, 0)) {
89 std::fprintf(stderr, "warning: continuing without crash dumps\n"); 92 fmt::print(stderr, "warning: continuing without crash dumps");
90 return false; 93 return false;
91 } 94 }
92 95
93 const bool can_debug = DebugActiveProcess(pi.dwProcessId); 96 const bool can_debug = DebugActiveProcess(pi.dwProcessId);
94 if (!can_debug) { 97 if (!can_debug) {
95 std::fprintf(stderr, 98 fmt::print(stderr,
96 "warning: DebugActiveProcess failed (%d), continuing without crash dumps\n", 99 "warning: DebugActiveProcess failed ({}), continuing without crash dumps",
97 GetLastError()); 100 GetLastError());
98 return false; 101 return false;
99 } 102 }
100 103
101 return true; 104 return true;
102} 105}
103 106
104void DebugDebuggee(PROCESS_INFORMATION& pi) { 107static const char* ExceptionName(DWORD exception) {
105 DEBUG_EVENT deb_ev;
106 std::memset(&deb_ev, 0, sizeof(deb_ev));
107
108 while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
109 const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
110 if (!wait_success) {
111 std::fprintf(stderr, "error: WaitForDebugEvent failed (%d)\n", GetLastError());
112 return;
113 }
114
115 switch (deb_ev.dwDebugEventCode) {
116 case OUTPUT_DEBUG_STRING_EVENT:
117 case CREATE_PROCESS_DEBUG_EVENT:
118 case CREATE_THREAD_DEBUG_EVENT:
119 case EXIT_PROCESS_DEBUG_EVENT:
120 case EXIT_THREAD_DEBUG_EVENT:
121 case LOAD_DLL_DEBUG_EVENT:
122 case RIP_EVENT:
123 case UNLOAD_DLL_DEBUG_EVENT:
124 // Continue on all other debug events
125 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
126 break;
127 case EXCEPTION_DEBUG_EVENT:
128 EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
129
130 // We want to generate a crash dump if we are seeing the same exception again.
131 if (!deb_ev.u.Exception.dwFirstChance) {
132 std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n",
133 record.ExceptionCode, ExceptionName(record.ExceptionCode));
134 DumpFromDebugEvent(deb_ev, pi);
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.
143 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
144 break;
145 }
146 }
147}
148
149const char* ExceptionName(DWORD exception) {
150 switch (exception) { 108 switch (exception) {
151 case EXCEPTION_ACCESS_VIOLATION: 109 case EXCEPTION_ACCESS_VIOLATION:
152 return "EXCEPTION_ACCESS_VIOLATION"; 110 return "EXCEPTION_ACCESS_VIOLATION";
@@ -193,6 +151,52 @@ const char* ExceptionName(DWORD exception) {
193 case EXCEPTION_INVALID_HANDLE: 151 case EXCEPTION_INVALID_HANDLE:
194 return "EXCEPTION_INVALID_HANDLE"; 152 return "EXCEPTION_INVALID_HANDLE";
195 default: 153 default:
196 return nullptr; 154 return "unknown exception type";
197 } 155 }
198} 156}
157
158void DebugDebuggee(PROCESS_INFORMATION& pi) {
159 DEBUG_EVENT deb_ev = {};
160
161 while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
162 const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
163 if (!wait_success) {
164 fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
165 return;
166 }
167
168 switch (deb_ev.dwDebugEventCode) {
169 case OUTPUT_DEBUG_STRING_EVENT:
170 case CREATE_PROCESS_DEBUG_EVENT:
171 case CREATE_THREAD_DEBUG_EVENT:
172 case EXIT_PROCESS_DEBUG_EVENT:
173 case EXIT_THREAD_DEBUG_EVENT:
174 case LOAD_DLL_DEBUG_EVENT:
175 case RIP_EVENT:
176 case UNLOAD_DLL_DEBUG_EVENT:
177 // Continue on all other debug events
178 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
179 break;
180 case EXCEPTION_DEBUG_EVENT:
181 EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
182
183 // We want to generate a crash dump if we are seeing the same exception again.
184 if (!deb_ev.u.Exception.dwFirstChance) {
185 fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
186 record.ExceptionCode, ExceptionName(record.ExceptionCode));
187 DumpFromDebugEvent(deb_ev, pi);
188 }
189
190 // Continue without handling the exception.
191 // Lets the debuggee use its own exception handler.
192 // - If one does not exist, we will see the exception once more where we make a minidump
193 // for. Then when it reaches here again, yuzu will probably crash.
194 // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
195 // infinite loop of exceptions.
196 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
197 break;
198 }
199 }
200}
201
202} // namespace MiniDump
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h
index 2052e5248..d6b6cca84 100644
--- a/src/yuzu/mini_dump.h
+++ b/src/yuzu/mini_dump.h
@@ -7,10 +7,13 @@
7 7
8#include <dbghelp.h> 8#include <dbghelp.h>
9 9
10namespace MiniDump {
11
10void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, 12void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
11 EXCEPTION_POINTERS* pep); 13 EXCEPTION_POINTERS* pep);
12 14
13void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi); 15void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
14bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); 16bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
15void DebugDebuggee(PROCESS_INFORMATION& pi); 17void DebugDebuggee(PROCESS_INFORMATION& pi);
16const char* ExceptionName(DWORD exception); 18
19} // namespace MiniDump