fix(scheduler-sim-diagnostics): bootstrap limit plugin in sim mode (bug-1825)
info limit was correctly implemented and tested but inert against a real replay: register_limit()'s only call site was normal-mode sched_register_ plugins(); sim mode's own plugin bootstrap (sim_bootstrap_picker()) never registered the limit plugin. The snapshot format already carries the original session's limit config (sim->core.config, a borrowed reference set by xce_sched_core_load_snapshot()) -- nothing needed to change in the recording/replay format. New sim_bootstrap_limit_plugin() (sim_limit.c, mirroring sim_picker.c's structure) registers the plugin via plugin_register_core() and seeds it via a new limit_seed_config(plugin, xce_sched_core_t* core, config) entry point, wired into sim_loop.c alongside the existing sim_bootstrap_picker() call. limit_new_plugin() extracted from register_limit() as a factory, mirroring builtin_fcfs_new_plugin()'s established pattern; register_limit() itself is behavior-preserving. First attempt set plugin->sched to a disguised xce_sched_sim_driver_t*, relying on it sharing xce_scheduler_t's first-member layout to reuse the existing config_hook path -- rejected on review: this repeats the exact cross-cast pattern that already corrupted memory once in this codebase (see core.h's xce_core_cache_clear_cb_t history). Fixed by making limit_seed_config() take xce_sched_core_t* as an honest parameter instead, with limit_update_config() (normal mode) and the new sim path both delegating to a shared limit_seed_config_impl() -- no cast, no aliasing risk, independently verified by two review rounds reading the actual struct layouts and call chains. Also added sim_limit.c to sched_sim_isolation_lib so Req 5 AC 17's controller-I/O-isolation check covers it (an initial gap, closed in the same remediation round). Two ripple waves of the same established link-stub pattern applied to the ~24 white-box test files that #include sim_loop.c. info limit now shows real configured-limit data against a genuine replay. Live usage will still correctly show "no active usage instance yet" until a separate, larger fix (logged as bug-1826) wires limit *matching* into the sim decision path -- deliberately out of this task's scope. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
2f1ffd1e4a
commit
487e7b9419
32 changed files with 1377 additions and 28 deletions
|
|
@ -90,6 +90,7 @@
|
|||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
#include "../src/sim_limit.c" /* bug-1825: sched_sim_driver_run() now calls sim_bootstrap_limit_plugin() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
#include "../src/sim_record.c"
|
||||
#include "../src/sim_verify.c"
|
||||
#include "../src/snapshot.c"
|
||||
|
|
@ -2395,6 +2396,180 @@ test_repl_step_continue_with_info_limit_interleaved_matches_baseline_decision_se
|
|||
"baseline_decision_sequence: PASS\n");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* scheduler-sim-diagnostics spec, task 6 (bug-1825 fix): sim mode never */
|
||||
/* registers the limit plugin at all, so `info limit`'s otherwise-correct */
|
||||
/* display logic (tested above via the hand-rolled register_limit_plugin() */
|
||||
/* fixture) is permanently inert against a REAL replay. These tests drive */
|
||||
/* the actual production bootstrap function, sim_bootstrap_limit_plugin() */
|
||||
/* (new src/sim_limit.c, sim_loop.c's sim_bootstrap_picker()-call-site */
|
||||
/* sibling), against a real xce_sched_core_init()-initialized core -- not */
|
||||
/* the register_limit_plugin()/fixture_register_plugin_in_core() shortcut */
|
||||
/* the rest of this file's tests use, since the whole point here is */
|
||||
/* proving the real bootstrap path (registration + config-seed) works, */
|
||||
/* not re-proving repl_info_limit()'s own display logic again. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* (1) Registration: before sim_bootstrap_limit_plugin() runs, the limit
|
||||
* plugin is not present in sim->core.plugins at all (limit_get_configured_
|
||||
* limits()'s own documented "plugin absent" NULL return). After it runs,
|
||||
* the accessor resolves a real, iterable handle -- proving the function
|
||||
* actually registered "xce_sched_limit" against the simulation core via
|
||||
* plugin_register_core(), mirroring sim_bootstrap_picker()'s own
|
||||
* registration call exactly.
|
||||
*/
|
||||
static void
|
||||
test_sim_bootstrap_limit_plugin_registers_plugin_against_real_core(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
var limits_handle;
|
||||
int rc;
|
||||
|
||||
memset(&sim, 0, sizeof(sim));
|
||||
rc = xce_sched_core_init(&(sim.core));
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
limits_handle = limit_get_configured_limits(&(sim.core));
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
limits_handle,
|
||||
"the limit plugin must not be registered before "
|
||||
"sim_bootstrap_limit_plugin() runs");
|
||||
|
||||
sim_bootstrap_limit_plugin(&sim);
|
||||
|
||||
limits_handle = limit_get_configured_limits(&(sim.core));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
limits_handle,
|
||||
"sim_bootstrap_limit_plugin() must register the limit plugin "
|
||||
"against sim->core, matching sim_bootstrap_picker()'s own "
|
||||
"plugin_register_core() pattern");
|
||||
|
||||
printf(
|
||||
"test_sim_bootstrap_limit_plugin_registers_plugin_against_real_"
|
||||
"core: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* (2) THE observable criterion (task 6's own stated Observable): a real
|
||||
* xce_sched_sim_driver_t whose core.config already carries a real
|
||||
* xce_limit_config_t (as sim_locate_and_load_snapshot()/
|
||||
* xce_sched_core_load_snapshot() would have set it up from a replay's own
|
||||
* snapshot, before sim_bootstrap_limit_plugin() ever runs -- reproduced
|
||||
* here via direct construction per this task's own stated test-level
|
||||
* guidance, no capnp/.replay file needed) ends up with
|
||||
* limit_get_configured_limits() iterating to that SAME real limit by name
|
||||
* once sim_bootstrap_limit_plugin() runs. This is the test that actually
|
||||
* proves the config-seed step (limit_seed_config(), plugins/limit.c,
|
||||
* called directly with a real xce_sched_core_t* -- bug-1825 remediation
|
||||
* round 2 replaced the original plugin->sched-type-punned
|
||||
* plugin->hooks.config_hook() call with this honestly-typed entry point;
|
||||
* see sim_limit.c's own header comment for why), not just registration.
|
||||
*/
|
||||
static void
|
||||
test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_config_present(
|
||||
void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
xce_limit_config_t* limits[1];
|
||||
xce_conf4sched_t config;
|
||||
var limits_handle;
|
||||
iterator_t it;
|
||||
xce_limit_t* found = NULL;
|
||||
int rc;
|
||||
|
||||
memset(&sim, 0, sizeof(sim));
|
||||
rc = xce_sched_core_init(&(sim.core));
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
limits[0] = make_limit_config("sim_replay_limit");
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.nlimits = 1;
|
||||
config.limits = limits;
|
||||
|
||||
/* Mirrors sim_locate_and_load_snapshot()'s (sim.c) borrowed-reference
|
||||
* assignment of sim->core.config from the replay's own snapshot --
|
||||
* this test only needs the END STATE that call produces, not the
|
||||
* capnp/file machinery that gets there. */
|
||||
sim.core.config = &config;
|
||||
|
||||
sim_bootstrap_limit_plugin(&sim);
|
||||
|
||||
limits_handle = limit_get_configured_limits(&(sim.core));
|
||||
TEST_ASSERT_NOT_NULL(limits_handle);
|
||||
|
||||
rc = iter_make(&it, limits_handle);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = iter_next(&it, (void**)&found);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
TEST_ASSERT_NOT_EQUAL(RTTI_ITER_NO_DATA, rc);
|
||||
TEST_ASSERT_NOT_NULL(found);
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(
|
||||
"sim_replay_limit", found->name,
|
||||
"sim_bootstrap_limit_plugin() must seed the plugin with the REAL "
|
||||
"limit already sitting in sim->core.config, not merely register "
|
||||
"an empty plugin");
|
||||
|
||||
rc = iter_next(&it, (void**)&found);
|
||||
TEST_ASSERT_EQUAL_INT(RTTI_ITER_NO_DATA, rc);
|
||||
|
||||
iter_destroy(&it);
|
||||
|
||||
free_limit_config(limits[0]);
|
||||
|
||||
printf(
|
||||
"test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_"
|
||||
"config_present: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* (3) Graceful no-config case (task 6's explicit design constraint): a
|
||||
* replay whose snapshot carries no config -- or a test fixture that never
|
||||
* sets one up -- leaves sim->core.config NULL. sim_bootstrap_limit_plugin()
|
||||
* must still register the plugin successfully (non-NULL, empty-iterating
|
||||
* accessor handle) and must NOT crash attempting to seed a config that
|
||||
* does not exist; this is the normal case, not an error.
|
||||
*/
|
||||
static void
|
||||
test_sim_bootstrap_limit_plugin_registers_without_crash_when_config_null(
|
||||
void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
var limits_handle;
|
||||
iterator_t it;
|
||||
void* item = NULL;
|
||||
int rc;
|
||||
|
||||
memset(&sim, 0, sizeof(sim));
|
||||
rc = xce_sched_core_init(&(sim.core));
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
TEST_ASSERT_NULL(sim.core.config);
|
||||
|
||||
sim_bootstrap_limit_plugin(&sim);
|
||||
|
||||
limits_handle = limit_get_configured_limits(&(sim.core));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
limits_handle,
|
||||
"the plugin must still register successfully when "
|
||||
"sim->core.config is NULL -- the config-seed step must be "
|
||||
"skipped silently, not treated as a registration failure");
|
||||
|
||||
rc = iter_make(&it, limits_handle);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = iter_next(&it, &item);
|
||||
TEST_ASSERT_EQUAL_INT(RTTI_ITER_NO_DATA, rc);
|
||||
|
||||
iter_destroy(&it);
|
||||
|
||||
printf(
|
||||
"test_sim_bootstrap_limit_plugin_registers_without_crash_when_"
|
||||
"config_null: PASS\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
|
@ -2443,5 +2618,11 @@ int main(void)
|
|||
test_info_limit_on_empty_registry_is_repeatable_and_never_registers_plugin);
|
||||
RUN_TEST(
|
||||
test_repl_step_continue_with_info_limit_interleaved_matches_baseline_decision_sequence);
|
||||
RUN_TEST(
|
||||
test_sim_bootstrap_limit_plugin_registers_plugin_against_real_core);
|
||||
RUN_TEST(
|
||||
test_sim_bootstrap_limit_plugin_seeds_real_config_limit_when_config_present);
|
||||
RUN_TEST(
|
||||
test_sim_bootstrap_limit_plugin_registers_without_crash_when_config_null);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue