mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
test(cd): fail test_cd_bios_boot when the CD transfer wedged (#799)
test_cd_bios_boot printed [PASS] on a run where cd_seek_wedge fired: its criteria (pc_in_ram, not_loop, not_thrash, ram_payload) are all satisfied by a 68K that keeps running its CD service loop while the transfer is dead. Ask the watchdog instead of grepping its log: crash_detect.c now keeps a sticky count of cd_seek_wedge threshold crossings (counted before the log throttle, reset with CrashDetectReset) behind CrashDetectCDSeekWedgeFires() and CrashDetectCDSeekWedgeStreak(), exported via the existing CrashDetect* test ABI. The harness reads them per disc, forces virtualjaguar_crash_detect on, prints a [CD-WEDGE] reason, and FAILs the disc. A core without the symbols FAILs rather than passing blind. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
93e4097f48
commit
0983d22c2d
5 files changed
+76
-5
No files matched your search
@@ -183,6 +183,8 @@ heartbeat every 600 frames). Cost enabled: one indirect call + ~256-px hash/fram
|
||||
by clearing BUTCH bit 0 (Myst, Primal Rage, BrainDead 13 …) or I2CNTRL bit 2 (Philia); those
|
||||
idle drives used to fire it (#741). The line prints `butch_int=`/`i2s_ctrl=`. Pinned by
|
||||
`test/test_crash_detect_cd_wedge` (CI) + `test/tools/cd_seek_wedge_regress.sh` (private discs).
|
||||
`test/test_cd_bios_boot` FAILs a disc when the watchdog fired (`CrashDetectCDSeekWedgeFires()`,
|
||||
sticky, `[CD-WEDGE]` line) -- its PC checks alone pass on a dead transfer (#799).
|
||||
|
||||
Triaging "X crashes/hangs/black screen": the RetroArch log shows the signature — no save state
|
||||
or input recording needed. **Add new signatures here when you find a recurring failure mode not
|
||||
|
||||
@@ -144,6 +144,10 @@ static unsigned fb_same_hash_frames;
|
||||
|
||||
static uint32_t last_cd_fifo_drains;
|
||||
static unsigned cd_seek_wedge_frames;
|
||||
/* Sticky: how many times cd_seek_wedge has crossed its threshold since the
|
||||
* last CrashDetectReset(). Counted before the log throttle, so a test can
|
||||
* ask the watchdog directly instead of grepping the log (issue #799). */
|
||||
static unsigned cd_seek_wedge_fires;
|
||||
|
||||
static unsigned next_heartbeat_frame;
|
||||
|
||||
@@ -379,6 +383,7 @@ void CrashDetectReset(void)
|
||||
fb_same_hash_frames = 0;
|
||||
last_cd_fifo_drains = 0;
|
||||
cd_seek_wedge_frames = 0;
|
||||
cd_seek_wedge_fires = 0;
|
||||
next_heartbeat_frame = HEARTBEAT_FRAMES;
|
||||
hb_prev_hires_hits = 0;
|
||||
hb_prev_hires_miss_value = 0;
|
||||
@@ -473,6 +478,16 @@ int CrashDetectCDSeekWedgeFrame(uint32_t seek_starts, uint32_t seek_dones,
|
||||
&& (i2s_ctrl & CD_I2S_FIFO_ENABLE) != 0;
|
||||
}
|
||||
|
||||
unsigned CrashDetectCDSeekWedgeFires(void)
|
||||
{
|
||||
return cd_seek_wedge_fires;
|
||||
}
|
||||
|
||||
unsigned CrashDetectCDSeekWedgeStreak(void)
|
||||
{
|
||||
return cd_seek_wedge_frames;
|
||||
}
|
||||
|
||||
void CrashDetectFrameTick(const uint32_t *fb, unsigned w, unsigned h)
|
||||
{
|
||||
uint32_t cur_gpu_pc;
|
||||
@@ -621,6 +636,8 @@ void CrashDetectFrameTick(const uint32_t *fb, unsigned w, unsigned h)
|
||||
cd_butch_int, cd_i2s_ctrl))
|
||||
{
|
||||
cd_seek_wedge_frames++;
|
||||
if (cd_seek_wedge_frames == WEDGE_FRAMES_CD_SEEK)
|
||||
cd_seek_wedge_fires++;
|
||||
if (cd_seek_wedge_frames == WEDGE_FRAMES_CD_SEEK
|
||||
&& may_log(&last_log_cd_seek_wedge))
|
||||
{
|
||||
|
||||
@@ -48,6 +48,16 @@ int CrashDetectCDSeekWedgeFrame(uint32_t seek_starts, uint32_t seek_dones,
|
||||
int processor_running, uint8_t butch_int,
|
||||
uint8_t i2s_ctrl);
|
||||
|
||||
/* Watchdog state for tests (issue #799). A harness's own pass criteria
|
||||
* (PC in RAM, not looping) are all satisfied by a 68K that keeps running
|
||||
* its CD service loop while the transfer is dead, so they cannot see a
|
||||
* cd_seek_wedge; the watchdog can. Fires = how many times the wedge
|
||||
* crossed its threshold since CrashDetectReset() (sticky, not subject to
|
||||
* the log throttle, 0 while the watchdog is disabled); Streak = frames the
|
||||
* wedge predicate has held in a row right now. */
|
||||
unsigned CrashDetectCDSeekWedgeFires(void);
|
||||
unsigned CrashDetectCDSeekWedgeStreak(void);
|
||||
|
||||
/* In-frame hang signature (issue #740). The blitter runs synchronously
|
||||
* inside one register write, so a garbage B_COUNT freezes the host inside
|
||||
* retro_run and the per-frame checks never get a turn. Called at blit
|
||||
|
||||
@@ -83,6 +83,9 @@ static bool cd_environment(unsigned cmd, void *data)
|
||||
return true;
|
||||
}
|
||||
if (strcmp(var->key, "virtualjaguar_cd_boot_mode") == 0) { var->value = "bios"; return true; }
|
||||
/* The wedge verdict below reads the watchdog, so it must be on
|
||||
* whatever the core's default becomes. */
|
||||
if (strcmp(var->key, "virtualjaguar_crash_detect") == 0) { var->value = "enabled"; return true; }
|
||||
var->value = NULL;
|
||||
return false;
|
||||
}
|
||||
@@ -134,6 +137,15 @@ struct cd_disc_result {
|
||||
|
||||
/* CD subsystem activity captured at end-of-run. */
|
||||
struct cd_diag_snapshot diag;
|
||||
|
||||
/* Crash-watchdog verdict on the CD transfer (issue #799). The PC checks
|
||||
* above cannot see a dead transfer: a 68K that keeps running its CD
|
||||
* service loop satisfies every one of them. So the watchdog's own
|
||||
* cd_seek_wedge state is the oracle, read through the exported accessors
|
||||
* rather than by grepping its log line. */
|
||||
bool wedge_accessor_missing;
|
||||
unsigned cd_wedge_fires; /* times cd_seek_wedge crossed 300 frames */
|
||||
unsigned cd_wedge_streak; /* frames the predicate holds at end-of-run */
|
||||
};
|
||||
|
||||
static bool cd_load_game(const char *path)
|
||||
@@ -293,6 +305,21 @@ static void cd_run_one_disc(const char *path, unsigned frames,
|
||||
/* Capture CD subsystem snapshot before unload (counters reset on next reset). */
|
||||
cd_diag_capture(C.handle, &out->diag);
|
||||
|
||||
/* Ask the watchdog whether the transfer wedged. A core without these
|
||||
* symbols (no TEST_EXPORTS) cannot give a verdict -- fail, never pass. */
|
||||
{
|
||||
unsigned (*p_fires)(void);
|
||||
unsigned (*p_streak)(void);
|
||||
p_fires = (unsigned (*)(void))dlsym(C.handle, "CrashDetectCDSeekWedgeFires");
|
||||
p_streak = (unsigned (*)(void))dlsym(C.handle, "CrashDetectCDSeekWedgeStreak");
|
||||
if (p_fires && p_streak) {
|
||||
out->cd_wedge_fires = p_fires();
|
||||
out->cd_wedge_streak = p_streak();
|
||||
} else {
|
||||
out->wedge_accessor_missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hist.unique_overflow && hist.unique_count <= 32) {
|
||||
size_t i;
|
||||
fprintf(stderr, " [PC-SET] %zu unique PCs:", hist.unique_count);
|
||||
@@ -472,7 +499,8 @@ TEST(boot_all_discovered_discs_real_bios)
|
||||
}
|
||||
|
||||
ok = r->pc_stayed_in_ram && r->not_self_looping &&
|
||||
r->not_thrashing && r->ram_has_payload;
|
||||
r->not_thrashing && r->ram_has_payload &&
|
||||
!r->wedge_accessor_missing && r->cd_wedge_fires == 0;
|
||||
status_word = ok ? "PASS" : "FAIL";
|
||||
if (!ok) fail++; else pass++;
|
||||
|
||||
@@ -485,6 +513,18 @@ TEST(boot_all_discovered_discs_real_bios)
|
||||
r->unique_pc_count,
|
||||
r->unique_pc_overflow ? "+" : "",
|
||||
r->final_pc);
|
||||
/* The reason a run that looks alive above is still a FAIL (#799). */
|
||||
if (r->cd_wedge_fires > 0)
|
||||
fprintf(stderr,
|
||||
" [CD-WEDGE] %s : CD transfer wedged (cd_seek_wedge fired "
|
||||
"%u time(s), %u frames held at end-of-run) -- the PC checks "
|
||||
"cannot see a dead transfer\n",
|
||||
label, r->cd_wedge_fires, r->cd_wedge_streak);
|
||||
if (r->wedge_accessor_missing)
|
||||
fprintf(stderr,
|
||||
" [CD-WEDGE] %s : CrashDetectCDSeekWedgeFires not exported "
|
||||
"(build with TEST_EXPORTS=1); cannot judge the transfer\n",
|
||||
label);
|
||||
fprintf(stderr,
|
||||
" [DIAG] %s : gpu_pc=$%06X irq0=%u irq3=%u "
|
||||
"butchExec=%u fifoIRQs=%u dsaIRQs=%u fifoReads=%u "
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
# Before #741 the watchdog counted any frame with frozen FIFO drains, and
|
||||
# fired on every one of these titles while they went on to run (15 fires,
|
||||
# BUTCH low byte $02 each time: the game had cleared the master interrupt
|
||||
# enable to signal completion). Asserts on the log line itself, not the
|
||||
# harness [PASS]: test_cd_bios_boot also reports [PASS] on a real
|
||||
# IMASK-stuck wedge, so [PASS] proves nothing here. The other side (a real
|
||||
# wedge must still fire) is pinned without a disc by
|
||||
# enable to signal completion). Asserts on the log line as well as the
|
||||
# harness verdict. Since #799 test_cd_bios_boot itself reports FAIL when the
|
||||
# watchdog's cd_seek_wedge fired (it used to print [PASS] on a real
|
||||
# IMASK-stuck wedge), so the [PASS] requirement below already covers a false
|
||||
# fire; the grep stays as an independent check on the log. The other side (a
|
||||
# real wedge must still fire) is pinned without a disc by
|
||||
# test/test_crash_detect_cd_wedge.
|
||||
#
|
||||
# Needs the private corpus; SKIPs (exit 0) without it.
|
||||
|
||||
Reference in new issue
Block a user