feat(scheduler-sim-repl-verbosity): task 3.2 - implement Event Echo Gate

Implements mode-aware event echo classification, job-arrival range
compression (Req 4.5), and round-collapse flush wiring at all 3
breakpoint-match/structural-event call sites (Requirements 1.1-1.4,
3.5, 3.6). All 4 of task 3.1's tests pass; also closes bug-1609
("continue is as verbose as step").

repl_break.c grew 827->905 lines (13% over the 800-line cap). Unlike
task 2.2's Round Rollup, this logic is wiring inside pre-existing
driver-facing hooks, not a relocatable subsystem - accepted in place
per design.md's established residual-tolerance precedent.

Review independently investigated a one-off ASAN stack-use-after-return
in an unrelated test (sched_sim_record_output_test) surfaced by this
change's stack-layout perturbation: traced to the same documented
pre-existing thread-lifecycle bug class as bug-1598/bug-1632 (an
unjoined picker thread reading a stack-local sim after test-function
return), confirmed via 11/11 clean reruns - logged as bug-1763, not a
regression from this task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAWMhfRJFnmmnG8h97yUDd
This commit is contained in:
Rongsong Shen 2026-09-02 13:00:09 +00:00
parent f587a441ac
commit cc78d09140
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 124 additions and 18 deletions

View file

@ -116,6 +116,33 @@ static xce_repl_bp_kind_t repl_bp_kind_for_core_event(xce_core_event_type_t t)
}
}
/*
* repl_event_is_structural - scheduler-sim-repl-verbosity spec, task 3.2,
* design.md's "Event Echo Gate" component (Requirement 1): partitions the
* same ten xce_core_event_type_t members BP_KIND_TABLE/
* repl_bp_kind_for_core_event() above already enumerate into structural
* (CONFIG_PUSH, JOBS_PUSH, WORKERS_PUSH, RESOURCES_PUSH, RESVALS_FULL --
* population/config arrivals echoed every mode, AC 1.3) vs. routine
* (RESVALS_DELTA, JOB_STATE_CHANGE, JOB_UPDATE, QUEUE_UPDATE, JOB_RUSAGE --
* echoed only while single-stepping, AC 1.1/1.2). Used only to gate
* xce_repl_on_event()'s call to repl_print_event_summary() below --
* breakpoint matching (repl_find_event_breakpoint()) runs on every event
* regardless of this classification (Requirement 1.4).
*/
static bool repl_event_is_structural(xce_core_event_type_t t)
{
switch (t) {
case XCE_CORE_EVENT_CONFIG_PUSH:
case XCE_CORE_EVENT_JOBS_PUSH:
case XCE_CORE_EVENT_WORKERS_PUSH:
case XCE_CORE_EVENT_RESOURCES_PUSH:
case XCE_CORE_EVENT_RESVALS_FULL:
return true;
default:
return false;
}
}
/* repl_job_state_matches - AC 22's `state=RUN`-style filter value, matched
* against the JOB_IS_*() bitmask macros (xce/types.h) every other live
* state check in this codebase already uses, rather than an exact bitwise
@ -616,21 +643,48 @@ static void repl_print_event_summary(xce_sched_sim_driver_t* sim,
break;
case XCE_CORE_EVENT_JOBS_PUSH: {
int i;
/* Requirement 4.5: adopt range compression for this job-arrival
* listing instead of printing each jobid individually. */
int n = ev->u.jobs_push.njobs;
uint64_t* jobids = (n > 0)
? (uint64_t*)malloc((size_t)n * sizeof(*jobids))
: NULL;
int njobids = 0;
int i;
fprintf(stdout, "[sim] event: JOB_PUSH njobs=%d jobids=[",
ev->u.jobs_push.njobs);
for (i = 0; i < ev->u.jobs_push.njobs; i++) {
for (i = 0; (i < n) && (jobids != NULL); i++) {
xce_job_t* j = ev->u.jobs_push.jobs[i];
char jobidStr[64];
if (j == NULL) {
continue;
if (j != NULL) {
jobids[njobids++] = j->jobid;
}
fmt_jobid(jobidStr, sizeof(jobidStr), j->jobid);
fprintf(stdout, "%s%s", (i > 0) ? "," : "", jobidStr);
}
fprintf(stdout, "[sim] event: JOB_PUSH njobs=%d jobids=[", n);
if (njobids > 0) {
char jobids_buf[1024];
int rc = repl_fmt_jobid_range(jobids_buf, sizeof(jobids_buf),
jobids, njobids);
if (rc >= 0) {
fprintf(stdout, "%s", jobids_buf);
} else {
/* design.md's Error Handling: a too-small compression
* buffer falls back to the pre-existing uncompressed
* per-id listing rather than truncating or dropping a
* jobid. */
for (i = 0; i < njobids; i++) {
char jobidStr[64];
fmt_jobid(jobidStr, sizeof(jobidStr), jobids[i]);
fprintf(stdout, "%s%s", (i > 0) ? "," : "", jobidStr);
}
}
}
fprintf(stdout, "]\n");
free(jobids);
break;
}
@ -733,13 +787,29 @@ void xce_repl_on_event(xce_sched_sim_driver_t* sim, xce_core_event_t* ev)
rs->event_index++;
/* task 12.5, bug-1595: unconditional echo, printed before the
* breakpoint check below so the operator sees "event happened" before
* (if it also matches) "breakpoint hit". */
repl_print_event_summary(sim, ev);
/*
* task 12.5, bug-1595: printed before the breakpoint check below so the
* operator sees "event happened" before (if it also matches) "breakpoint
* hit". scheduler-sim-repl-verbosity task 3.2 (Event Echo Gate,
* Requirement 1): a structural event is always echoed -- first flushing
* any active Round Rollup collapsed range so the two never interleave
* out of order (Requirement 3.5) -- while a routine event is echoed only
* while single-stepping; `continue` mode suppresses it.
*/
if (repl_event_is_structural(ev->type)) {
repl_round_collapse_flush(sim);
repl_print_event_summary(sim, ev);
} else if (rs->mode == XCE_REPL_MODE_STEP) {
repl_print_event_summary(sim, ev);
}
bp = repl_find_event_breakpoint(rs, ev);
if (bp != NULL) {
/* Requirement 3.6: end any active collapsed range before the
* breakpoint interruption is shown -- breakpoint matching itself
* is unaffected by the routine/structural classification above
* (Requirement 1.4). */
repl_round_collapse_flush(sim);
fprintf(stdout, "[sim] hit breakpoint %d @ event #%llu: %s\n", bp->id,
(unsigned long long)rs->event_index, bp->token);
repl_command_loop(sim);
@ -774,6 +844,10 @@ void xce_repl_on_begin_round(xce_sched_sim_driver_t* sim, uint64_t begin_round)
bp = repl_find_begin_round_breakpoint(rs);
if (bp != NULL) {
/* scheduler-sim-repl-verbosity task 3.2, Requirement 3.6: end any
* active collapsed range before this breakpoint interruption is
* shown. */
repl_round_collapse_flush(sim);
fprintf(stdout, "[sim] hit breakpoint %d @ BEGIN_ROUND round=%llu\n", bp->id,
(unsigned long long)begin_round);
repl_command_loop(sim);
@ -820,6 +894,10 @@ void xce_repl_on_decision(xce_sched_sim_driver_t* sim, xce_decision_t* d)
/* bug-1591: see repl_info.c's repl_info_worker_one() comment on
* fmt_jobid() -- same fix, same rationale. */
fmt_jobid(jobidStr, sizeof(jobidStr), decision_jobid(d));
/* scheduler-sim-repl-verbosity task 3.2, Requirement 3.6: end any
* active collapsed range before this breakpoint interruption is
* shown. */
repl_round_collapse_flush(sim);
fprintf(stdout, "[sim] hit breakpoint %d @ decision: %s jobid=%s\n",
bp->id, decision_op_name(d->op), jobidStr);
repl_command_loop(sim);