feat(scheduler-sim-decision-trace): hook the allocator runner

Task 3.4: the runner records no-plan reasons, the cluster-wide check,
plans (tried, discarded, not tried) and their ends, positions, claimed
skips and per-host candidate verdicts, rollbacks, commit or finalize
failure, and search end. A declined or generator-failed requirement now
counts once as 'no plan generated'. Runner results are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-08 12:04:16 +00:00
parent d4bee4b5d0
commit 16f40fa9dc
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
9 changed files with 1216 additions and 14 deletions

View file

@ -2696,3 +2696,23 @@ sched_trace_host_hooks_test = executable(
test('scheduler trace host hooks test',
sched_trace_host_hooks_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
# scheduler-sim-decision-trace task 3.4: the allocator runner's trace hooks
# (design.md "TraceHooks": Runner entry, Cluster-wide check, Generator start
# failure, Search end, Plan loop, Position, Candidate, Rollback, Finalize;
# Requirements 4.1, 4.3-4.9, 5.1, 5.2, 5.6-5.8) on the real
# sched_allocator_run(). Same white-box #include set as
# sched_alloc_runner_search_test; xvec_dup (claim snapshot) and
# make_job_alloc (finalize) are wrapped to fail on demand.
sched_trace_hooks_runner_test = executable(
'sched_trace_hooks_runner_test',
['test_trace_hooks_runner.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps],
link_args: ['-Wl,--wrap=xvec_dup',
'-Wl,--wrap=make_job_alloc'],
install: false)
test('scheduler trace hooks runner test',
sched_trace_hooks_runner_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])

File diff suppressed because it is too large Load diff

View file

@ -1334,8 +1334,7 @@ static void test_search_end_none_generated(void)
fixture_primary_t fp;
TEST_ASSERT_NOT_NULL(rec);
open_single(rec, &fp);
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_NONE_GENERATED);
open_single(rec, &fp); /* the search states it: no no_plan entry */
sched_trace_search_end(rec, 0);
assert_search_end(rec->entries_tail, 0, 0, 0, 0, 0,
SCHED_TRACE_SEARCH_NONE_GENERATED);
@ -1481,8 +1480,7 @@ static void test_search_end_nonzero_rc_has_no_statement(void)
SCHED_TRACE_SEARCH_NO_STMT);
close_single(rec, 1);
open_single(rec, &fp); /* generator failed: no plan, rc < 0 */
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_GENERATOR_FAILED);
open_single(rec, &fp); /* nothing generated, but rc < 0 */
sched_trace_search_end(rec, -12);
assert_search_end(rec->entries_tail, 0, 0, 0, 0, -12,
SCHED_TRACE_SEARCH_NO_STMT);
@ -1492,6 +1490,44 @@ static void test_search_end_nonzero_rc_has_no_statement(void)
sched_trace_rec_free(rec);
}
/* A declined requirement or a generator that cannot start (the runner
* returns 0 there, without a search end) is counted as "no plan generated"
* by its no_plan entry, once; a search end after it in the same
* requirement neither repeats the statement nor counts it again (4.7,
* 9.3). The next requirement starts over. */
static void test_no_plan_counts_none_generated_once(void)
{
xce_sched_trace_rec_t* rec = new_rec(XCE_TRACE_LINE_BOUND_DEFAULT);
fixture_primary_t fp;
TEST_ASSERT_NOT_NULL(rec);
make_primary(&fp, 1, 1);
sched_trace_path_push(rec, XCE_TRACE_PATH_SINGLE, 1, 1);
sched_trace_resreq_begin(rec, &fp.p, 0);
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_DECLINED);
close_single(rec, 0);
assert_req_counters(rec, 1, 0, 0);
open_single(rec, &fp);
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_GENERATOR_FAILED);
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_NO_PLAN, rec->entries_tail->type);
assert_req_counters(rec, 2, 0, 0);
sched_trace_search_end(rec, 0);
assert_search_end(rec->entries_tail, 0, 0, 0, 0, 0,
SCHED_TRACE_SEARCH_NO_STMT);
close_single(rec, 0);
assert_req_counters(rec, 2, 0, 0);
/* a later requirement with no no_plan entry states it itself */
open_single(rec, &fp);
sched_trace_search_end(rec, 0);
assert_search_end(rec->entries_tail, 0, 0, 0, 0, 0,
SCHED_TRACE_SEARCH_NONE_GENERATED);
close_single(rec, 0);
assert_req_counters(rec, 3, 0, 0);
sched_trace_rec_free(rec);
}
/* Counts belong to the requirement the search ran for: they restart with
* each member, while the decision-wide counters add up. */
static void test_search_end_counts_per_requirement(void)
@ -1539,6 +1575,35 @@ static void test_search_end_counts_per_requirement(void)
sched_trace_rec_free(rec);
}
/* A cluster-wide check never runs in a host scope left open: check_begin
* drops the stale host, its held rows and its row verdicts, so the check's
* rows are cluster-wide rows (task 3.4). */
static void test_check_begin_drops_a_stale_host_scope(void)
{
xce_sched_trace_rec_t* rec = new_rec(XCE_TRACE_LINE_BOUND_DEFAULT);
char name[8] = "n01";
xce_sched_worker_t w = make_worker(name, 0);
sched_trace_row_t row;
TEST_ASSERT_NOT_NULL(rec);
memset(&row, 0, sizeof(row));
row.res = "mem";
sched_trace_host_begin(rec, NULL, &w);
sched_trace_row(rec, &row); /* held for the host's verdict */
rec->host_row_verdicts |= XCE_TRACE_V_SHORTFALL;
TEST_ASSERT_EQUAL_INT(1, rec->host_nrows);
sched_trace_check_begin(rec, NULL);
TEST_ASSERT_NULL(rec->scope_w);
TEST_ASSERT_EQUAL_INT(0, rec->host_nrows);
TEST_ASSERT_EQUAL_UINT(0, rec->host_row_verdicts);
sched_trace_row(rec, &row);
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_CHECK_ROW, rec->entries_tail->type);
sched_trace_check_end(rec, 1, true);
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
sched_trace_rec_free(rec);
}
/* ---- counters under truncation (9.3) ---------------------------------- */
/*
@ -2008,6 +2073,8 @@ int main(void)
RUN_TEST(test_search_end_plan_internal_error_has_no_statement);
RUN_TEST(test_search_end_not_tried_after_discarded);
RUN_TEST(test_search_end_nonzero_rc_has_no_statement);
RUN_TEST(test_no_plan_counts_none_generated_once);
RUN_TEST(test_check_begin_drops_a_stale_host_scope);
RUN_TEST(test_search_end_counts_per_requirement);
RUN_TEST(test_counters_exact_after_truncation);
RUN_TEST(test_summary_position_tallies);