Crash reports: the game's own fatal crashes come with the memory their stacks point at

Most crashes are caught by the game's own fatal handlers, which write a
dump of thread stacks only; the launcher could only copy it after the
game had exited. A crash on a freed or overwritten object then showed
the faulting registers but not the object or the list it came from.

- game_fatal_dump: the game's MiniDumpWriteDump import points at
  ReSkate. Before the game writes its dump, the crash reporter takes its
  own of the still-running game for the faulting thread; the game's dump
  follows as before. Reports say capture.source "game-fatal-handler".
- The reporter's dumps include the memory the stacks point at
  (MiniDumpWithIndirectlyReferencedMemory), a few MB more a report.
- The shared report says who asked for the dump (protocol version 4).
- Tests: a game-handler crash is captured with a heap block only its
  stack points at; ReSkate's own capture keeps it too.
This commit is contained in:
AyeZeeBB committed 2026-10-15 20:01:55 -05:00
1 parent ea3f081367
commit 3249f747dc
10 files changed
+169 -18

No files matched your search

+12 -3
View File
@@ -104,16 +104,25 @@ StartResult start(const std::filesystem::path& log_directory) noexcept {
return StartResult::ready;
} catch (...) { return StartResult::failed; }
}
void capture(EXCEPTION_POINTERS* exception) noexcept {
if (!exception || InterlockedCompareExchange(&capturing, 1, 0) != 0) return;
report->thread_id = GetCurrentThreadId();
namespace {
void capture_on(DWORD thread_id, EXCEPTION_POINTERS* exception, CaptureSource source) noexcept {
if (!exception || !thread_id || InterlockedCompareExchange(&capturing, 1, 0) != 0) return;
report->thread_id = thread_id;
report->exception = exception;
report->source = source;
MemoryBarrier();
if (SetEvent(handles[requested])) {
const HANDLE wait_for[]{handles[captured], helper};
WaitForMultipleObjects(2, wait_for, FALSE, capture_timeout_ms);
}
}
}
void capture(EXCEPTION_POINTERS* exception) noexcept {
capture_on(GetCurrentThreadId(), exception, CaptureSource::unhandled_exception);
}
void capture_game_fatal(DWORD thread_id, EXCEPTION_POINTERS* exception) noexcept {
capture_on(thread_id, exception, CaptureSource::game_fatal_handler);
}
void set_destination(std::wstring_view destination) noexcept {
if (auto* const shared = report) copy_cut(shared->destination, destination);
}