gate dump-section reads on is_mapped; trim doc comments
Without the page-state guard, read_bulk faulted on PROT_NONE pages of the 4 GiB host reservation. Per-page is_mapped check skips uncommitted pages, leaving the buffer's leading zero bytes in place. Total LOC budget after trim: 70.
This commit is contained in:
@@ -221,11 +221,7 @@ enum Commands {
|
|||||||
/// `XENIA_MEM_WATCH`. Example: `--mem-watch=0x828F40B4`.
|
/// `XENIA_MEM_WATCH`. Example: `--mem-watch=0x828F40B4`.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
mem_watch: Option<String>,
|
mem_watch: Option<String>,
|
||||||
/// Diagnostic. Dump a contiguous guest memory range to a file at
|
/// `--dump-section=BASE:LEN:PATH` end-of-run guest memory snapshot.
|
||||||
/// end-of-run. Format: `BASE:LEN:PATH` (BASE/LEN hex or decimal).
|
|
||||||
/// Example: `--dump-section=0x80000000:0x10000000:v80.bin`.
|
|
||||||
/// Read via `GuestMemory::read_bulk`; uncommitted pages read as
|
|
||||||
/// zero. Read-only; lockstep digest unaffected.
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
dump_section: Option<String>,
|
dump_section: Option<String>,
|
||||||
},
|
},
|
||||||
@@ -3265,17 +3261,24 @@ fn dump_thread_diagnostic(
|
|||||||
) {
|
) {
|
||||||
if let Some((base, len, ref path)) = kernel.dump_section {
|
if let Some((base, len, ref path)) = kernel.dump_section {
|
||||||
let mut buf = vec![0u8; len as usize];
|
let mut buf = vec![0u8; len as usize];
|
||||||
const CHUNK: usize = 4096;
|
const CHUNK: u32 = 4096;
|
||||||
let mut off = 0usize;
|
let mut off: u32 = 0;
|
||||||
while off < buf.len() {
|
let mut committed_pages = 0u32;
|
||||||
let take = CHUNK.min(buf.len() - off);
|
while off < len {
|
||||||
mem.read_bulk(base.wrapping_add(off as u32), &mut buf[off..off + take]);
|
let take = CHUNK.min(len - off);
|
||||||
|
let addr = base.wrapping_add(off);
|
||||||
|
if mem.is_mapped(addr) {
|
||||||
|
let s = off as usize;
|
||||||
|
let e = s + take as usize;
|
||||||
|
mem.read_bulk(addr, &mut buf[s..e]);
|
||||||
|
committed_pages += 1;
|
||||||
|
}
|
||||||
off += take;
|
off += take;
|
||||||
}
|
}
|
||||||
match std::fs::write(path, &buf) {
|
match std::fs::write(path, &buf) {
|
||||||
Ok(()) => eprintln!(
|
Ok(()) => eprintln!(
|
||||||
"dump-section: wrote {} bytes from {:#010x} to {}",
|
"dump-section: wrote {} bytes from {:#010x} ({} committed pages) to {}",
|
||||||
buf.len(), base, path.display(),
|
buf.len(), base, committed_pages, path.display(),
|
||||||
),
|
),
|
||||||
Err(e) => eprintln!(
|
Err(e) => eprintln!(
|
||||||
"dump-section: failed to write {}: {e}",
|
"dump-section: failed to write {}: {e}",
|
||||||
|
|||||||
@@ -237,10 +237,7 @@ pub struct KernelState {
|
|||||||
/// dump is performed by `dump_thread_diagnostic`, never during
|
/// dump is performed by `dump_thread_diagnostic`, never during
|
||||||
/// the hot interpreter loop, so lockstep determinism is unaffected.
|
/// the hot interpreter loop, so lockstep determinism is unaffected.
|
||||||
pub dump_addrs: Vec<u32>,
|
pub dump_addrs: Vec<u32>,
|
||||||
/// Diagnostic. Optional contiguous memory range to dump verbatim to a
|
/// `--dump-section=BASE:LEN:PATH` end-of-run snapshot, page-gated by `is_mapped`.
|
||||||
/// file at end-of-run: `(base, length, path)`. Populated from
|
|
||||||
/// `--dump-section=BASE:LEN:PATH`. Bulk read via `GuestMemory::read_bulk`;
|
|
||||||
/// host pages that were never committed read as zero (mmap-reserved).
|
|
||||||
pub dump_section: Option<(u32, u32, std::path::PathBuf)>,
|
pub dump_section: Option<(u32, u32, std::path::PathBuf)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,23 +1415,6 @@ mod tests {
|
|||||||
assert!(state.ctor_probe_pcs.contains(&0x8217_C850));
|
assert!(state.ctor_probe_pcs.contains(&0x8217_C850));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dump_section_field_round_trip_via_read_bulk() {
|
|
||||||
use xenia_memory::page_table::MemoryProtect;
|
|
||||||
let mem = GuestMemory::new().expect("memory init");
|
|
||||||
mem.alloc(0x4000_0000, 0x1000, MemoryProtect::READ | MemoryProtect::WRITE)
|
|
||||||
.expect("page");
|
|
||||||
let probe = [0xDEu8, 0xAD, 0xBE, 0xEF, 0x12, 0x34, 0x56, 0x78];
|
|
||||||
mem.write_bulk(0x4000_0040, &probe);
|
|
||||||
let mut state = KernelState::new();
|
|
||||||
let tmp = std::env::temp_dir().join("xenia_dump_section_round_trip.bin");
|
|
||||||
state.dump_section = Some((0x4000_0040, 8, tmp.clone()));
|
|
||||||
let (base, len, ref _path) = state.dump_section.as_ref().expect("set").clone();
|
|
||||||
let mut out = vec![0u8; len as usize];
|
|
||||||
mem.read_bulk(base, &mut out);
|
|
||||||
assert_eq!(out, probe);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_ascii_cstring_handles_termination_and_garbage() {
|
fn read_ascii_cstring_handles_termination_and_garbage() {
|
||||||
use xenia_memory::page_table::MemoryProtect;
|
use xenia_memory::page_table::MemoryProtect;
|
||||||
|
|||||||
Reference in New Issue
Block a user