From e7d96b68c19b7b755642ebb10168054b8aa935b2 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Mon, 12 Oct 2026 04:40:33 -0700 Subject: [PATCH] freeze_dump_policy: unit-test the profile-ring diversity scan Co-Authored-By: Claude Opus 5.5 (1M context) --- runtime/src/freeze_dump_policy.c | 15 +++++++++++++++ runtime/src/freeze_dump_policy.h | 8 ++++++++ runtime/src/freeze_heartbeat.c | 26 +++++++++----------------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/runtime/src/freeze_dump_policy.c b/runtime/src/freeze_dump_policy.c index 4cb55f4d..b3ebec64 100644 --- a/runtime/src/freeze_dump_policy.c +++ b/runtime/src/freeze_dump_policy.c @@ -98,6 +98,21 @@ int freeze_distinct_add(FreezeDistinct *set, uint32_t value) { return set->count > FREEZE_WEDGE_SPIN_MAX_FNS; } +uint32_t freeze_window_fn_diversity(uint64_t lo, uint64_t hi, uint64_t oldest, + int (*get)(uint64_t seq, uint32_t *fn, + int *in_exception)) { + if (lo < oldest) lo = oldest; + FreezeDistinct set = {{0}, 0}; + int any = 0; + for (uint64_t seq = lo; seq < hi; seq++) { + uint32_t fn = 0; + int in_exception = 0; + if (!get(seq, &fn, &in_exception) || in_exception) continue; + any = 1; + if (freeze_distinct_add(&set, fn)) break; + } + return any ? set.count : FREEZE_WEDGE_FN_DIVERSITY_UNKNOWN; +} uint32_t freeze_wedge_classify(const FreezeWedgeSample *window, uint32_t count, uint32_t fn_diversity, int (*mdec_recently_active)(uint32_t frames)) { diff --git a/runtime/src/freeze_dump_policy.h b/runtime/src/freeze_dump_policy.h index 2ce93c10..aa2c4184 100644 --- a/runtime/src/freeze_dump_policy.h +++ b/runtime/src/freeze_dump_policy.h @@ -73,6 +73,14 @@ typedef struct FreezeDistinct { * distinct values have been seen (the caller can stop scanning). */ int freeze_distinct_add(FreezeDistinct *set, uint32_t value); +/* Distinct functions entered outside exceptions over profile samples + * [lo, hi), clamped to oldest (the ring's oldest held sample). get() returns + * 0 for an evicted/unavailable sample. Stops counting past + * FREEZE_WEDGE_SPIN_MAX_FNS; FREEZE_WEDGE_FN_DIVERSITY_UNKNOWN when no + * non-exception sample covers the window. */ +uint32_t freeze_window_fn_diversity(uint64_t lo, uint64_t hi, uint64_t oldest, + int (*get)(uint64_t seq, uint32_t *fn, + int *in_exception)); /* Classify a window of consecutive samples, oldest first. A spin requires the * executing function, last store PC and dirty-RAM instruction count to hold * one value at EVERY sample, AND (when known) at most FREEZE_WEDGE_SPIN_MAX_FNS diff --git a/runtime/src/freeze_heartbeat.c b/runtime/src/freeze_heartbeat.c index 68b3138c..f3fb810e 100644 --- a/runtime/src/freeze_heartbeat.c +++ b/runtime/src/freeze_heartbeat.c @@ -756,24 +756,14 @@ void freeze_heartbeat_fatal_dump(const char *reason) { sio_get_mc_max_state(), sio_get_tx_writes()); } -/* Distinct guest functions entered outside exceptions in profile samples - * [lo, hi), read from the always-on guest-cycle profile ring. Stops counting - * past FREEZE_WEDGE_SPIN_MAX_FNS; UNKNOWN when no sample covers the window. */ -static uint32_t window_fn_diversity(uint64_t lo, uint64_t hi) { - uint64_t oldest = guest_cycle_profile_oldest(); - if (lo < oldest) lo = oldest; - FreezeDistinct set = {{0}, 0}; - int any = 0; +/* Adapter from the guest-cycle profile ring to freeze_window_fn_diversity. */ +static int gcp_sample_fn(uint64_t seq, uint32_t *fn, int *in_exception) { GcpSample s; - for (uint64_t seq = lo; seq < hi; seq++) { - if (!guest_cycle_profile_get(seq, &s)) continue; - if (s.flags & GCP_FLAG_EXCEPTION) continue; - any = 1; - if (freeze_distinct_add(&set, s.fn)) break; - } - return any ? set.count : FREEZE_WEDGE_FN_DIVERSITY_UNKNOWN; + if (!guest_cycle_profile_get(seq, &s)) return 0; + *fn = s.fn; + *in_exception = (s.flags & GCP_FLAG_EXCEPTION) != 0; + return 1; } - static void heartbeat_write(void) { uint64_t cyc = psx_get_cycle_count(); uint64_t frame = s_frame_count; @@ -866,7 +856,9 @@ static void heartbeat_write(void) { uint64_t lo = s_ring[(s_ring_head + RING_CAP - WEDGE_WINDOW_TICKS) % RING_CAP].gcp_seq; uint64_t hi = s_ring[(s_ring_head + RING_CAP - 1u) % RING_CAP].gcp_seq; wedge_kind = freeze_wedge_classify(window, WEDGE_WINDOW_TICKS, - window_fn_diversity(lo, hi), + freeze_window_fn_diversity( + lo, hi, guest_cycle_profile_oldest(), + gcp_sample_fn), mdec_recently_active); } }