23 DebugActiveProcessStop(process_id);
29 const HMODULE kernel32 = GetModuleHandleA(
"kernel32.dll");
37 if (is_wow64_process2) {
38 uint16_t process_machine = IMAGE_FILE_MACHINE_UNKNOWN;
40 uint16_t native_machine = IMAGE_FILE_MACHINE_UNKNOWN;
41 if (is_wow64_process2(GetCurrentProcess(), &process_machine, &native_machine)) {
43 "Watchdog host machine detection: process=0x%X native=0x%X",
47 return native_machine == IMAGE_FILE_MACHINE_ARM64;
51 SYSTEM_INFO system_info = {0};
52 GetNativeSystemInfo(&system_info);
54 "Watchdog fallback architecture detection: native_arch=0x%X",
55 system_info.wProcessorArchitecture
58 return system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64;
62 HANDLE thread = OpenThread(THREAD_ALL_ACCESS,
FALSE, tid);
64 DTTR_ERROR(
"Failed to open crashing thread %lu", tid);
68 CONTEXT thread_context = {.ContextFlags = CONTEXT_ALL};
69 if (!GetThreadContext(thread, &thread_context)) {
71 DTTR_ERROR(
"Failed to read crashing thread %lu context", tid);
75 EXCEPTION_RECORD fake_record = {.ExceptionCode = exception_code};
76 EXCEPTION_POINTERS ptrs = {
77 .ExceptionRecord = &fake_record,
78 .ContextRecord = &thread_context,
85 sds summary = sdsempty();
87 summary = sdscatprintf(
89 "Game crashed (exception 0x%08lX). Crash dump written to %s.",
95 summary = sdscatprintf(
97 "Game crashed (exception 0x%08lX). Failed to write crash dump.",
104 sdsfree(stack_trace);
112 sdsfree(report_message);
120 "Skipping watchdog debugger because debugging is not available on this "
126 if (!DebugActiveProcess(child_info->dwProcessId)) {
128 "Could not attach debugger to child process; skipping early crash detection"
134 DTTR_LOG_DEBUG(
"Watchdog attached to PID %lu", child_info->dwProcessId);
141static bool is_sentinel(HANDLE process,
const OUTPUT_DEBUG_STRING_INFO *info) {
147 SIZE_T bytes_read = 0;
149 if (!ReadProcessMemory(
151 info->lpDebugStringData,
165 DTTR_LOG_DEBUG(
"Watchdog not attached; skipping early crash monitoring");
170 "Watching for early crash or ready sentinel (timeout=%dms)",
174 DEBUG_EVENT evt = {0};
176 bool saw_sentinel =
false;
177 bool saw_failure =
false;
179 while (remaining > 0) {
180 const DWORD start = GetTickCount();
182 if (!WaitForDebugEvent(&evt, remaining)) {
186 DWORD continue_status = DBG_CONTINUE;
189 switch (evt.dwDebugEventCode) {
190 case EXCEPTION_DEBUG_EVENT: {
191 const DWORD code = evt.u.Exception.ExceptionRecord.ExceptionCode;
193 if (evt.u.Exception.dwFirstChance) {
194 if (code != EXCEPTION_BREAKPOINT) {
195 continue_status = DBG_EXCEPTION_NOT_HANDLED;
202 child_info->hProcess,
203 child_info->dwProcessId,
212 case OUTPUT_DEBUG_STRING_EVENT:
213 if (
is_sentinel(child_info->hProcess, &evt.u.DebugString)) {
221 case CREATE_PROCESS_DEBUG_EVENT:
222 if (evt.u.CreateProcessInfo.hFile) {
223 CloseHandle(evt.u.CreateProcessInfo.hFile);
228 case LOAD_DLL_DEBUG_EVENT:
229 if (evt.u.LoadDll.hFile) {
230 CloseHandle(evt.u.LoadDll.hFile);
235 case EXIT_PROCESS_DEBUG_EVENT:
240 evt.u.ExitProcess.dwExitCode
249 ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, continue_status);
255 const DWORD elapsed = GetTickCount() - start;
256 remaining -= (elapsed < remaining) ? elapsed : remaining;
260 if (!saw_sentinel && !saw_failure) {
262 "Sidecar did not report entrypoint within %ds; aborting "
269 return saw_sentinel && !saw_failure;
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f BOOL
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f FALSE
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
void DTTR_CrashDump_LogAndTraceReport(const char *message)
sds DTTR_CrashDump_Write(HANDLE process, DWORD pid, DWORD tid, EXCEPTION_POINTERS *exception_info)
sds DTTR_CrashDump_FormatStackTrace(HANDLE process, HANDLE thread, const CONTEXT *context)
Formats a stack trace from a thread context. Caller frees the returned sds.
sds DTTR_CrashDump_AppendReportMessage(sds message, const char *stack_trace)
#define DTTR_REPORT_SUFFIX
#define DTTR_ERROR(error_message,...)
void DTTR_Errors_ShowMessage(const char *title, const char *message)
#define DTTR_LOG_DEBUG(...)
#define DTTR_LOG_WARN(...)
#define DTTR_LOG_INFO(...)
static bool is_sentinel(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *info)
void DTTR_Loader_WatchdogDetach(const PROCESS_INFORMATION *child_info)
static const char WATCHDOG_SENTINEL[]
BOOL(* is_wow64_process2_fn)(HANDLE, USHORT *, USHORT *)
static void detach_watchdog(DWORD process_id)
static bool watchdog_attached
static void write_child_dump(HANDLE process, DWORD pid, DWORD tid, DWORD exception_code)
bool DTTR_Loader_WatchdogWait(const PROCESS_INFORMATION *child_info)
void DTTR_Loader_WatchdogAttach(const PROCESS_INFORMATION *child_info)
static bool should_disable_watchdog()