Compare commits
3 Commits
iterate-4D
...
iterate-4E
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
869790bab9 | ||
|
|
19700736b8 | ||
|
|
48e166579b |
@@ -1031,7 +1031,15 @@ fn cmd_exec_inner(
|
||||
let v = v.trim().to_ascii_lowercase();
|
||||
v == "1" || v == "true" || v == "yes"
|
||||
});
|
||||
let parallel_active = parallel || parallel_via_env;
|
||||
// iterate-4E: native-threads mode is a superset of parallel behavior (it
|
||||
// delegates to the free-run executor and runs guest code on multiple host
|
||||
// threads concurrently). Folding it into `parallel_active` is what enables
|
||||
// the host-atomic ReservationTable below — MANDATORY under concurrency, else
|
||||
// lwarx/stwcx on non-primary slots silently can't observe cross-thread
|
||||
// stores (PPCBUG-108). Also flips `kernel.parallel_active` so the wall-clock
|
||||
// vsync/coordination paths engage like `--parallel`.
|
||||
let native_active = native_threads_enabled();
|
||||
let parallel_active = parallel || parallel_via_env || native_active;
|
||||
kernel.parallel_active = parallel_active;
|
||||
// AUDIT-032: default is `KernelState::xaudio_tick_enabled = true` now
|
||||
// that the dedicated worker eliminates HW-thread hijack regressions.
|
||||
@@ -1854,7 +1862,18 @@ fn cmd_exec_inner(
|
||||
let v = v.trim().to_ascii_lowercase();
|
||||
v == "1" || v == "true" || v == "yes"
|
||||
});
|
||||
let do_parallel = parallel || env_parallel;
|
||||
// iterate-4E: canary-model native-threads mode (host-thread-per-
|
||||
// guest-thread). Opt-in; reuses the parallel spawn substrate
|
||||
// (kernel in Arc<Mutex<>>, worker-thread dispatch). `run_execution_
|
||||
// parallel` routes it to `run_execution_native`. Flag-off leaves the
|
||||
// lockstep golden path byte-identical.
|
||||
let env_native = std::env::var("XENIA_NATIVE_THREADS")
|
||||
.ok()
|
||||
.is_some_and(|v| {
|
||||
let v = v.trim().to_ascii_lowercase();
|
||||
v == "1" || v == "true" || v == "yes"
|
||||
});
|
||||
let do_parallel = parallel || env_parallel || env_native;
|
||||
// Step 04 gate: --parallel runs N=6 workers that release
|
||||
// the kernel mutex around step_block, so the per-instruction
|
||||
// observation path (debugger hooks, DB writer, force-per-instr)
|
||||
@@ -4069,6 +4088,25 @@ fn run_execution_parallel(
|
||||
// the workload exposes needs fine-grained kernel locking (the single
|
||||
// Arc<Mutex<KernelState>> serializes the 6 workers); until then the default
|
||||
// --parallel path stays the per-round barrier executor below.
|
||||
// iterate-4E: canary-model native-threads mode. Routed here ahead of the
|
||||
// freerun check because native mode is the evolution of the freerun
|
||||
// executor (Stage 0 delegates to it verbatim; Stage 1' replaces the worker
|
||||
// set with one host thread per guest thread). Opt-in via XENIA_NATIVE_THREADS.
|
||||
if native_threads_enabled() {
|
||||
return run_execution_native(
|
||||
mem,
|
||||
kernel_arc,
|
||||
debugger,
|
||||
thunk_map,
|
||||
db_writer,
|
||||
max_instructions,
|
||||
ips_limit,
|
||||
quiet,
|
||||
halt_on_deadlock,
|
||||
shutdown_outer,
|
||||
);
|
||||
}
|
||||
|
||||
if std::env::var("XENIA_PARALLEL_FREERUN")
|
||||
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
.unwrap_or(false)
|
||||
@@ -4559,6 +4597,65 @@ mod freerun_prof {
|
||||
/// Non-deterministic by design (thread interleaving); this is the opt-in perf
|
||||
/// mode. Lockstep (`run_execution`) remains the byte-identical golden path.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// iterate-4E: is the canary-model native-threads mode selected?
|
||||
/// `XENIA_NATIVE_THREADS=1`. Single source of truth so every guarded native
|
||||
/// branch agrees; when unset the emulator behaves exactly as before (the
|
||||
/// lockstep golden path and the existing `--parallel`/freerun paths are
|
||||
/// untouched).
|
||||
fn native_threads_enabled() -> bool {
|
||||
std::env::var("XENIA_NATIVE_THREADS")
|
||||
.map(|v| {
|
||||
let v = v.trim().to_ascii_lowercase();
|
||||
v == "1" || v == "true" || v == "yes"
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// iterate-4E — canary-model native-threads executor entry.
|
||||
///
|
||||
/// Target end state (across Stages 1'–4'): one real host OS thread per guest
|
||||
/// thread, free-running, blocking on host primitives at guest waits, wall-clock
|
||||
/// timebase — i.e. xenia-canary's model, opt-in and non-deterministic, with the
|
||||
/// lockstep path retained as the deterministic golden/reference mode.
|
||||
///
|
||||
/// STAGE 0 (this commit): pure scaffolding. The flag is wired end-to-end and
|
||||
/// routed here, but the body delegates to the proven `iterate-4D` free-run
|
||||
/// executor verbatim, so native mode == freerun for now. This lets the
|
||||
/// functional gate (milestone boot + stress + deadlock check) run green against
|
||||
/// the flag before the executor is transformed in Stage 1'. Nothing in the
|
||||
/// default (flag-off) path changes.
|
||||
fn run_execution_native(
|
||||
mem: &xenia_memory::GuestMemory,
|
||||
kernel_arc: &std::sync::Arc<std::sync::Mutex<xenia_kernel::KernelState>>,
|
||||
debugger: &mut xenia_debugger::Debugger,
|
||||
thunk_map: &HashMap<u32, (ModuleId, u16, String)>,
|
||||
db_writer: Option<&mut xenia_analysis::DbWriter>,
|
||||
max_instructions: Option<u64>,
|
||||
ips_limit: Option<u64>,
|
||||
quiet: bool,
|
||||
halt_on_deadlock: bool,
|
||||
shutdown_outer: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
|
||||
) -> ExecStats {
|
||||
if !quiet {
|
||||
info!(
|
||||
"iterate-4E Stage 0: native-threads mode selected (delegating to \
|
||||
free-run executor; per-guest-thread model lands in Stage 1')"
|
||||
);
|
||||
}
|
||||
run_execution_parallel_freerun(
|
||||
mem,
|
||||
kernel_arc,
|
||||
debugger,
|
||||
thunk_map,
|
||||
db_writer,
|
||||
max_instructions,
|
||||
ips_limit,
|
||||
quiet,
|
||||
halt_on_deadlock,
|
||||
shutdown_outer,
|
||||
)
|
||||
}
|
||||
|
||||
fn run_execution_parallel_freerun(
|
||||
mem: &xenia_memory::GuestMemory,
|
||||
kernel_arc: &std::sync::Arc<std::sync::Mutex<xenia_kernel::KernelState>>,
|
||||
|
||||
87
native-gate.sh
Executable file
87
native-gate.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
# native-gate.sh — functional correctness gate for the iterate-4E canary-model
|
||||
# native-threads rework (host-thread-per-guest-thread, non-deterministic).
|
||||
#
|
||||
# Byte-identical goldens cannot gate a multi-threaded run (OS interleaving is
|
||||
# nondeterministic), so this script is the REPLACEMENT oracle. It runs three
|
||||
# checks and exits 0 iff all pass:
|
||||
#
|
||||
# 1. LOCKSTEP GOLDEN (flag-off): the deterministic default path stays
|
||||
# byte-identical on sylpheed_n200m.json. This is the safety net that
|
||||
# proves the native-mode work did not disturb the reference path.
|
||||
# 2. NATIVE RENDER MILESTONE: native mode (XENIA_NATIVE_THREADS=1) boots far
|
||||
# enough to render — draws>0 && swaps>0 in the run digest — proving the
|
||||
# executor runs guest CPU + drives the GPU end-to-end without hanging.
|
||||
# 3. NATIVE DEADLOCK STRESS: parallel_stress_short under XENIA_NATIVE_THREADS=1
|
||||
# with --halt-on-deadlock; N back-to-back short runs with no panic/hang,
|
||||
# surfacing lost-wakeups / lock-order inversions the single run misses.
|
||||
#
|
||||
# Usage: [SYLPHEED_ISO=...] native-gate.sh [milestone_n] [milestone_timeout_s]
|
||||
# milestone_n default 200000000 (renders; matches the golden anchor)
|
||||
# milestone_timeout_s default 120
|
||||
#
|
||||
# The binary must be built first (the caller owns the build so the OOM guardrail
|
||||
# CARGO_BUILD_JOBS=4 / free-check stays explicit):
|
||||
# CARGO_BUILD_JOBS=4 cargo build --release
|
||||
set -u
|
||||
cd "$(dirname "$0")" || exit 2
|
||||
BIN=./target/release/xenia-rs
|
||||
ISO_CHECK=sylpheed.iso
|
||||
MN="${1:-200000000}"
|
||||
MTO="${2:-120}"
|
||||
DIGEST=/tmp/native-gate-digest.json
|
||||
LOG=/tmp/native-gate
|
||||
mkdir -p "$LOG"
|
||||
fails=0
|
||||
hr(){ printf '=%.0s' {1..64}; echo; }
|
||||
|
||||
[ -x "$BIN" ] || { echo "FAIL: build first: CARGO_BUILD_JOBS=4 cargo build --release"; exit 3; }
|
||||
|
||||
# ---------------------------------------------------------------- 1) golden
|
||||
hr; echo "[1/3] LOCKSTEP GOLDEN — flag-off byte-identity (sylpheed_n200m)"; hr
|
||||
cargo test --release -p xenia-app --test sylpheed_oracles -- \
|
||||
--ignored --nocapture sylpheed_n200m >"$LOG/golden.log" 2>&1
|
||||
rc=$?
|
||||
if [ $rc -eq 0 ]; then echo " PASS (golden byte-identical)"; else
|
||||
echo " FAIL rc=$rc — see $LOG/golden.log"; tail -20 "$LOG/golden.log"; fails=$((fails+1)); fi
|
||||
|
||||
# ------------------------------------------------------- 2) native milestone
|
||||
hr; echo "[2/3] NATIVE RENDER MILESTONE — XENIA_NATIVE_THREADS=1, -n $MN"; hr
|
||||
rm -f "$DIGEST"
|
||||
XENIA_NATIVE_THREADS=1 timeout "$MTO" "$BIN" check "$ISO_CHECK" \
|
||||
-n "$MN" --gpu-inline --out "$DIGEST" >"$LOG/milestone.log" 2>&1
|
||||
rc=$?
|
||||
pkill -x xenia-rs 2>/dev/null
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo " FAIL emulator rc=$rc (timeout=$MTO s) — see $LOG/milestone.log"
|
||||
tail -20 "$LOG/milestone.log"; fails=$((fails+1))
|
||||
elif [ ! -f "$DIGEST" ]; then
|
||||
echo " FAIL no digest written — see $LOG/milestone.log"; fails=$((fails+1))
|
||||
else
|
||||
read -r draws swaps instrs < <(python3 - "$DIGEST" <<'PY'
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
print(d.get("draws",0), d.get("swaps",0), d.get("instructions",0))
|
||||
PY
|
||||
)
|
||||
echo " digest: instructions=$instrs draws=$draws swaps=$swaps"
|
||||
if [ "${draws:-0}" -gt 0 ] && [ "${swaps:-0}" -gt 0 ]; then
|
||||
echo " PASS (native mode renders)"
|
||||
else
|
||||
echo " FAIL (native mode did not render: draws=$draws swaps=$swaps)"; fails=$((fails+1)); fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------- 3) native stress
|
||||
hr; echo "[3/3] NATIVE DEADLOCK STRESS — parallel_stress_short (native)"; hr
|
||||
XENIA_NATIVE_THREADS=1 cargo test --release -p xenia-app --test parallel_stress -- \
|
||||
--nocapture parallel_stress_short >"$LOG/stress.log" 2>&1
|
||||
rc=$?
|
||||
if [ $rc -eq 0 ]; then
|
||||
grep -o "runs=[0-9]* ok=[0-9]* failed=[0-9]*" "$LOG/stress.log" | tail -1
|
||||
echo " PASS (no deadlock/panic)"
|
||||
else
|
||||
echo " FAIL rc=$rc — see $LOG/stress.log"; tail -20 "$LOG/stress.log"; fails=$((fails+1)); fi
|
||||
|
||||
hr
|
||||
if [ "$fails" -eq 0 ]; then echo "NATIVE GATE: PASS (3/3)"; exit 0
|
||||
else echo "NATIVE GATE: FAIL ($fails/3 checks failed)"; exit 1; fi
|
||||
@@ -19,7 +19,9 @@
|
||||
# resumes (tid25 start_entry 0x82506588 / tid26 0x825065b8). Full per-run log kept under
|
||||
# /tmp/sylph-run/.
|
||||
set -u
|
||||
cd "/home/fabi/RE - Project Sylpheed/xenia-rs" || exit 2
|
||||
# cd to this script's own dir (was a hard-coded '/home/fabi/RE - Project Sylpheed/
|
||||
# xenia-rs' that went stale when the tree was renamed 'RE Project Sylpheed').
|
||||
cd "$(dirname "$(readlink -f "$0")")" || exit 2
|
||||
RUNS="${1:-6}"; N="${2:-3000000000}"; TO="${3:-180}"; XGREP="${4:-}"
|
||||
OUT=/tmp/sylph-run; mkdir -p "$OUT"
|
||||
BIN=./target/release/xenia-rs
|
||||
|
||||
8
zq.py
8
zq.py
@@ -14,9 +14,13 @@ Usage:
|
||||
zq.py grep <substr> # instructions whose operands LIKE %substr%
|
||||
zq.py find <word_hex> # instructions whose raw word == value (e.g. a ptr)
|
||||
"""
|
||||
import duckdb, sys
|
||||
import duckdb, sys, os
|
||||
|
||||
DB = '/home/fabi/RE - Project Sylpheed/xenia-rs/sylpheed.db'
|
||||
# Resolve the DB next to this script so the tool survives the tree being renamed
|
||||
# (the old hard-coded '/home/fabi/RE - Project Sylpheed/...' path went stale when
|
||||
# the dir became 'RE Project Sylpheed'). Override with $SYLPHEED_DB.
|
||||
DB = os.environ.get('SYLPHEED_DB',
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sylpheed.db'))
|
||||
c = duckdb.connect(DB, read_only=True)
|
||||
H = lambda x: '0x%08x' % x
|
||||
|
||||
|
||||
Reference in New Issue
Block a user