handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
196
audit-runs/audit-061-sub821C4EB0-branch-diff/canary-patch.diff
Normal file
196
audit-runs/audit-061-sub821C4EB0-branch-diff/canary-patch.diff
Normal file
@@ -0,0 +1,196 @@
|
||||
diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc
|
||||
index 5da8f6adc..e54f1f3e0 100644
|
||||
--- a/src/xenia/cpu/backend/x64/x64_emitter.cc
|
||||
+++ b/src/xenia/cpu/backend/x64/x64_emitter.cc
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
+#include <string>
|
||||
+#include <vector>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/assert.h"
|
||||
@@ -63,6 +65,47 @@ DEFINE_bool(instrument_call_times, false,
|
||||
"Compute time taken for functions, for profiling guest code",
|
||||
"x64");
|
||||
#endif
|
||||
+
|
||||
+// AUDIT-061: forward decl of the PC table (defined in ppc_hir_builder.cc).
|
||||
+namespace xe {
|
||||
+namespace cpu {
|
||||
+namespace audit61 {
|
||||
+const std::vector<uint32_t>& pcs();
|
||||
+} // namespace audit61
|
||||
+} // namespace cpu
|
||||
+} // namespace xe
|
||||
+
|
||||
+// AUDIT-061: handler for trap codes >= 200. arg0 carries trap idx
|
||||
+// (trap_code - 200), mapping to ::xe::cpu::audit61::pcs()[idx]. Emits one
|
||||
+// log line per fire with cr0/cr6 LGE flags + key GPRs + LR + tid.
|
||||
+static uint64_t TrapAudit61Branch(void* raw_context, uint64_t idx) {
|
||||
+ auto* ctx = reinterpret_cast<xe::cpu::ppc::PPCContext_s*>(raw_context);
|
||||
+ const auto& pcs = ::xe::cpu::audit61::pcs();
|
||||
+ uint32_t pc = (idx < pcs.size()) ? pcs[static_cast<size_t>(idx)] : 0u;
|
||||
+ uint32_t tid = 0;
|
||||
+ if (ctx->thread_state) {
|
||||
+ tid = ctx->thread_state->thread_id();
|
||||
+ }
|
||||
+ auto enc = [](uint8_t lt, uint8_t gt, uint8_t eq) {
|
||||
+ char buf[4];
|
||||
+ buf[0] = lt ? 'L' : '.';
|
||||
+ buf[1] = gt ? 'G' : '.';
|
||||
+ buf[2] = eq ? 'E' : '.';
|
||||
+ buf[3] = '\0';
|
||||
+ return std::string(buf);
|
||||
+ };
|
||||
+ XELOGI(
|
||||
+ "AUDIT-061-BR pc={:08X} lr={:08X} cr0={} cr6={} r3={:08X} r4={:08X} "
|
||||
+ "r5={:08X} r6={:08X} r31={:08X} tid={}",
|
||||
+ pc, static_cast<uint32_t>(ctx->lr),
|
||||
+ enc(ctx->cr0.cr0_lt, ctx->cr0.cr0_gt, ctx->cr0.cr0_eq),
|
||||
+ enc(ctx->cr6.cr6_all_equal, ctx->cr6.cr6_1, ctx->cr6.cr6_none_equal),
|
||||
+ static_cast<uint32_t>(ctx->r[3]), static_cast<uint32_t>(ctx->r[4]),
|
||||
+ static_cast<uint32_t>(ctx->r[5]), static_cast<uint32_t>(ctx->r[6]),
|
||||
+ static_cast<uint32_t>(ctx->r[31]), tid);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
@@ -455,6 +498,13 @@ void X64Emitter::Trap(uint16_t trap_type) {
|
||||
// ?
|
||||
break;
|
||||
default:
|
||||
+ // AUDIT-061: trap codes >= 200 dispatch the branch-probe handler.
|
||||
+ // arg0 = idx into ::xe::cpu::audit61::pcs().
|
||||
+ if (trap_type >= 200) {
|
||||
+ CallNative(::TrapAudit61Branch,
|
||||
+ static_cast<uint64_t>(trap_type - 200));
|
||||
+ break;
|
||||
+ }
|
||||
XELOGW("Unknown trap type {}", trap_type);
|
||||
db(0xCC);
|
||||
break;
|
||||
diff --git a/src/xenia/cpu/cpu_flags.cc b/src/xenia/cpu/cpu_flags.cc
|
||||
index 3ff067e15..2acce8db3 100644
|
||||
--- a/src/xenia/cpu/cpu_flags.cc
|
||||
+++ b/src/xenia/cpu/cpu_flags.cc
|
||||
@@ -57,3 +57,16 @@ DEFINE_bool(break_condition_truncate, true, "truncate value to 32-bits", "CPU");
|
||||
|
||||
DEFINE_bool(break_on_debugbreak, true, "int3 on JITed __debugbreak requests.",
|
||||
"CPU");
|
||||
+
|
||||
+// AUDIT-DEMO: smoke marker (memory entry: emulator.cc:225,283). Always-on bool.
|
||||
+DEFINE_bool(audit_demo_setup_trace, true,
|
||||
+ "Audit smoke marker: log AUDIT-DEMO-SETUP-BEGIN at emulator setup.",
|
||||
+ "Audit");
|
||||
+
|
||||
+// AUDIT-061: comma-separated list of guest PCs to log on each fire.
|
||||
+// Format: "0xPC1,0xPC2,..." (max 32 PCs). Each fire emits
|
||||
+// AUDIT-061-BR pc=X lr=X cr0=LGE cr6=LGE r3=X r4=X r5=X r6=X r31=X tid=N.
|
||||
+// Default empty (off); no perf cost when empty.
|
||||
+DEFINE_string(audit_61_branch_probe_pcs, "",
|
||||
+ "AUDIT-061: CSV of guest PCs to trace (cr0/cr6 + regs/tid).",
|
||||
+ "Audit");
|
||||
diff --git a/src/xenia/cpu/cpu_flags.h b/src/xenia/cpu/cpu_flags.h
|
||||
index 38c4f98ba..5731804f4 100644
|
||||
--- a/src/xenia/cpu/cpu_flags.h
|
||||
+++ b/src/xenia/cpu/cpu_flags.h
|
||||
@@ -35,4 +35,11 @@ DECLARE_bool(break_condition_truncate);
|
||||
|
||||
DECLARE_bool(break_on_debugbreak);
|
||||
|
||||
+// AUDIT-DEMO smoke marker.
|
||||
+DECLARE_bool(audit_demo_setup_trace);
|
||||
+
|
||||
+// AUDIT-061: multi-PC branch probe — emits one log line per fire with
|
||||
+// (pc, lr, cr0 LGE, cr6 LGE, r3, r4, r5, r6, r31, tid). CSV of guest PCs.
|
||||
+DECLARE_string(audit_61_branch_probe_pcs);
|
||||
+
|
||||
#endif // XENIA_CPU_CPU_FLAGS_H_
|
||||
diff --git a/src/xenia/cpu/ppc/ppc_hir_builder.cc b/src/xenia/cpu/ppc/ppc_hir_builder.cc
|
||||
index 42d996cba..adc431fd2 100644
|
||||
--- a/src/xenia/cpu/ppc/ppc_hir_builder.cc
|
||||
+++ b/src/xenia/cpu/ppc/ppc_hir_builder.cc
|
||||
@@ -34,6 +34,58 @@ DEFINE_bool(
|
||||
"unimplemented PowerPC instruction is encountered.",
|
||||
"CPU");
|
||||
|
||||
+// AUDIT-061 — multi-PC branch probe. Parses cvars::audit_61_branch_probe_pcs
|
||||
+// once and exposes a (pc -> trap_id) lookup table. trap_id range [200, 65535].
|
||||
+// PCs outside the table are not probed. Native side reads g_audit61_pcs[idx].
|
||||
+#include <vector>
|
||||
+#include <string>
|
||||
+namespace xe {
|
||||
+namespace cpu {
|
||||
+namespace audit61 {
|
||||
+constexpr uint16_t kTrapBase = 200;
|
||||
+constexpr size_t kMaxPcs = 32;
|
||||
+static std::vector<uint32_t> g_pcs;
|
||||
+static bool g_parsed = false;
|
||||
+
|
||||
+const std::vector<uint32_t>& pcs() {
|
||||
+ if (!g_parsed) {
|
||||
+ g_parsed = true;
|
||||
+ const std::string& csv = cvars::audit_61_branch_probe_pcs;
|
||||
+ size_t pos = 0;
|
||||
+ while (pos < csv.size() && g_pcs.size() < kMaxPcs) {
|
||||
+ size_t end = csv.find(',', pos);
|
||||
+ std::string tok = csv.substr(pos, end - pos);
|
||||
+ // strip whitespace
|
||||
+ while (!tok.empty() && (tok.front() == ' ' || tok.front() == '\t'))
|
||||
+ tok.erase(tok.begin());
|
||||
+ while (!tok.empty() && (tok.back() == ' ' || tok.back() == '\t'))
|
||||
+ tok.pop_back();
|
||||
+ if (!tok.empty()) {
|
||||
+ try {
|
||||
+ uint32_t v = static_cast<uint32_t>(std::stoul(tok, nullptr, 0));
|
||||
+ g_pcs.push_back(v);
|
||||
+ } catch (...) {
|
||||
+ }
|
||||
+ }
|
||||
+ if (end == std::string::npos) break;
|
||||
+ pos = end + 1;
|
||||
+ }
|
||||
+ }
|
||||
+ return g_pcs;
|
||||
+}
|
||||
+
|
||||
+// Returns trap id for pc, or 0 if pc not in probe set.
|
||||
+uint16_t trap_id_for(uint32_t pc) {
|
||||
+ const auto& v = pcs();
|
||||
+ for (size_t i = 0; i < v.size(); ++i) {
|
||||
+ if (v[i] == pc) return static_cast<uint16_t>(kTrapBase + i);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+} // namespace audit61
|
||||
+} // namespace cpu
|
||||
+} // namespace xe
|
||||
+
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
@@ -174,6 +226,20 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
|
||||
|
||||
MaybeBreakOnInstruction(address);
|
||||
|
||||
+ // AUDIT-061: emit a trap before this instruction if it's on the probe
|
||||
+ // list. The trap fires BEFORE the cmp/branch HIR emit so the native
|
||||
+ // handler observes cr0/cr6 set by the *previous* instruction (the cmp
|
||||
+ // that controls this conditional branch). ContextBarrier flushes
|
||||
+ // HIR temporaries to PPCContext so the handler reads consistent state.
|
||||
+ if (!::xe::cpu::audit61::pcs().empty()) {
|
||||
+ uint16_t tid = ::xe::cpu::audit61::trap_id_for(address);
|
||||
+ if (tid != 0) {
|
||||
+ Comment("--audit_61_branch_probe target");
|
||||
+ ContextBarrier();
|
||||
+ Trap(tid);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
InstrData i;
|
||||
i.address = address;
|
||||
i.code = code;
|
||||
Reference in New Issue
Block a user