diff --git a/src/xenia/cpu/cpu_flags.cc b/src/xenia/cpu/cpu_flags.cc index 3ff067e15..467bc34b2 100644 --- a/src/xenia/cpu/cpu_flags.cc +++ b/src/xenia/cpu/cpu_flags.cc @@ -57,3 +57,9 @@ DEFINE_bool(break_condition_truncate, true, "truncate value to 32-bits", "CPU"); DEFINE_bool(break_on_debugbreak, true, "int3 on JITed __debugbreak requests.", "CPU"); + +DEFINE_string(memory_dump_path, "", + "If non-empty, dump guest memory (heaps v0/v40/v80/v90/phys) to " + "this path on first XamNotifyCreateListener call. Format = " + "Memory::Save() raw (no signature/header).", + "CPU"); diff --git a/src/xenia/cpu/cpu_flags.h b/src/xenia/cpu/cpu_flags.h index 38c4f98ba..02f15d5b2 100644 --- a/src/xenia/cpu/cpu_flags.h +++ b/src/xenia/cpu/cpu_flags.h @@ -35,4 +35,6 @@ DECLARE_bool(break_condition_truncate); DECLARE_bool(break_on_debugbreak); +DECLARE_string(memory_dump_path); + #endif // XENIA_CPU_CPU_FLAGS_H_ diff --git a/src/xenia/kernel/xam/xam_notify.cc b/src/xenia/kernel/xam/xam_notify.cc index 2f88d5e7d..006c7e5d9 100644 --- a/src/xenia/kernel/xam/xam_notify.cc +++ b/src/xenia/kernel/xam/xam_notify.cc @@ -15,6 +15,15 @@ #include "xenia/kernel/xthread.h" #include "xenia/xbox.h" +#include +#include "xenia/base/byte_stream.h" +#include "xenia/base/filesystem.h" +#include "xenia/base/logging.h" +#include "xenia/base/mapped_memory.h" +#include "xenia/cpu/cpu_flags.h" +#include "xenia/emulator.h" +#include "xenia/memory.h" + namespace xe { namespace kernel { namespace xam { @@ -42,7 +51,33 @@ dword_result_t XamNotifyCreateListener_entry(qword_t mask, auto thread = kernel::XThread::GetCurrentThread(); auto ctx = thread->thread_state()->context(); auto type = xboxkrnl::xeKeGetCurrentProcessType(ctx); - return xeXamNotifyCreateListener(mask, type == 2, max_version); + uint32_t result = xeXamNotifyCreateListener(mask, type == 2, max_version); + + // AUDIT-023: dump guest memory on first XamNotifyCreateListener call. + static std::atomic dumped{false}; + bool expected = false; + if (!cvars::memory_dump_path.empty() && + dumped.compare_exchange_strong(expected, true)) { + std::filesystem::path path(cvars::memory_dump_path); + XELOGI("AUDIT-023: dumping guest memory to {} (mask={:#x})", + cvars::memory_dump_path, uint64_t(mask)); + // Pre-size the file: PosixMappedMemory mmap's the existing file size. + constexpr size_t kReserve = size_t(2) * 1024 * 1024 * 1024; + filesystem::CreateEmptyFile(path); + std::filesystem::resize_file(path, kReserve); + auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite, 0, + kReserve); + if (map) { + ByteStream stream(map->data(), map->size()); + kernel_state()->emulator()->memory()->Save(&stream); + map->Close(stream.offset()); + XELOGI("AUDIT-023: wrote {} bytes", stream.offset()); + } else { + XELOGE("AUDIT-023: failed to open dump path"); + } + } + + return result; } DECLARE_XAM_EXPORT1(XamNotifyCreateListener, kNone, kImplemented);