[Audit] --audit-r3-dump-bytes: dump N bytes at r3 when probe fires
AUDIT-059 round 15 — diagnostic. When `--audit-r3-dump-bytes=N` is set, every `--audit-pc-probe-hex` fire emits a paired `AUDIT-R3-DUMP` line with N bytes of guest memory from r3 as u32 lanes (4-byte aligned, cap 256B). Sized for the 80-byte stack-local struct at sub_82452DC0's `r31+96` (probe sub_8245B000 entry where r3 IS the struct ptr). Settable via `XENIA_AUDIT_R3_DUMP_BYTES` env. Read-only; lockstep digest unaffected (empty-set fast path in fire_audit_pc_probe_if_match). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -271,6 +271,15 @@ enum Commands {
|
|||||||
/// `--audit-mem-read-hex=828E1F08`.
|
/// `--audit-mem-read-hex=828E1F08`.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
audit_mem_read_hex: Option<String>,
|
audit_mem_read_hex: Option<String>,
|
||||||
|
/// AUDIT-052 — number of bytes (4-byte aligned, max 256) to
|
||||||
|
/// dump from `r3` on every `--audit-pc-probe-hex` fire. Emits a
|
||||||
|
/// paired `AUDIT-R3-DUMP` line with the u32 lanes. Designed for
|
||||||
|
/// the 80-byte stack-local struct at `sub_82452DC0` (`r31+96`)
|
||||||
|
/// when probing `sub_8245B000` entry — where `r3` IS the struct
|
||||||
|
/// pointer. Read-only; lockstep digest unaffected. Settable via
|
||||||
|
/// `XENIA_AUDIT_R3_DUMP_BYTES`. Example: `--audit-r3-dump-bytes=80`.
|
||||||
|
#[arg(long)]
|
||||||
|
audit_r3_dump_bytes: Option<u32>,
|
||||||
},
|
},
|
||||||
/// Browse XISO disc image contents
|
/// Browse XISO disc image contents
|
||||||
Browse {
|
Browse {
|
||||||
@@ -436,6 +445,7 @@ fn main() -> Result<()> {
|
|||||||
lr_trace_out,
|
lr_trace_out,
|
||||||
audit_pc_probe_hex,
|
audit_pc_probe_hex,
|
||||||
audit_mem_read_hex,
|
audit_mem_read_hex,
|
||||||
|
audit_r3_dump_bytes,
|
||||||
} => cmd_exec(
|
} => cmd_exec(
|
||||||
&path,
|
&path,
|
||||||
max_instructions,
|
max_instructions,
|
||||||
@@ -464,6 +474,7 @@ fn main() -> Result<()> {
|
|||||||
lr_trace_out.as_deref(),
|
lr_trace_out.as_deref(),
|
||||||
audit_pc_probe_hex.as_deref(),
|
audit_pc_probe_hex.as_deref(),
|
||||||
audit_mem_read_hex.as_deref(),
|
audit_mem_read_hex.as_deref(),
|
||||||
|
audit_r3_dump_bytes,
|
||||||
),
|
),
|
||||||
Commands::Browse { path } => cmd_browse(&path),
|
Commands::Browse { path } => cmd_browse(&path),
|
||||||
Commands::Info { path } => cmd_info(&path),
|
Commands::Info { path } => cmd_info(&path),
|
||||||
@@ -697,6 +708,7 @@ fn cmd_exec(
|
|||||||
lr_trace_out: Option<&str>,
|
lr_trace_out: Option<&str>,
|
||||||
audit_pc_probe_hex: Option<&str>,
|
audit_pc_probe_hex: Option<&str>,
|
||||||
audit_mem_read_hex: Option<&str>,
|
audit_mem_read_hex: Option<&str>,
|
||||||
|
audit_r3_dump_bytes: Option<u32>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
cmd_exec_inner(
|
cmd_exec_inner(
|
||||||
path,
|
path,
|
||||||
@@ -726,6 +738,7 @@ fn cmd_exec(
|
|||||||
lr_trace_out,
|
lr_trace_out,
|
||||||
audit_pc_probe_hex,
|
audit_pc_probe_hex,
|
||||||
audit_mem_read_hex,
|
audit_mem_read_hex,
|
||||||
|
audit_r3_dump_bytes,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
@@ -774,6 +787,7 @@ fn cmd_check(
|
|||||||
None, // lr_trace_out — same
|
None, // lr_trace_out — same
|
||||||
None, // audit_pc_probe_hex — diagnostic, never wanted on goldens
|
None, // audit_pc_probe_hex — diagnostic, never wanted on goldens
|
||||||
None, // audit_mem_read_hex — same
|
None, // audit_mem_read_hex — same
|
||||||
|
None, // audit_r3_dump_bytes — same
|
||||||
out,
|
out,
|
||||||
expect,
|
expect,
|
||||||
stable_digest,
|
stable_digest,
|
||||||
@@ -808,6 +822,7 @@ fn cmd_exec_inner(
|
|||||||
lr_trace_out: Option<&str>,
|
lr_trace_out: Option<&str>,
|
||||||
audit_pc_probe_hex: Option<&str>,
|
audit_pc_probe_hex: Option<&str>,
|
||||||
audit_mem_read_hex: Option<&str>,
|
audit_mem_read_hex: Option<&str>,
|
||||||
|
audit_r3_dump_bytes: Option<u32>,
|
||||||
digest_out: Option<&str>,
|
digest_out: Option<&str>,
|
||||||
digest_expect: Option<&str>,
|
digest_expect: Option<&str>,
|
||||||
stable_digest: bool,
|
stable_digest: bool,
|
||||||
@@ -1263,6 +1278,29 @@ fn cmd_exec_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AUDIT-052 — `--audit-r3-dump-bytes=80`. When set, every
|
||||||
|
// `--audit-pc-probe-hex` fire emits a paired `AUDIT-R3-DUMP` line
|
||||||
|
// with N bytes from `r3` (4-byte aligned, capped at 256). Sized for
|
||||||
|
// the 80-byte stack-local struct at `sub_82452DC0`'s `r31+96` —
|
||||||
|
// probe `sub_8245B000` entry where `r3 == parent's r31+96`.
|
||||||
|
let audit_r3_dump_combined: Option<u32> = match (
|
||||||
|
audit_r3_dump_bytes, std::env::var("XENIA_AUDIT_R3_DUMP_BYTES").ok(),
|
||||||
|
) {
|
||||||
|
(Some(n), _) => Some(n),
|
||||||
|
(None, Some(s)) if !s.is_empty() => Some(
|
||||||
|
s.parse::<u32>().map_err(|e| anyhow::anyhow!("--audit-r3-dump-bytes {s:?}: {e}"))?,
|
||||||
|
),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(n) = audit_r3_dump_combined {
|
||||||
|
if n > 0 {
|
||||||
|
kernel.audit_r3_dump_bytes = Some(n);
|
||||||
|
if !quiet {
|
||||||
|
tracing::info!("audit-r3-dump armed: {} bytes", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Diagnostic. Parse `--dump-addr=0x828F3D08,...` (or
|
// Diagnostic. Parse `--dump-addr=0x828F3D08,...` (or
|
||||||
// `XENIA_DUMP_ADDR=...`) into `kernel.dump_addrs`. The contents
|
// `XENIA_DUMP_ADDR=...`) into `kernel.dump_addrs`. The contents
|
||||||
// are dumped at end-of-run by `dump_thread_diagnostic`. Pure
|
// are dumped at end-of-run by `dump_thread_diagnostic`. Pure
|
||||||
|
|||||||
@@ -270,6 +270,15 @@ pub struct KernelState {
|
|||||||
/// unaffected. Settable via `--audit-mem-read-hex` /
|
/// unaffected. Settable via `--audit-mem-read-hex` /
|
||||||
/// `XENIA_AUDIT_MEM_READ`.
|
/// `XENIA_AUDIT_MEM_READ`.
|
||||||
pub audit_mem_read_addr: Option<u32>,
|
pub audit_mem_read_addr: Option<u32>,
|
||||||
|
/// AUDIT-052 — diagnostic. When set, each `AUDIT-PC-PROBE` fire
|
||||||
|
/// additionally emits an `AUDIT-R3-DUMP` line with N bytes of guest
|
||||||
|
/// memory dumped from `r3` as `u32` lanes (4-byte aligned only).
|
||||||
|
/// Sized for audit-051's 80-byte stack-local struct at `r31+96`
|
||||||
|
/// inside `sub_82452DC0` (probe `sub_8245B000` entry where
|
||||||
|
/// `r3 == parent's r31+96`). Read-only; lockstep digest unaffected.
|
||||||
|
/// Settable via `--audit-r3-dump-bytes` /
|
||||||
|
/// `XENIA_AUDIT_R3_DUMP_BYTES`.
|
||||||
|
pub audit_r3_dump_bytes: Option<u32>,
|
||||||
/// M12 — diagnostic. PCs at which to emit a structured JSONL record
|
/// M12 — diagnostic. PCs at which to emit a structured JSONL record
|
||||||
/// per fire, designed for diffing against xenia-canary's
|
/// per fire, designed for diffing against xenia-canary's
|
||||||
/// `--log_lr_on_pc` patch output. Each line carries
|
/// `--log_lr_on_pc` patch output. Each line carries
|
||||||
@@ -355,6 +364,7 @@ impl KernelState {
|
|||||||
branch_probe_pcs: std::collections::HashSet::new(),
|
branch_probe_pcs: std::collections::HashSet::new(),
|
||||||
audit_pc_probe_pcs: std::collections::HashSet::new(),
|
audit_pc_probe_pcs: std::collections::HashSet::new(),
|
||||||
audit_mem_read_addr: None,
|
audit_mem_read_addr: None,
|
||||||
|
audit_r3_dump_bytes: None,
|
||||||
lr_trace_pcs: std::collections::HashSet::new(),
|
lr_trace_pcs: std::collections::HashSet::new(),
|
||||||
lr_trace_writer: None,
|
lr_trace_writer: None,
|
||||||
dump_addrs: Vec::new(),
|
dump_addrs: Vec::new(),
|
||||||
@@ -886,6 +896,28 @@ impl KernelState {
|
|||||||
addr, val, vt, m0, m6, pc, tid, cycle,
|
addr, val, vt, m0, m6, pc, tid, cycle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// AUDIT-052 — dump N bytes of guest memory from r3 as u32 lanes
|
||||||
|
// when `audit_r3_dump_bytes` is set. Sized for the 80-byte
|
||||||
|
// stack-local struct at sub_82452DC0's `r31+96` (probe is
|
||||||
|
// sub_8245B000 entry where r3 IS the struct ptr). Output
|
||||||
|
// format: `AUDIT-R3-DUMP pc=… r3=… +0x00=… +0x04=… …`.
|
||||||
|
if let Some(n) = self.audit_r3_dump_bytes {
|
||||||
|
let n = n.min(256) & !3u32; // cap 256B, 4-byte align
|
||||||
|
let mut out = String::with_capacity(64 + (n as usize) * 16);
|
||||||
|
use std::fmt::Write as _;
|
||||||
|
let _ = write!(
|
||||||
|
&mut out,
|
||||||
|
"AUDIT-R3-DUMP pc={:#010x} tid={} cycle={} r3={:#010x}",
|
||||||
|
pc, tid, cycle, r3,
|
||||||
|
);
|
||||||
|
let mut off: u32 = 0;
|
||||||
|
while off < n {
|
||||||
|
let v = mem.read_u32(r3.wrapping_add(off));
|
||||||
|
let _ = write!(&mut out, " +0x{:02x}={:#010x}", off, v);
|
||||||
|
off = off.wrapping_add(4);
|
||||||
|
}
|
||||||
|
println!("{}", out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// M12 — diagnostic. If the live PC for HW slot `hw_id` is in
|
/// M12 — diagnostic. If the live PC for HW slot `hw_id` is in
|
||||||
|
|||||||
Reference in New Issue
Block a user