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
This commit is contained in:
parent
3dbaafe1f3
commit
e339ec0e00
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
@ -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));
|
std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
|
||||||
|
|
||||||
// Open the file
|
// Open the file
|
||||||
HANDLE file_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
|
HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
|
||||||
if ((file_handle != nullptr) && (file_handle != INVALID_HANDLE_VALUE)) {
|
if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
std::fprintf(stderr, "CreateFileA failed. Error: %d\n", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the minidump
|
// Create the minidump
|
||||||
const MINIDUMP_TYPE dump_type = MiniDumpNormal;
|
const MINIDUMP_TYPE dump_type = MiniDumpNormal;
|
||||||
|
|
||||||
const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
|
const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
|
||||||
dump_type, (pep != 0) ? info : 0, 0, 0);
|
dump_type, (pep != 0) ? info : 0, 0, 0);
|
||||||
|
|
||||||
if (!write_dump_status) {
|
if (write_dump_status) {
|
||||||
std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError());
|
|
||||||
} else {
|
|
||||||
std::fprintf(stderr, "MiniDump created: %s\n", file_name);
|
std::fprintf(stderr, "MiniDump created: %s\n", file_name);
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "MiniDumpWriteDump failed. Error: %d\n", GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the file
|
// Close the file
|
||||||
CloseHandle(file_handle);
|
CloseHandle(file_handle);
|
||||||
|
|
||||||
} else {
|
|
||||||
std::fprintf(stderr, "CreateFile failed. Error: %d\n", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
|
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) {
|
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
|
||||||
std::memset(&pi, 0, sizeof(pi));
|
std::memset(&pi, 0, sizeof(pi));
|
||||||
|
|
||||||
if (!SpawnChild(arg0, &pi, 0)) {
|
// Don't debug if we are already being debugged
|
||||||
std::fprintf(stderr, "warning: continuing without crash dumps\n");
|
if (IsDebuggerPresent()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't debug if we are already being debugged
|
if (!SpawnChild(arg0, &pi, 0)) {
|
||||||
if (IsDebuggerPresent()) {
|
std::fprintf(stderr, "warning: continuing without crash dumps\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +103,7 @@ bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
|
||||||
|
|
||||||
void DebugDebuggee(PROCESS_INFORMATION& pi) {
|
void DebugDebuggee(PROCESS_INFORMATION& pi) {
|
||||||
DEBUG_EVENT deb_ev;
|
DEBUG_EVENT deb_ev;
|
||||||
const std::time_t start_time = std::time(nullptr);
|
std::memset(&deb_ev, 0, sizeof(deb_ev));
|
||||||
//~ bool seen_nonzero_thread_exit = false;
|
|
||||||
|
|
||||||
while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
|
while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
|
||||||
const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
|
const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
|
||||||
|
@ -119,60 +121,29 @@ void DebugDebuggee(PROCESS_INFORMATION& pi) {
|
||||||
case LOAD_DLL_DEBUG_EVENT:
|
case LOAD_DLL_DEBUG_EVENT:
|
||||||
case RIP_EVENT:
|
case RIP_EVENT:
|
||||||
case UNLOAD_DLL_DEBUG_EVENT:
|
case UNLOAD_DLL_DEBUG_EVENT:
|
||||||
|
// Continue on all other debug events
|
||||||
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
|
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
|
||||||
break;
|
break;
|
||||||
//~ case EXIT_THREAD_DEBUG_EVENT: {
|
case EXCEPTION_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;
|
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<int>(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.
|
// We want to generate a crash dump if we are seeing the same exception again.
|
||||||
if (!deb_ev.u.Exception.dwFirstChance) {
|
if (!deb_ev.u.Exception.dwFirstChance) {
|
||||||
std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n",
|
std::fprintf(stderr, "Creating MiniDump on ExceptionCode: 0x%08x %s\n",
|
||||||
record.ExceptionCode, ExceptionName(record.ExceptionCode));
|
record.ExceptionCode, ExceptionName(record.ExceptionCode));
|
||||||
DumpFromDebugEvent(deb_ev, pi);
|
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);
|
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ExceptionName(DWORD exception) {
|
const char* ExceptionName(DWORD exception) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
Reference in New Issue