Compare commits
1 Commits
iterate-2A
...
iterate-2A
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc37074f9e |
@@ -2451,6 +2451,23 @@ fn coord_pre_round(
|
|||||||
// restores the ~60 Hz rate at the cost of bit-exact run reproducibility,
|
// restores the ~60 Hz rate at the cost of bit-exact run reproducibility,
|
||||||
// which is acceptable under `--parallel` (M11 already documented
|
// which is acceptable under `--parallel` (M11 already documented
|
||||||
// `--parallel` as non-deterministic by design).
|
// `--parallel` as non-deterministic by design).
|
||||||
|
// 2.AZ — lockstep v-sync clock source.
|
||||||
|
//
|
||||||
|
// CORRECTION to the 2.AX framing (this iterate, measured): the lockstep
|
||||||
|
// ticker's instruction-count clock does NOT freeze after the post-boot
|
||||||
|
// wedge. `stats.instruction_count` is monotone & global and climbs the
|
||||||
|
// whole run (reaches the full -n budget) because the "wedge" is not a
|
||||||
|
// true all-blocked stall — tids 7/8/9/10 stay `Ready` and spin, so
|
||||||
|
// instructions keep retiring and the ticker keeps crossing the 150k
|
||||||
|
// threshold (~3 333 crossings @ -n 500M). The measured ~73-v-sync/run
|
||||||
|
// cap on *delivered* interrupts is the INJECTOR throughput
|
||||||
|
// (INTERRUPT_QUEUE_CAP=4 + one drain/round in
|
||||||
|
// `try_inject_graphics_interrupt`), NOT the clock. And even a delivered
|
||||||
|
// r3==0 VSync ISR never signals Event 0x10e8 — it takes the opt_callback
|
||||||
|
// `+44` path, a confirmed structural dead-end (2.AV/2.AX). So the
|
||||||
|
// cadence clock is NOT the wedge gate; the original instruction-count
|
||||||
|
// source is retained (driving a timebase ticker off `max_timebase`
|
||||||
|
// PLATEAUS when the lead thread blocks and regresses delivery 73→13).
|
||||||
let fired = if kernel.parallel_active {
|
let fired = if kernel.parallel_active {
|
||||||
kernel.interrupts.tick_vsync_wallclock()
|
kernel.interrupts.tick_vsync_wallclock()
|
||||||
} else {
|
} else {
|
||||||
@@ -2740,7 +2757,7 @@ fn worker_prologue(
|
|||||||
// the helper, no overhead on the hot path.
|
// the helper, no overhead on the hot path.
|
||||||
kernel.fire_ctor_probe_if_match(hw_id, mem);
|
kernel.fire_ctor_probe_if_match(hw_id, mem);
|
||||||
kernel.fire_branch_probe_if_match(hw_id);
|
kernel.fire_branch_probe_if_match(hw_id);
|
||||||
kernel.fire_lr_trace_if_match(hw_id, mem);
|
kernel.fire_lr_trace_if_match(hw_id);
|
||||||
|
|
||||||
if mem.has_mem_watch() {
|
if mem.has_mem_watch() {
|
||||||
let ctx = kernel.scheduler.ctx(hw_id);
|
let ctx = kernel.scheduler.ctx(hw_id);
|
||||||
|
|||||||
@@ -1196,6 +1196,26 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maximum guest timebase across every thread in every slot's runqueue
|
||||||
|
/// (2.AZ). This is the global guest-clock proxy: it advances both when
|
||||||
|
/// any thread executes (per-instruction `timebase += 1`) and when the
|
||||||
|
/// idle path jumps the timebase forward to a pending deadline
|
||||||
|
/// (`advance_all_timebases_to`). Unlike `ctx(hw_id).timebase` — which
|
||||||
|
/// reads only the *currently scheduled* thread on one slot and therefore
|
||||||
|
/// stalls whenever that slot's thread is Blocked — the max is monotone
|
||||||
|
/// across the whole machine, so a v-sync ticker keyed to it keeps
|
||||||
|
/// advancing even when the slot-0 thread is wedged. Deterministic:
|
||||||
|
/// derived purely from guest-cycle state, never host wall-clock.
|
||||||
|
/// Returns 0 when no threads exist.
|
||||||
|
pub fn max_timebase(&self) -> u64 {
|
||||||
|
self.slots
|
||||||
|
.iter()
|
||||||
|
.flat_map(|slot| slot.runqueue.iter())
|
||||||
|
.map(|t| t.ctx.timebase)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
/// Fast-forward the timebase to the earliest pending timed wait and
|
/// Fast-forward the timebase to the earliest pending timed wait and
|
||||||
/// wake that sleeper. Used when a round had no Ready threads and no
|
/// wake that sleeper. Used when a round had no Ready threads and no
|
||||||
/// timer fires closer than the earliest wait. Returns the woken
|
/// timer fires closer than the earliest wait. Returns the woken
|
||||||
|
|||||||
@@ -165,6 +165,15 @@ pub struct InterruptState {
|
|||||||
/// ticker. `tick_vsync_instr` diffs against this to advance
|
/// ticker. `tick_vsync_instr` diffs against this to advance
|
||||||
/// `vsync_accumulator`.
|
/// `vsync_accumulator`.
|
||||||
pub last_instr_count: u64,
|
pub last_instr_count: u64,
|
||||||
|
/// Last observed guest **timebase** for the deterministic-idle v-sync
|
||||||
|
/// ticker (`tick_vsync_timebase`, 2.AZ). Distinct accumulator state
|
||||||
|
/// from `last_instr_count` so the two tickers never alias. The guest
|
||||||
|
/// timebase advances `+1` per executed instruction during execution
|
||||||
|
/// (≈ the instruction count) *and* jumps forward in 1 µs units while
|
||||||
|
/// every thread is wedged (`advance_all_timebases_to` during idle), so
|
||||||
|
/// diffing it keeps the v-sync cadence moving when the guest stops
|
||||||
|
/// executing — fixing the lockstep self-stall (ISR dies at cyc 7.46M).
|
||||||
|
pub last_timebase: u64,
|
||||||
/// Wall-clock anchor for the production v-sync ticker. `None` until
|
/// Wall-clock anchor for the production v-sync ticker. `None` until
|
||||||
/// the first `tick_vsync_wallclock` call (lazy init so unit tests
|
/// the first `tick_vsync_wallclock` call (lazy init so unit tests
|
||||||
/// that never invoke that function don't construct an Instant).
|
/// that never invoke that function don't construct an Instant).
|
||||||
@@ -249,6 +258,52 @@ impl InterruptState {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **Lockstep (2.AZ)** — deterministic v-sync ticker driven off the
|
||||||
|
/// guest **timebase** instead of `stats.instruction_count`.
|
||||||
|
///
|
||||||
|
/// Root cause it fixes: `tick_vsync_instr` diffs `instruction_count`,
|
||||||
|
/// which is bumped ONLY by real guest execution. Once `tid=1` wedges on
|
||||||
|
/// Event 0x10e8 and every thread is Blocked/Exited, the lockstep loop
|
||||||
|
/// executes 0 instructions/round, `instruction_count` freezes, the
|
||||||
|
/// ticker delta is 0, and the VSync ISR `sub_824be9a0` stops firing
|
||||||
|
/// after cyc 7.46M (2.AX). Canary sustains 60 Hz forever because its
|
||||||
|
/// v-sync is host-clock driven, independent of guest CPU progress.
|
||||||
|
///
|
||||||
|
/// The guest timebase keeps advancing while the guest is wedged:
|
||||||
|
/// `coord_idle_advance` jumps it forward (in 1 µs units) to the next
|
||||||
|
/// timer / wait deadline via `advance_all_timebases_to`. Diffing it
|
||||||
|
/// therefore keeps queuing v-syncs during the wedge, and the existing
|
||||||
|
/// `try_inject_graphics_interrupt` Pass-2 delivers them onto a Blocked
|
||||||
|
/// thread. During *normal* execution the timebase advances ≈ 1:1 with
|
||||||
|
/// instruction count, so the same `VSYNC_INSTR_PERIOD` (150 000)
|
||||||
|
/// reproduces the established lockstep cadence — behaviour is
|
||||||
|
/// continuous across the execute↔idle boundary.
|
||||||
|
///
|
||||||
|
/// **Determinism**: the cadence derives purely from the deterministic
|
||||||
|
/// guest timebase (guest-cycle / µs deadlines), never host wall-clock,
|
||||||
|
/// so golden oracles stay bit-stable. Reuses the same period constant
|
||||||
|
/// as the instruction-count ticker for cadence continuity.
|
||||||
|
pub fn tick_vsync_timebase(&mut self, current_timebase: u64) -> bool {
|
||||||
|
let delta = current_timebase.saturating_sub(self.last_timebase);
|
||||||
|
self.last_timebase = current_timebase;
|
||||||
|
self.vsync_accumulator = self.vsync_accumulator.saturating_add(delta);
|
||||||
|
if self.vsync_accumulator < VSYNC_INSTR_PERIOD {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let periods = self.vsync_accumulator / VSYNC_INSTR_PERIOD;
|
||||||
|
self.vsync_accumulator %= VSYNC_INSTR_PERIOD;
|
||||||
|
// Cap the per-call burst at the FIFO depth: an idle round can jump
|
||||||
|
// the timebase forward by many periods at once (a far-off deadline),
|
||||||
|
// and `queue_interrupt` would otherwise drop the overflow silently.
|
||||||
|
// Bounding the queued count keeps delivery paced one-per-round
|
||||||
|
// rather than dumping a backlog that the injector can't drain.
|
||||||
|
let to_queue = periods.min(INTERRUPT_QUEUE_CAP as u64);
|
||||||
|
for _ in 0..to_queue {
|
||||||
|
self.queue_interrupt(INTERRUPT_SOURCE_VSYNC);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// **Production** — wall-clock v-sync ticker. Fires
|
/// **Production** — wall-clock v-sync ticker. Fires
|
||||||
/// `floor(elapsed / VSYNC_PERIOD)` v-syncs since the last call and
|
/// `floor(elapsed / VSYNC_PERIOD)` v-syncs since the last call and
|
||||||
/// advances the anchor by that many full periods (so a long pause
|
/// advances the anchor by that many full periods (so a long pause
|
||||||
@@ -356,6 +411,45 @@ mod tests {
|
|||||||
assert_eq!(s.pending.len(), 3);
|
assert_eq!(s.pending.len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_vsync_timebase_fires_at_period_threshold() {
|
||||||
|
// 2.AZ — timebase-driven lockstep ticker mirrors the
|
||||||
|
// instruction-count one: a delta < period queues nothing, a delta
|
||||||
|
// == period queues exactly one v-sync.
|
||||||
|
let mut s = InterruptState::default();
|
||||||
|
s.set_callback(0x1000, 0xAB);
|
||||||
|
assert!(!s.tick_vsync_timebase(VSYNC_INSTR_PERIOD - 1));
|
||||||
|
assert!(s.pending.is_empty());
|
||||||
|
assert!(s.tick_vsync_timebase(VSYNC_INSTR_PERIOD));
|
||||||
|
assert_eq!(s.peek_next(), Some(INTERRUPT_SOURCE_VSYNC));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_vsync_timebase_advances_while_guest_wedged() {
|
||||||
|
// The core 2.AZ fix: even with ZERO executed instructions, an idle
|
||||||
|
// round jumps the guest timebase forward (µs deadlines). Diffing
|
||||||
|
// the timebase must still queue the due v-syncs so the ISR keeps
|
||||||
|
// firing during the wedge. Here the timebase jumps by 2 periods in
|
||||||
|
// a single call with no intervening "instruction" progress.
|
||||||
|
let mut s = InterruptState::default();
|
||||||
|
s.set_callback(0x1000, 0xAB);
|
||||||
|
assert!(s.tick_vsync_timebase(VSYNC_INSTR_PERIOD * 2));
|
||||||
|
assert_eq!(s.pending.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_vsync_timebase_caps_burst_at_queue_cap() {
|
||||||
|
// A far-off idle deadline can jump the timebase forward by many
|
||||||
|
// periods at once; the per-call burst is capped at the FIFO depth
|
||||||
|
// so the backlog doesn't silently overflow `queue_interrupt`.
|
||||||
|
let mut s = InterruptState::default();
|
||||||
|
s.set_callback(0x1000, 0xAB);
|
||||||
|
let huge = VSYNC_INSTR_PERIOD * (INTERRUPT_QUEUE_CAP as u64 + 50);
|
||||||
|
assert!(s.tick_vsync_timebase(huge));
|
||||||
|
assert_eq!(s.pending.len(), INTERRUPT_QUEUE_CAP);
|
||||||
|
assert_eq!(s.dropped, 0, "cap should pre-bound, not drop");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_vsync_wallclock_first_call_sets_anchor() {
|
fn tick_vsync_wallclock_first_call_sets_anchor() {
|
||||||
// First call seeds the anchor and never fires. KRNBUG-D08:
|
// First call seeds the anchor and never fires. KRNBUG-D08:
|
||||||
|
|||||||
@@ -1509,7 +1509,7 @@ impl KernelState {
|
|||||||
/// `self.lr_trace_pcs`, emit one JSONL record. Format mirrors what
|
/// `self.lr_trace_pcs`, emit one JSONL record. Format mirrors what
|
||||||
/// xenia-canary's `--log_lr_on_pc` patch emits, plus the cycle
|
/// xenia-canary's `--log_lr_on_pc` patch emits, plus the cycle
|
||||||
/// counter. Read-only; lockstep digest unaffected.
|
/// counter. Read-only; lockstep digest unaffected.
|
||||||
pub fn fire_lr_trace_if_match(&self, hw_id: u8, mem: &GuestMemory) {
|
pub fn fire_lr_trace_if_match(&self, hw_id: u8) {
|
||||||
if self.lr_trace_pcs.is_empty() {
|
if self.lr_trace_pcs.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1518,155 +1518,6 @@ impl KernelState {
|
|||||||
if !self.lr_trace_pcs.contains(&pc) {
|
if !self.lr_trace_pcs.contains(&pc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 2.AT THROWAWAY DEREF PROBE: at the opt_callback block HEAD
|
|
||||||
// (PC 0x822F2248 — the bcctrl at 0x822F2278 is mid-block and the
|
|
||||||
// block-cache fast path never lands the live ctx.pc on it, so we
|
|
||||||
// resolve at the head where the object/vtable are already stable),
|
|
||||||
// resolve the vtable+0x1C method PC so we can lr-trace it.
|
|
||||||
// Read-only (loads only). Logs first 8 hits.
|
|
||||||
if pc == 0x822F2248 {
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static DEREF_HITS: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let n = DEREF_HITS.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if n < 8 {
|
|
||||||
let obj = mem.read_u32(0x828E1F08);
|
|
||||||
let vtable = mem.read_u32(obj);
|
|
||||||
let method = mem.read_u32(vtable.wrapping_add(0x1C));
|
|
||||||
let r4 = ctx.gpr[4] as u32;
|
|
||||||
println!(
|
|
||||||
"DEREF-PROBE pc=0x822f2248 hit={} obj=0x{:08x} vtable=0x{:08x} method=0x{:08x} r4=0x{:08x}",
|
|
||||||
n, obj, vtable, method, r4,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2.AT LEVEL-2 PROBE: method 0x821753c8 is itself a virtual
|
|
||||||
// trampoline: r11=[r3+8]; r11=[r11+44]; if r11==0 beqlr (no-op!);
|
|
||||||
// r3=[r11]; r11=[[r3]+48]; bctr. Capture this (r3), field_8,
|
|
||||||
// next_obj=[field_8+44] (the NULL-check target), and final
|
|
||||||
// vtable+0x30 method. Read-only. First 8 hits.
|
|
||||||
if pc == 0x821753c8 {
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static L2_HITS: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let n = L2_HITS.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if n < 8 {
|
|
||||||
let this = ctx.gpr[3] as u32;
|
|
||||||
let field8 = mem.read_u32(this.wrapping_add(8));
|
|
||||||
let next_obj = mem.read_u32(field8.wrapping_add(44));
|
|
||||||
let (l2_obj, l2_vtable, l2_method) = if next_obj != 0 {
|
|
||||||
let o = mem.read_u32(next_obj);
|
|
||||||
let vt = mem.read_u32(o);
|
|
||||||
let m = mem.read_u32(vt.wrapping_add(0x30));
|
|
||||||
(o, vt, m)
|
|
||||||
} else {
|
|
||||||
(0, 0, 0)
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"L2-PROBE pc=0x821753c8 hit={} this=0x{:08x} field8=0x{:08x} next_obj=0x{:08x} l2_obj=0x{:08x} l2_vtable=0x{:08x} l2_method=0x{:08x}",
|
|
||||||
n, this, field8, next_obj, l2_obj, l2_vtable, l2_method,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2.BC THROWAWAY PROBE A: right after `bl sub_822F13B0` returns
|
|
||||||
// inside opt_callback sub_822F2248 (block continues at 0x822F22D0
|
|
||||||
// after the enqueue). r31 = the work-object returned. The branch at
|
|
||||||
// 0x822F22D4 gates on [r31+8] (state field) which selects which of
|
|
||||||
// the two events ([r31+0] or [r31+4]) gets NtSetEvent'd. Capture
|
|
||||||
// the state field + both candidate handles + the queue head/tail at
|
|
||||||
// +84/+88 of the GLOBAL object [ [0x828E1F08]... actually the global
|
|
||||||
// at [0x828F+14404] ]. Read-only. First 12 hits.
|
|
||||||
if pc == 0x822F22D0 {
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static P_A: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let n = P_A.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if n < 12 {
|
|
||||||
// At block head 0x822F22D0 the `or r31,r3,r3` has NOT yet run,
|
|
||||||
// so gpr[31] is stale (incoming `this`). The bl to sub_822F13B0
|
|
||||||
// just returned, so the FRESH object ptr is in gpr[3].
|
|
||||||
let r31 = ctx.gpr[3] as u32; // = r3 returned from sub_822F13B0 (global 0x828f3844)
|
|
||||||
let state = mem.read_u32(r31.wrapping_add(8));
|
|
||||||
let h0 = mem.read_u32(r31.wrapping_add(0));
|
|
||||||
let h1 = mem.read_u32(r31.wrapping_add(4));
|
|
||||||
let q84 = mem.read_u32(r31.wrapping_add(84));
|
|
||||||
let q88 = mem.read_u32(r31.wrapping_add(88));
|
|
||||||
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
|
||||||
println!(
|
|
||||||
"PROBE-A pc=0x822f22d0 hit={} tid={} obj=0x{:08x} state(+8)=0x{:08x} h0(+0)=0x{:08x} h1(+4)=0x{:08x} q84=0x{:08x} q88=0x{:08x}",
|
|
||||||
n, tid, r31, state, h0, h1, q84, q88,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2.BC THROWAWAY PROBE B: at the NtSetEvent wrapper sub_824AA2F0
|
|
||||||
// head. opt_callback reaches it via 0x822F22F0 (lr=0x822f22f4) or
|
|
||||||
// 0x822F2300 (lr=0x822f2304). r3 = the event HANDLE being signaled.
|
|
||||||
// This is the decisive "does ours reach NtSetEvent from opt_callback
|
|
||||||
// and on which handle (0x10e8?)" check. Read-only. First 16 hits +
|
|
||||||
// tally of calls reached from the opt_callback lrs.
|
|
||||||
if pc == 0x824AA2F0 {
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static P_B: AtomicU32 = AtomicU32::new(0);
|
|
||||||
static P_B_OPTCB: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let lr = ctx.lr as u32;
|
|
||||||
let from_optcb = lr == 0x822F22F4 || lr == 0x822F2304;
|
|
||||||
if from_optcb {
|
|
||||||
P_B_OPTCB.fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
let n = P_B.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if n < 16 || from_optcb {
|
|
||||||
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
|
||||||
let handle = ctx.gpr[3] as u32;
|
|
||||||
println!(
|
|
||||||
"PROBE-B pc=0x824aa2f0(NtSetEvent-wrapper) hit={} tid={} handle=0x{:08x} lr=0x{:08x} from_optcb={} optcb_total={}",
|
|
||||||
n, tid, handle, lr, from_optcb,
|
|
||||||
P_B_OPTCB.load(Ordering::Relaxed),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2.BC THROWAWAY PROBE C: at 0x824ac574 (bl NtWaitForSingleObjectEx)
|
|
||||||
// r3 = the handle tid=1 wedges on. Capture which handle + tid. This
|
|
||||||
// is the consumer side: does opt_callback's signalled handle (0x108c
|
|
||||||
// per PROBE-B) EQUAL the handle the waiter blocks on? Read-only.
|
|
||||||
// Logs first 8 distinct + a tally per handle. First 24 hits.
|
|
||||||
if pc == 0x824AC574 {
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static P_C: AtomicU32 = AtomicU32::new(0);
|
|
||||||
// Per-handle tally for the two events of interest + 0x10e8.
|
|
||||||
static W_108C: AtomicU32 = AtomicU32::new(0);
|
|
||||||
static W_1090: AtomicU32 = AtomicU32::new(0);
|
|
||||||
static W_10E8: AtomicU32 = AtomicU32::new(0);
|
|
||||||
static W_108C_T1: AtomicU32 = AtomicU32::new(0);
|
|
||||||
static W_10E8_T1: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
|
||||||
let handle = ctx.gpr[3] as u32;
|
|
||||||
match handle {
|
|
||||||
0x108c => {
|
|
||||||
W_108C.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if tid == 1 { W_108C_T1.fetch_add(1, Ordering::Relaxed); }
|
|
||||||
}
|
|
||||||
0x1090 => { W_1090.fetch_add(1, Ordering::Relaxed); }
|
|
||||||
0x10e8 => {
|
|
||||||
W_10E8.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if tid == 1 { W_10E8_T1.fetch_add(1, Ordering::Relaxed); }
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
let n = P_C.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if n < 24 {
|
|
||||||
println!(
|
|
||||||
"PROBE-C pc=0x824ac574(NtWaitForSingleObjectEx) hit={} tid={} wait_handle=0x{:08x}",
|
|
||||||
n, tid, handle,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Periodic tally dump.
|
|
||||||
if n % 2000 == 0 {
|
|
||||||
println!(
|
|
||||||
"PROBE-C-TALLY n={} waits_on: 0x108c={}(tid1={}) 0x1090={} 0x10e8={}(tid1={})",
|
|
||||||
n,
|
|
||||||
W_108C.load(Ordering::Relaxed), W_108C_T1.load(Ordering::Relaxed),
|
|
||||||
W_1090.load(Ordering::Relaxed),
|
|
||||||
W_10E8.load(Ordering::Relaxed), W_10E8_T1.load(Ordering::Relaxed),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
||||||
let r3 = ctx.gpr[3] as u32;
|
let r3 = ctx.gpr[3] as u32;
|
||||||
let r4 = ctx.gpr[4] as u32;
|
let r4 = ctx.gpr[4] as u32;
|
||||||
|
|||||||
Reference in New Issue
Block a user