diff --git a/.kiro/specs/scheduler-sim-diagnostics/tasks.md b/.kiro/specs/scheduler-sim-diagnostics/tasks.md index 3d888592..196a9967 100644 --- a/.kiro/specs/scheduler-sim-diagnostics/tasks.md +++ b/.kiro/specs/scheduler-sim-diagnostics/tasks.md @@ -29,7 +29,7 @@ - _Requirements: 1.6_ - [ ] 3. Core: Live accounted-usage display -- [ ] 3.1 Display usage for cluster-wide (non-host-scoped) limit instances +- [x] 3.1 Display usage for cluster-wide (non-host-scoped) limit instances - Iterate `limit_get_usage_instances()`, filter by `vres->limit == matched_limit`; for a GLOBAL/SHARED `vres`, read capacity from `core.sharedInst[idx]` and accounted usage from `core.sharedInUse[idx]` - Existence-of-instance and zero-usage are distinct: zero matching `xce_vres_t` (no capacity entry at all) prints "no active usage instance yet" (Requirement 2.2); a matched `xce_vres_t` whose `sharedInUse[idx]` is `NULL`/absent instead displays a genuine `accounted=0` (it has been evaluated and has a real capacity, just nothing charged against it yet) — existence is gated on the capacity read, never on the in-use read - Observable: a global limit that has matched at least one job shows an accounted/capacity pair reflecting current state; a configured-but-never-evaluated limit shows the explicit no-instance message instead; a matched-but-never-charged limit shows `accounted=0` rather than the no-instance message diff --git a/src/scheduler/src/repl_info.c b/src/scheduler/src/repl_info.c index 16fb675f..650820f2 100644 --- a/src/scheduler/src/repl_info.c +++ b/src/scheduler/src/repl_info.c @@ -1245,6 +1245,123 @@ static void repl_print_limit_host_scope_note_if_implicit( "does not show an explicit host scope\n"); } +/* + * repl_print_limit_usage - task 3.1, Requirement 1.4/2.1/2.2/2.3/3.3: + * prints `limit`'s live accounted usage against its configured capacity, + * one line per distinct GLOBAL/SHARED xce_vres_t instance found in + * limit_get_usage_instances() whose `->limit` pointer-equals `limit`. + * A host-scoped instance (`vres->vres` not flagged GLOBAL/SHARED) is + * task 3.2's job and is silently skipped here, per design.md's own + * per-instance filter ("skip -- do not display -- any per-host-scoped + * vres in this task"). + * + * Existence-of-instance is gated ONLY on the matched vres's CAPACITY entry + * (core.sharedInst[idx]) being present -- never on the in-use entry + * (core.sharedInUse[idx]). instantiate_limit() and update_limit_res_value() + * run back-to-back inside the same match_job_global_limit_() call + * (research.md's "Additional Findings"), so a genuinely existing vres + * always has a real capacity by the time it is observable here; only its + * in-use entry can legitimately be absent, and an absent in-use entry + * means genuine zero usage (accounted=0), never "no instance yet". This + * distinction is this task's single most important correctness property + * (design.md's own explicit callout) -- do not gate existence on the + * in-use read. + */ +static void repl_print_limit_usage(xce_sched_sim_driver_t* sim, + xce_limit_t* limit) +{ + var instances = limit_get_usage_instances(&(sim->core)); + iterator_t it; + int rc; + int n_shown = 0; + + if (instances == NULL) { + fprintf(stdout, "[sim] no active usage instance yet\n"); + return; + } + + rc = iter_make(&it, instances); + if (rc < 0) { + fprintf(stdout, "[sim] no active usage instance yet\n"); + return; + } + + for (;;) { + xce_vres_t* vres = NULL; + xce_val_t* cap_v = NULL; + + rc = iter_next(&it, (void**)&vres); + if ((rc < 0) || (rc == RTTI_ITER_NO_DATA) || (vres == NULL)) { + break; + } + + if (vres->limit != limit) { + continue; + } + + if ((vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) { + /* Host-scoped instance -- task 3.2's branch, not this one. */ + continue; + } + + if (vres->vres->idx < 0) { + /* No capacity entry at all -- this vres has never actually + * been evaluated to a usable capacity; do not fabricate a + * display for it (Requirement 2.2). */ + continue; + } + + /* zarray_get()/zarray_get_ex() (zarray.c) already bounds-check + * `idx` against the array's real capacity (`.caps`) and safely + * return NULL for anything out of range -- unlike + * repl_info_resource_one()'s own pre-check, this deliberately does + * NOT gate on `.count` first: `.count` tracks how many slots have + * ever been written (non-NULL), not the array's size, so a sparse + * array (a high idx populated while lower ones never were -- + * exactly this task's own "matched but different resources + * populated in different orders" reality) would otherwise be + * misread as absent even though zarray_get() itself would return + * the real, present entry. */ + cap_v = (xce_val_t*)zarray_get(&(sim->core.sharedInst), + vres->vres->idx); + if ((cap_v == NULL) || (cap_v->which != Value_u_fvalue)) { + continue; + } + + { + xce_val_t* used_v = NULL; + double used = 0.0; + char value_str[80]; + char used_str[80]; + + used_v = (xce_val_t*)zarray_get(&(sim->core.sharedInUse), + vres->vres->idx); + if ((used_v != NULL) && (used_v->which == Value_u_fvalue)) { + used = used_v->u.fvalue; + } + /* used_v == NULL (or wrong-typed) here is genuine zero usage, + * never "no instance" -- the capacity check above already + * gated existence; existence must not be re-derived from this + * read. */ + + repl_format_consumable_value_and_used( + repl_current_units_mode(sim), vres->vres->name, + cap_v->u.fvalue, used, value_str, sizeof(value_str), + used_str, sizeof(used_str)); + + fprintf(stdout, "[sim] usage %s: accounted=%s capacity=%s\n", + vres->vres->name, used_str, value_str); + } + + n_shown++; + } + iter_destroy(&it); + + if (n_shown == 0) { + fprintf(stdout, "[sim] no active usage instance yet\n"); + } +} + /* * repl_info_limit - `info limit ` name/all resolution (task 2.1) * plus, once a genuine match is found, printing that limit's configured @@ -1302,7 +1419,7 @@ static void repl_info_limit(xce_sched_sim_driver_t* sim, const char* arg) } } repl_print_limit_host_scope_note_if_implicit(&(sim->core), limit); - /* task 3 adds this limit's live usage. */ + repl_print_limit_usage(sim, limit); n_shown++; } iter_destroy(&it); @@ -1333,7 +1450,7 @@ static void repl_info_limit(xce_sched_sim_driver_t* sim, const char* arg) } } repl_print_limit_host_scope_note_if_implicit(&(sim->core), match); - /* task 3 adds this limit's live usage. */ + repl_print_limit_usage(sim, match); } } diff --git a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c index 1a8a9b94..03e01c44 100644 --- a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c +++ b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c @@ -180,6 +180,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + int sched_reconcile_job_resource_usage(xce_sched_core_t* c, xce_job_account_t* a, xce_job_rusages_t* r) diff --git a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c index fef5054b..70ec4750 100644 --- a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c +++ b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c @@ -116,6 +116,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_equivalence.c b/src/scheduler/testsuite/test_repl_equivalence.c index ce8e837d..0f390f9e 100644 --- a/src/scheduler/testsuite/test_repl_equivalence.c +++ b/src/scheduler/testsuite/test_repl_equivalence.c @@ -165,6 +165,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_event_echo.c b/src/scheduler/testsuite/test_repl_event_echo.c index 978441a4..f36bf997 100644 --- a/src/scheduler/testsuite/test_repl_event_echo.c +++ b/src/scheduler/testsuite/test_repl_event_echo.c @@ -133,6 +133,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_execution_control.c b/src/scheduler/testsuite/test_repl_execution_control.c index b4af286e..659d73a9 100644 --- a/src/scheduler/testsuite/test_repl_execution_control.c +++ b/src/scheduler/testsuite/test_repl_execution_control.c @@ -130,6 +130,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_help.c b/src/scheduler/testsuite/test_repl_help.c index 78581b4c..13f29ae3 100644 --- a/src/scheduler/testsuite/test_repl_help.c +++ b/src/scheduler/testsuite/test_repl_help.c @@ -125,6 +125,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c index 38324647..1c9869df 100644 --- a/src/scheduler/testsuite/test_repl_limit_table.c +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -513,6 +513,91 @@ static void add_configured_percent_limit_explicit_per_host( TEST_ASSERT_EQUAL_INT(0, rc); } +/* ---------------------------------------------------------------------- */ +/* Live-usage fixture helpers (task 3.1, Requirement 2.1/2.2/2.3/3.3/1.4) */ +/* ---------------------------------------------------------------------- */ + +/* + * instantiate_and_register_global_vres - drives the real, already-linked + * instantiate_limit() (xcelimit.c, part of libxce -- NOT plugins/limit.c, + * so no new link dependency) directly against `matched_limit` to create a + * genuine xce_vres_t exactly the way match_job_global_limit_() + * (plugins/limit.c) would on a real job match, then key_add()s it into + * `plugin`'s privdata->instances -- this TU has direct access to + * limit_plugin_data_t since it white-box #includes ../src/plugins/limit.c + * itself, so this is the same table entry limit_get_usage_instances() + * exposes, populated the same way production code populates it. + * + * `matched_limit` must be configured the way add_configured_limit() shapes + * it (absolute value, i.e. percent=0, on "mem") so is_host_based_limit() + * returns 0 and instantiate_limit()'s internal `shared` local stays 1 -- + * add_vres() then flags the resulting vres->vres XCE_RESF_SHARED| + * XCE_RESF_GLOBAL, the exact case task 3.1's own scope covers (a per-host + * vres is task 3.2's job, deliberately not exercised here). + * + * user/queue/project are passed as empty strings, never NULL -- + * instantiate_limit()/build_vres_name() both reject NULL outright (see + * xcelimit.c). Since this fixture's limit has no per-user/queue/project + * scoping configured (per_consumer=0 for all three, add_configured_limit()'s + * own shape), build_vres_name() never actually reads these values into the + * vresname it builds anyway -- only "limit=" is ever produced for + * this fixture family. + */ +static xce_vres_t* instantiate_and_register_global_vres( + xce_sched_sim_driver_t* sim, xce_sched_plugin_t* plugin, + xce_limit_t* matched_limit) +{ + limit_plugin_data_t* privdata = (limit_plugin_data_t*)plugin->data; + xce_vres_t* vres = NULL; + int rc; + + rc = instantiate_limit(&vres, sim->core.resmgr, matched_limit, "", "", + ""); + /* instantiate_limit() returns its own final key_find()'s positive + * result on success (matching match_job_global_limit_()'s own + * `if (rc < 0) return rc;` success convention, plugins/limit.c), not + * literally 0 -- do not assert rc == 0 here. */ + TEST_ASSERT_TRUE(rc >= 0); + TEST_ASSERT_NOT_NULL(vres); + TEST_ASSERT_NOT_NULL(vres->vres); + TEST_ASSERT_TRUE(vres->vres->idx >= 0); + TEST_ASSERT_TRUE_MESSAGE( + (vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) != 0, + "fixture sanity: this helper only ever produces a GLOBAL/SHARED " + "vres -- a per-host vres would mean the fixture's own limit shape " + "drifted from add_configured_limit()'s absolute-value contract"); + + rc = key_add(privdata->instances, (void*)vres->vres->name, (void*)vres); + TEST_ASSERT_TRUE(rc >= 0); + + return vres; +} + +/* + * put_shared_fvalue - manually populates sim->core.sharedInst or + * core.sharedInUse at `idx` with a Value_u_fvalue xce_val_t -- the exact + * shape put_resvals() (resource.c, deliberately NOT linked into this TU, + * see this file's own header comment) would build. Constructed by hand + * here rather than driving the real put_resvals()/update_limit_res_value() + * so this fixture does not need to link resource.c for real -- a + * deliberate, simpler-and-equally-valid choice this task's own brief calls + * out ("directly construct an xce_vres_t ... and manually populate + * core.sharedInst/core.sharedInUse via zarray_put() ... matching how + * update_limit_res_value()/put_resvals() do it"). + */ +static void put_shared_fvalue(zarray_t* array, int idx, double val) +{ + xce_val_t* v = (xce_val_t*)calloc(1, sizeof(*v)); + int rc; + + TEST_ASSERT_NOT_NULL(v); + v->which = Value_u_fvalue; + v->u.fvalue = val; + + rc = zarray_put(array, idx, (void*)v); + TEST_ASSERT_TRUE(rc >= 0); +} + /* ---------------------------------------------------------------------- */ /* Tests */ /* ---------------------------------------------------------------------- */ @@ -961,6 +1046,160 @@ test_info_limit_named_does_not_duplicate_host_scope_note_when_per_host_explicitl "per_host_explicitly_configured: PASS\n"); } +/* + * Task 3.1's own central Observable #1 (Requirement 2.1, 1.4, 3.3): a + * GLOBAL/SHARED limit instance that has genuinely been matched AND charged + * shows an accounted/capacity pair reflecting that real state -- not a + * placeholder or the no-instance message. + */ +static void +test_info_limit_named_shows_accounted_and_capacity_for_matched_and_charged_global_instance( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + xce_limit_t* matched = NULL; + var limits_handle; + xce_vres_t* vres; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_limit(&plugin, "charged_limit"); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"charged_limit", (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_global_vres(&sim, &plugin, matched); + put_shared_fvalue(&(sim.core.sharedInst), vres->vres->idx, 10.0); + put_shared_fvalue(&(sim.core.sharedInUse), vres->vres->idx, 4.0); + + captured = capture_info_limit(&sim, "charged_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "accounted=4.00"), + "Requirement 2.1: a global limit matched and charged must show its " + "accounted usage"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "capacity=10.00"), + "Requirement 2.1: ... alongside its configured capacity"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no active usage instance yet"), + "a genuinely matched+charged instance must not report 'no active " + "usage instance yet'"); + + free(captured); + + printf("test_info_limit_named_shows_accounted_and_capacity_for_matched_" + "and_charged_global_instance: PASS\n"); +} + +/* + * Task 3.1's own central Observable #2 (Requirement 2.2): a configured + * limit that has NEVER been evaluated against any job (no xce_vres_t at + * all in privdata->instances for it) must report the explicit no-instance + * message, never a fabricated zero-usage line. + */ +static void +test_info_limit_named_shows_no_active_usage_instance_for_never_matched_limit( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_limit(&plugin, "never_matched_limit"); + /* Deliberately no instantiate_and_register_global_vres() call -- this + * limit has never been evaluated against any job, so + * privdata->instances has no xce_vres_t at all for it. */ + + captured = capture_info_limit(&sim, "never_matched_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no active usage instance yet"), + "Requirement 2.2: a configured-but-never-evaluated limit must " + "report no active usage instance, not a fabricated zero"); + + free(captured); + + printf("test_info_limit_named_shows_no_active_usage_instance_for_never_" + "matched_limit: PASS\n"); +} + +/* + * Task 3.1's own central Observable #3 -- THE existence-vs-zero-usage + * distinction (Requirement 2.2's own most important correctness property, + * per design.md's explicit callout): a vres that genuinely exists AND has + * a real capacity entry, but has never been charged (no core.sharedInUse + * entry), must display a genuine accounted=0 -- NOT the no-instance + * message, since the instance itself (and its capacity) both genuinely + * exist. + */ +static void +test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instance( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + xce_limit_t* matched = NULL; + var limits_handle; + xce_vres_t* vres; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_limit(&plugin, "uncharged_limit"); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"uncharged_limit", (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_global_vres(&sim, &plugin, matched); + put_shared_fvalue(&(sim.core.sharedInst), vres->vres->idx, 10.0); + /* Deliberately no put_shared_fvalue() into core.sharedInUse -- a + * genuinely matched vres with a real capacity entry, but nothing + * charged against it yet. */ + + captured = capture_info_limit(&sim, "uncharged_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "accounted=0.00"), + "the existence-vs-zero-usage distinction: a matched vres with a " + "real capacity entry but an absent in-use entry must display a " + "genuine accounted=0, not the no-instance message"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no active usage instance yet"), + "must NOT report 'no active usage instance yet' -- the vres and " + "its capacity both genuinely exist"); + + free(captured); + + printf("test_info_limit_named_shows_accounted_zero_for_matched_but_" + "never_charged_instance: PASS\n"); +} + int main(void) { UNITY_BEGIN(); @@ -985,5 +1224,11 @@ int main(void) test_info_limit_named_does_not_show_host_scope_note_for_absolute_value_limit); RUN_TEST( test_info_limit_named_does_not_duplicate_host_scope_note_when_per_host_explicitly_configured); + RUN_TEST( + test_info_limit_named_shows_accounted_and_capacity_for_matched_and_charged_global_instance); + RUN_TEST( + test_info_limit_named_shows_no_active_usage_instance_for_never_matched_limit); + RUN_TEST( + test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instance); return UNITY_END(); } diff --git a/src/scheduler/testsuite/test_repl_prompt_context.c b/src/scheduler/testsuite/test_repl_prompt_context.c index 59a24267..b826ec7b 100644 --- a/src/scheduler/testsuite/test_repl_prompt_context.c +++ b/src/scheduler/testsuite/test_repl_prompt_context.c @@ -129,6 +129,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* Link-satisfying stubs -- same rationale as every sibling in this file * family (see test_repl_execution_control.c's own copies for the full * per-stub doc comments). */ diff --git a/src/scheduler/testsuite/test_repl_resource_table.c b/src/scheduler/testsuite/test_repl_resource_table.c index ed14ef5c..d795a0d6 100644 --- a/src/scheduler/testsuite/test_repl_resource_table.c +++ b/src/scheduler/testsuite/test_repl_resource_table.c @@ -96,6 +96,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_round_rollup.c b/src/scheduler/testsuite/test_repl_round_rollup.c index dacfc545..a00b7f61 100644 --- a/src/scheduler/testsuite/test_repl_round_rollup.c +++ b/src/scheduler/testsuite/test_repl_round_rollup.c @@ -92,6 +92,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_snapshot_params.c b/src/scheduler/testsuite/test_repl_snapshot_params.c index 8ae3b2a0..cde6b978 100644 --- a/src/scheduler/testsuite/test_repl_snapshot_params.c +++ b/src/scheduler/testsuite/test_repl_snapshot_params.c @@ -105,6 +105,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_step_verbosity.c b/src/scheduler/testsuite/test_repl_step_verbosity.c index 4c526ff3..e7bfed7c 100644 --- a/src/scheduler/testsuite/test_repl_step_verbosity.c +++ b/src/scheduler/testsuite/test_repl_step_verbosity.c @@ -146,6 +146,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_alt_events.c b/src/scheduler/testsuite/test_sim_alt_events.c index a3d54216..9bdb3458 100644 --- a/src/scheduler/testsuite/test_sim_alt_events.c +++ b/src/scheduler/testsuite/test_sim_alt_events.c @@ -123,6 +123,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } /* resource-accounting.md's reconciliation (resource.c, not linked here -- diff --git a/src/scheduler/testsuite/test_sim_contention_parity.c b/src/scheduler/testsuite/test_sim_contention_parity.c index bb2db615..1f3f26a7 100644 --- a/src/scheduler/testsuite/test_sim_contention_parity.c +++ b/src/scheduler/testsuite/test_sim_contention_parity.c @@ -208,6 +208,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* xce_sched_driver_on_decision()'s (driver.c) publish-side linker * dependencies -- never actually invoked: this file never registers a * decision callback. Same rationale test_sim_resource_contention.c's own diff --git a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c index abeb60f9..8d31afcc 100644 --- a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c +++ b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c @@ -231,6 +231,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy()/ * sched_charge_account()/sched_commit_job_alloc() directly). Same stub diff --git a/src/scheduler/testsuite/test_sim_equivalence.c b/src/scheduler/testsuite/test_sim_equivalence.c index 88ba575f..c5031ee6 100644 --- a/src/scheduler/testsuite/test_sim_equivalence.c +++ b/src/scheduler/testsuite/test_sim_equivalence.c @@ -192,6 +192,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_event_loop.c b/src/scheduler/testsuite/test_sim_event_loop.c index 9d13d8db..2c8ada31 100644 --- a/src/scheduler/testsuite/test_sim_event_loop.c +++ b/src/scheduler/testsuite/test_sim_event_loop.c @@ -139,6 +139,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_print_decision_gate.c b/src/scheduler/testsuite/test_sim_print_decision_gate.c index 05d2d890..ef7439c4 100644 --- a/src/scheduler/testsuite/test_sim_print_decision_gate.c +++ b/src/scheduler/testsuite/test_sim_print_decision_gate.c @@ -151,6 +151,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c index e0514836..a3b802ac 100644 --- a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c +++ b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c @@ -154,6 +154,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* sched_worker_release_host_ctx() is now a REAL (non-stub) symbol pulled in * from resource.c (task 2.1 addition, below) -- no local stand-in needed or * permitted (a local definition here would collide at link time). Unused by diff --git a/src/scheduler/testsuite/test_sim_record_output.c b/src/scheduler/testsuite/test_sim_record_output.c index f608a561..786117c4 100644 --- a/src/scheduler/testsuite/test_sim_record_output.c +++ b/src/scheduler/testsuite/test_sim_record_output.c @@ -110,6 +110,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_resource_contention.c b/src/scheduler/testsuite/test_sim_resource_contention.c index d9cc572d..69ad935e 100644 --- a/src/scheduler/testsuite/test_sim_resource_contention.c +++ b/src/scheduler/testsuite/test_sim_resource_contention.c @@ -175,6 +175,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* xce_sched_driver_run_normal_mode_round()'s (driver.c) linker dependency -- * a no-op stand-in, same rationale test_sim_equivalence.c's own copy * documents (this file drives its own resource accounting directly via diff --git a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c index a2ef678a..056c91e8 100644 --- a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c +++ b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c @@ -143,6 +143,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy() * directly, never sched_try_alloc_on_hosts()/sched_try_alloc_on_host_() diff --git a/src/scheduler/testsuite/test_sim_session_ordering.c b/src/scheduler/testsuite/test_sim_session_ordering.c index 79a1b489..e6e84fc8 100644 --- a/src/scheduler/testsuite/test_sim_session_ordering.c +++ b/src/scheduler/testsuite/test_sim_session_ordering.c @@ -175,6 +175,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- never * invoked (no worker is ever added here). */ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } diff --git a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c index 126d7570..b1e7563f 100644 --- a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c +++ b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c @@ -129,6 +129,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- same stub * test_sim_verify.c already establishes; never invoked (no worker is ever * added here). */ diff --git a/src/scheduler/testsuite/test_sim_verify.c b/src/scheduler/testsuite/test_sim_verify.c index 6a3bf81d..e82b140d 100644 --- a/src/scheduler/testsuite/test_sim_verify.c +++ b/src/scheduler/testsuite/test_sim_verify.c @@ -130,6 +130,19 @@ var limit_get_configured_limits(xce_sched_core_t* core) return NULL; } +/* scheduler-sim-diagnostics spec, task 3.1: repl_info.c now also calls + * limit_get_usage_instances() (plugins/limit.c, not part of this TU) to + * read a limit's live usage instances -- identical link-satisfying stub, + * same rationale as limit_get_configured_limits() immediately above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_usage_instances()'s own documented "plugin absent" return) is + * also the behaviorally correct answer here, not just a link placeholder. */ +var limit_get_usage_instances(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts !=