summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/yuzu/main.cpp1
-rw-r--r--src/yuzu/mini_dump.cpp128
-rw-r--r--src/yuzu/mini_dump.h1
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
12void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, 12void 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
43void 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
69void DebugDebuggee(PROCESS_INFORMATION& pi) { 101void 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 @@
7void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, 7void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
8 EXCEPTION_POINTERS* pep); 8 EXCEPTION_POINTERS* pep);
9 9
10void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
10bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); 11bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
11void DebugDebuggee(PROCESS_INFORMATION& pi); 12void DebugDebuggee(PROCESS_INFORMATION& pi);
12const char* ExceptionName(DWORD exception); 13const char* ExceptionName(DWORD exception);