feat(scheduler-sim-repl-verbosity): task 8.2 - implement resource table rendering
Rewrites info worker's/info resource's resource-listing loops from a flat, unconditional fprintf into a 3-pass, section-grouped table (Consumable/Capacity, Load Metrics, Static Hardware Attributes), units_mode-aware value rendering (raw/human/both, including swp's MB->bytes conversion), whole-number formatting with no trailing .00 outside the consumable value=/used= pair, and jobid-range-compression adoption for info worker's assigned-job listing (Requirements 6.4-6.6, 4.5). All 10 of task 8.1's tests pass. Preserves every named prior-bug invariant (bug-1727's avail_v-as-total, bug-1613's consumable/ non-consumable split, bug-1593's GLOBAL/SHARED filter, bug-1688's .caps-bound iteration). Fixes one pre-existing test (bug-1688, predating this spec) whose "value=5.00" assertion for a whole-number non-consumable resource directly conflicts with Requirement 6.5's intentional no-trailing-.00 change - same class of fix as task 4.2's pre-existing-test updates. repl_info.c grew from 985 to 1267 lines (58% over the 800-line cap, worse than any other file this spec touched) - but unlike repl.c/ repl_break.c, it was already 23% over cap before this spec began. Review empirically confirmed a narrow helper-only extraction doesn't achieve compliance (still 41% over), and a compliance-achieving split would mean restructuring ~690 lines of pre-existing display logic this feature doesn't otherwise touch - a larger scheduler-core-level decision, accepted as a residual rather than folded into this task. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YAWMhfRJFnmmnG8h97yUDd
This commit is contained in:
parent
e48e222bd1
commit
9db99964f0
4 changed files with 415 additions and 130 deletions
|
|
@ -276,6 +276,175 @@ static void repl_info_queue(xce_sched_sim_driver_t* sim, const char* arg)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resource Value Formatter (scheduler-sim-repl-verbosity spec, task 8.2,
|
||||
* design.md's "Resource Value Formatter" component, Req 6.4-6.6) -- groups
|
||||
* `info worker`'s/`info resource`'s resource listings into three labeled
|
||||
* sections instead of one flat list, and renders a non-consumable numeric
|
||||
* value per the operator's `rs->units_mode` when it is byte/megabyte-valued
|
||||
* (repl_is_bytes_valued_resource()/repl_format_bytes_human(), repl_fmt.c,
|
||||
* task 7.2). Section classification is the SAME three categories design.md
|
||||
* names, in the SAME fixed order, for both commands:
|
||||
* Consumable/Capacity -- numeric, XCE_RESF_CONSUMABLE
|
||||
* Load Metrics -- numeric, not CONSUMABLE
|
||||
* Static Hardware Attrs -- string/boolean
|
||||
* A section prints its header only when it has at least one resource to
|
||||
* show -- `info resource`'s GLOBAL/SHARED-only scope (bug-1593) may
|
||||
* genuinely have none of a given category for a given replay.
|
||||
*
|
||||
* The Consumable/Capacity value=/used= pair deliberately keeps its
|
||||
* existing unconditional "%.2f" formatting (bug-1601/1613/1727's own
|
||||
* pinned shapes, test_repl_breakpoints_inspection.c) and is NOT
|
||||
* units_mode-aware -- Req 6.5's whole-number rule is explicitly scoped to
|
||||
* non-consumable values only (test_repl_resource_table.c's own file header
|
||||
* comment), and no bytes-valued CONSUMABLE resource is exercised anywhere
|
||||
* in this codebase's tests today. Only a Load Metrics (non-consumable
|
||||
* numeric) value goes through repl_format_numeric_resource_value() below.
|
||||
*/
|
||||
typedef enum {
|
||||
XCE_REPL_RES_SECTION_NONE = 0,
|
||||
XCE_REPL_RES_SECTION_CONSUMABLE,
|
||||
XCE_REPL_RES_SECTION_LOAD,
|
||||
XCE_REPL_RES_SECTION_STATIC
|
||||
} xce_repl_res_section_t;
|
||||
|
||||
static const struct {
|
||||
xce_repl_res_section_t section;
|
||||
const char* header;
|
||||
} XCE_REPL_RES_SECTION_TABLE[] = {
|
||||
{ XCE_REPL_RES_SECTION_CONSUMABLE, "-- Consumable/Capacity --" },
|
||||
{ XCE_REPL_RES_SECTION_LOAD, "-- Load Metrics --" },
|
||||
{ XCE_REPL_RES_SECTION_STATIC, "-- Static Hardware Attributes --" },
|
||||
};
|
||||
#define XCE_REPL_RES_SECTION_COUNT \
|
||||
((int)(sizeof(XCE_REPL_RES_SECTION_TABLE) \
|
||||
/ sizeof(XCE_REPL_RES_SECTION_TABLE[0])))
|
||||
|
||||
/* repl_classify_worker_resval_section - classifies one per-worker resval by
|
||||
* its ACTUAL wire type (avail_v->which, the same switch every existing
|
||||
* print site already branches on) plus the matching resource definition's
|
||||
* XCE_RESF_CONSUMABLE flag; rdef==NULL (resource not found in the resmgr
|
||||
* walk -- should not happen in practice) degrades to Load Metrics for a
|
||||
* numeric value, matching the pre-existing fallback behavior. A wire type
|
||||
* this codebase's resource system never actually produces for a resval
|
||||
* (ivalue32/uvalue32/ivalue64/uvalue64) classifies as NONE -- skipped
|
||||
* entirely, same as today. */
|
||||
static xce_repl_res_section_t
|
||||
repl_classify_worker_resval_section(const xce_val_t* avail_v,
|
||||
const xce_resource_t* rdef)
|
||||
{
|
||||
switch (avail_v->which) {
|
||||
case Value_u_svalue:
|
||||
case Value_u_bvalue:
|
||||
return XCE_REPL_RES_SECTION_STATIC;
|
||||
case Value_u_fvalue:
|
||||
return ((rdef != NULL) && ((rdef->flags & XCE_RESF_CONSUMABLE) != 0))
|
||||
? XCE_REPL_RES_SECTION_CONSUMABLE
|
||||
: XCE_REPL_RES_SECTION_LOAD;
|
||||
default:
|
||||
return XCE_REPL_RES_SECTION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* repl_classify_resource_def_section - the `info resource all` counterpart:
|
||||
* classifies a resource DEFINITION (not yet a runtime value) the same way,
|
||||
* driven by r->valtype/r->flags -- used to group the "all" listing into
|
||||
* sections BEFORE repl_info_resource_one() prints each resource's own
|
||||
* metadata/value lines (Requirement 6.6). */
|
||||
static xce_repl_res_section_t
|
||||
repl_classify_resource_def_section(const xce_resource_t* r)
|
||||
{
|
||||
if (r->valtype != XCE_VAL_NUMERIC) {
|
||||
return XCE_REPL_RES_SECTION_STATIC;
|
||||
}
|
||||
return ((r->flags & XCE_RESF_CONSUMABLE) != 0)
|
||||
? XCE_REPL_RES_SECTION_CONSUMABLE
|
||||
: XCE_REPL_RES_SECTION_LOAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* repl_format_numeric_resource_value - Requirement 6.1/6.2/6.5: formats the
|
||||
* text that follows "value=" for a non-consumable ("Load Metrics") numeric
|
||||
* resource. A byte-valued resource (repl_is_bytes_valued_resource()) or
|
||||
* `swp` (repl_internal.h's own documented MB-special-case, converted
|
||||
* x1024x1024 before scaling) renders per `mode` -- raw: the bare "%f" wire
|
||||
* value; human: repl_format_bytes_human()'s scaled form alone; both (the
|
||||
* default): "<raw> (<human>)", the exact "%f (%s)" precedent xce_test's own
|
||||
* cmd_resval already establishes (test.c:2540). Every other numeric
|
||||
* resource renders with no unit suffix and no trailing ".00" on a whole
|
||||
* number, keeping two decimal places for a fractional one. Shared verbatim
|
||||
* by repl_info_worker_one()'s and repl_info_resource_one()'s identical Load
|
||||
* Metrics rendering so the two commands can never disagree about this
|
||||
* format.
|
||||
*/
|
||||
static void repl_format_numeric_resource_value(xce_repl_units_mode_t mode,
|
||||
const char* rname,
|
||||
double value, char* buf,
|
||||
size_t bufsz)
|
||||
{
|
||||
bool is_swp = (rname != NULL) && (strcmp(rname, "swp") == 0);
|
||||
|
||||
if (repl_is_bytes_valued_resource(rname) || is_swp) {
|
||||
double bytes_value = is_swp ? (value * 1024.0 * 1024.0) : value;
|
||||
char human[64];
|
||||
|
||||
repl_format_bytes_human(bytes_value, human, sizeof(human));
|
||||
|
||||
switch (mode) {
|
||||
case XCE_REPL_UNITS_RAW:
|
||||
snprintf(buf, bufsz, "%f", value);
|
||||
break;
|
||||
case XCE_REPL_UNITS_HUMAN:
|
||||
snprintf(buf, bufsz, "%s", human);
|
||||
break;
|
||||
case XCE_REPL_UNITS_BOTH:
|
||||
default:
|
||||
snprintf(buf, bufsz, "%f (%s)", value, human);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requirement 6.5: no trailing ".00" on a whole-number value; a
|
||||
* fractional value keeps its two decimal places. */
|
||||
if (value == (double)(long long)value) {
|
||||
snprintf(buf, bufsz, "%.0f", value);
|
||||
} else {
|
||||
snprintf(buf, bufsz, "%.2f", value);
|
||||
}
|
||||
}
|
||||
|
||||
/* repl_current_units_mode - sim->repl_state may legitimately be NULL (a few
|
||||
* white-box tests dispatch `info worker`/`info resource` without ever
|
||||
* calling repl_state_ensure()); default to the same XCE_REPL_UNITS_BOTH
|
||||
* repl_state_ensure() itself explicitly assigns (Req 6.3), rather than
|
||||
* leaving repl_format_numeric_resource_value() to guess. */
|
||||
static xce_repl_units_mode_t
|
||||
repl_current_units_mode(xce_sched_sim_driver_t* sim)
|
||||
{
|
||||
return (sim->repl_state != NULL) ? sim->repl_state->units_mode
|
||||
: XCE_REPL_UNITS_BOTH;
|
||||
}
|
||||
|
||||
/* repl_append_u64 - grows `*arr`/`*n` by one element, the same exact-size
|
||||
* per-append realloc idiom repl_fmt.c's repl_jobid_group_add_idx()
|
||||
* establishes for this file family. Returns false (arr/n unchanged) only on
|
||||
* allocation failure -- callers degrade gracefully (report whatever was
|
||||
* collected so far) rather than losing the whole listing. */
|
||||
static bool repl_append_u64(uint64_t** arr, int* n, uint64_t val)
|
||||
{
|
||||
uint64_t* grown
|
||||
= (uint64_t*)realloc(*arr, (size_t)(*n + 1) * sizeof(**arr));
|
||||
|
||||
if (grown == NULL) {
|
||||
return false;
|
||||
}
|
||||
grown[*n] = val;
|
||||
*arr = grown;
|
||||
(*n)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* repl_info_worker_one - AC 22's "info worker <name>": "Dump one worker:
|
||||
* state, resources, assigned jobs." `w->resvals` holds the resource's
|
||||
|
|
@ -352,129 +521,213 @@ static void repl_info_worker_one(xce_sched_sim_driver_t* sim,
|
|||
* A `.caps`-bound loop can hit legitimately-empty slots that a
|
||||
* `.count`-bound loop never would, so avail_v/rname's existing
|
||||
* NULL-skip below (unchanged) is what keeps this safe. */
|
||||
for (i = 0; i < w->resvals.caps; i++) {
|
||||
xce_val_t* avail_v = (xce_val_t*)zarray_get(&(w->resvals), i);
|
||||
xce_val_t* used_v = (xce_val_t*)zarray_get(&(w->resInUse), i);
|
||||
const char* rname = find_resource_name_by_idx(res_arr, res_n, i);
|
||||
xce_resource_t* rdef = NULL;
|
||||
int j;
|
||||
/*
|
||||
* Requirement 6.6: three passes over the SAME .caps-bound resval range
|
||||
* (bug-1688's own fix, unchanged) -- one per XCE_REPL_RES_SECTION_TABLE
|
||||
* entry, in that fixed order -- rather than one flat pass, so each
|
||||
* section's header prints once, only if it has at least one matching
|
||||
* resource, and every section's content is fully printed before the
|
||||
* next section's header (the ordering test_repl_resource_table.c's own
|
||||
* tests assert on).
|
||||
*/
|
||||
{
|
||||
xce_repl_units_mode_t units_mode = repl_current_units_mode(sim);
|
||||
int s;
|
||||
|
||||
if ((avail_v == NULL) || (rname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
for (s = 0; s < XCE_REPL_RES_SECTION_COUNT; s++) {
|
||||
bool section_header_printed = false;
|
||||
|
||||
for (j = 0; j < res_n; j++) {
|
||||
if (res_arr[j]->idx == i) {
|
||||
rdef = res_arr[j];
|
||||
break;
|
||||
for (i = 0; i < w->resvals.caps; i++) {
|
||||
xce_val_t* avail_v = (xce_val_t*)zarray_get(&(w->resvals), i);
|
||||
xce_val_t* used_v = (xce_val_t*)zarray_get(&(w->resInUse),
|
||||
i);
|
||||
const char* rname
|
||||
= find_resource_name_by_idx(res_arr, res_n, i);
|
||||
xce_resource_t* rdef = NULL;
|
||||
int j;
|
||||
|
||||
if ((avail_v == NULL) || (rname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < res_n; j++) {
|
||||
if (res_arr[j]->idx == i) {
|
||||
rdef = res_arr[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* bug-1590: xce_val_t.which (a Value_u_which wire tag,
|
||||
* controller.capnp.h) tells us the value's REAL type --
|
||||
* bug-1568 fixed comparing it against the wrong enum for
|
||||
* the numeric case, but left every non-numeric resource
|
||||
* (Value_u_svalue/Value_u_bvalue -- e.g. this worker's
|
||||
* `type`/`model` string resources, or any boolean-typed
|
||||
* resource) silently coerced to 0.0 by the old
|
||||
* unconditional %.2f formatting. A value/used split is
|
||||
* only meaningful for a CONSUMABLE numeric resource -- a
|
||||
* string/boolean resource, or a non-consumable numeric one
|
||||
* (a static per-worker attribute nothing ever allocates
|
||||
* against), gets its own single "value=" line instead --
|
||||
* repl_classify_worker_resval_section() above encodes this
|
||||
* same three-way split for the section this line belongs
|
||||
* under. */
|
||||
if (repl_classify_worker_resval_section(avail_v, rdef)
|
||||
!= XCE_REPL_RES_SECTION_TABLE[s].section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!section_header_printed) {
|
||||
fprintf(stdout, "[sim] %s\n",
|
||||
XCE_REPL_RES_SECTION_TABLE[s].header);
|
||||
section_header_printed = true;
|
||||
}
|
||||
|
||||
switch (avail_v->which) {
|
||||
case Value_u_svalue:
|
||||
fprintf(stdout, "[sim] resource %s: value=%s\n", rname,
|
||||
avail_v->u.svalue ? avail_v->u.svalue : "(null)");
|
||||
break;
|
||||
|
||||
case Value_u_bvalue:
|
||||
fprintf(stdout, "[sim] resource %s: value=%s\n", rname,
|
||||
avail_v->u.bvalue ? "true" : "false");
|
||||
break;
|
||||
|
||||
case Value_u_fvalue:
|
||||
if ((rdef != NULL)
|
||||
&& (rdef->flags & XCE_RESF_CONSUMABLE)) {
|
||||
/* bug-1727: w->resvals[idx] (avail_v here) is the
|
||||
* resource's STATIC TOTAL capacity, not a live
|
||||
* "remaining available" quantity that decreases as
|
||||
* jobs consume it -- confirmed against the actual
|
||||
* admission-control formula this exact field
|
||||
* feeds, resource.c's unsatisfy_(): `total[i] <
|
||||
* inuse[i] + uncomitted[i] + wanted[i]` treats
|
||||
* get_resvals(&w->resvals, ...)'s output as `total`
|
||||
* directly, comparing it against inuse (not
|
||||
* total-inuse already subtracted from avail_v).
|
||||
* Nothing else ever decrements w->resvals[idx] as
|
||||
* jobs run (core_apply_worker_resvals() only
|
||||
* overwrites it wholesale on a WORKERS_PUSH/
|
||||
* RESVALS_DELTA re-announce, and built-in load
|
||||
* indices -- resource_sync.c's
|
||||
* init_builtin_load_indices() -- never include
|
||||
* "slots"). Printing avail+used therefore
|
||||
* double-counts `used` on top of a value that is
|
||||
* ALREADY the total, growing without bound as more
|
||||
* jobs run -- live-reproduced and matching the
|
||||
* user's exact reported numbers (value=44.00
|
||||
* used=22.00 on a 22-slot worker at full
|
||||
* occupancy: 22 (never-decremented total) + 22
|
||||
* (used) = 44). Print avail_v as-is; NOT
|
||||
* units_mode-aware (this section's own doc comment
|
||||
* above explains why). */
|
||||
double avail = avail_v->u.fvalue;
|
||||
double used = ((used_v != NULL) &&
|
||||
(used_v->which == Value_u_fvalue))
|
||||
? used_v->u.fvalue
|
||||
: 0.0;
|
||||
|
||||
fprintf(stdout,
|
||||
"[sim] resource %s: value=%.2f used=%.2f\n",
|
||||
rname, avail, used);
|
||||
} else {
|
||||
char valbuf[80];
|
||||
|
||||
repl_format_numeric_resource_value(
|
||||
units_mode, rname, avail_v->u.fvalue, valbuf,
|
||||
sizeof(valbuf));
|
||||
fprintf(stdout, "[sim] resource %s: value=%s\n",
|
||||
rname, valbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ivalue32/uvalue32/ivalue64/uvalue64 -- not a shape
|
||||
* this codebase's resource system currently produces
|
||||
* for a resval (xce_resource_t.valtype, xce/types.h,
|
||||
* only ever numeric/string/boolean); reported honestly
|
||||
* rather than guessed at, same "silently skipped ...
|
||||
* rather than guessed at" posture core_events.c's
|
||||
* bug-1573 fix already established for this identical
|
||||
* ambiguity -- repl_classify_worker_resval_section()
|
||||
* above already routed this case to SECTION_NONE, so
|
||||
* this branch is unreachable in practice; kept for
|
||||
* defensive completeness. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* bug-1590: xce_val_t.which (a Value_u_which wire tag,
|
||||
* controller.capnp.h) tells us the value's REAL type -- bug-1568
|
||||
* fixed comparing it against the wrong enum for the numeric case,
|
||||
* but left every non-numeric resource (Value_u_svalue/
|
||||
* Value_u_bvalue -- e.g. this worker's `type`/`model` string
|
||||
* resources, or any boolean-typed resource) silently coerced to
|
||||
* 0.0 by the old unconditional %.2f formatting below. A value/used
|
||||
* split is only meaningful for a CONSUMABLE numeric resource -- a
|
||||
* string/boolean resource, or a non-consumable numeric one (a
|
||||
* static per-worker attribute nothing ever allocates against), gets
|
||||
* its own single "value=" line instead. */
|
||||
switch (avail_v->which) {
|
||||
case Value_u_svalue:
|
||||
fprintf(stdout, "[sim] resource %s: value=%s\n", rname,
|
||||
avail_v->u.svalue ? avail_v->u.svalue : "(null)");
|
||||
break;
|
||||
|
||||
case Value_u_bvalue:
|
||||
fprintf(stdout, "[sim] resource %s: value=%s\n", rname,
|
||||
avail_v->u.bvalue ? "true" : "false");
|
||||
break;
|
||||
|
||||
case Value_u_fvalue:
|
||||
if ((rdef != NULL) && (rdef->flags & XCE_RESF_CONSUMABLE)) {
|
||||
/* bug-1727: w->resvals[idx] (avail_v here) is the resource's
|
||||
* STATIC TOTAL capacity, not a live "remaining available"
|
||||
* quantity that decreases as jobs consume it -- confirmed
|
||||
* against the actual admission-control formula this exact
|
||||
* field feeds, resource.c's unsatisfy_(): `total[i] <
|
||||
* inuse[i] + uncomitted[i] + wanted[i]` treats
|
||||
* get_resvals(&w->resvals, ...)'s output as `total` directly,
|
||||
* comparing it against inuse (not total-inuse already
|
||||
* subtracted from avail_v). Nothing else ever decrements
|
||||
* w->resvals[idx] as jobs run (core_apply_worker_resvals()
|
||||
* only overwrites it wholesale on a WORKERS_PUSH/RESVALS_
|
||||
* DELTA re-announce, and built-in load indices --
|
||||
* resource_sync.c's init_builtin_load_indices() -- never
|
||||
* include "slots"). Printing avail+used therefore double-
|
||||
* counts `used` on top of a value that is ALREADY the
|
||||
* total, growing without bound as more jobs run --
|
||||
* live-reproduced and matching the user's exact reported
|
||||
* numbers (value=44.00 used=22.00 on a 22-slot worker at
|
||||
* full occupancy: 22 (never-decremented total) + 22 (used)
|
||||
* = 44). Print avail_v as-is. */
|
||||
double avail = avail_v->u.fvalue;
|
||||
double used = ((used_v != NULL) &&
|
||||
(used_v->which == Value_u_fvalue))
|
||||
? used_v->u.fvalue
|
||||
: 0.0;
|
||||
|
||||
fprintf(stdout,
|
||||
"[sim] resource %s: value=%.2f used=%.2f\n", rname,
|
||||
avail, used);
|
||||
} else {
|
||||
fprintf(stdout, "[sim] resource %s: value=%.2f\n", rname,
|
||||
avail_v->u.fvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ivalue32/uvalue32/ivalue64/uvalue64 -- not a shape this
|
||||
* codebase's resource system currently produces for a resval
|
||||
* (xce_resource_t.valtype, xce/types.h, only ever numeric/
|
||||
* string/boolean); reported honestly rather than guessed at,
|
||||
* same "silently skipped ... rather than guessed at" posture
|
||||
* core_events.c's bug-1573 fix already established for this
|
||||
* identical ambiguity. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(res_arr);
|
||||
|
||||
job = NULL;
|
||||
rc = iter_make(&it, sim->core.jobs);
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
rc = iter_next(&it, (void**)&job);
|
||||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
|
||||
break;
|
||||
}
|
||||
if (job->allocs == NULL) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < job->allocs->nhosts; i++) {
|
||||
xce_host_alloc_t* ha = job->allocs->allocs[i];
|
||||
/*
|
||||
* Requirement 4.5: the assigned-job listing adopts the SAME
|
||||
* jobid-range-compression helper (repl_fmt_jobid_range(), repl_fmt.c,
|
||||
* task 1.2) repl_break.c's JOB_PUSH event echo and the Round Rollup's
|
||||
* pend-jobid display already use, instead of printing one "assigned
|
||||
* job: jobid=..." line per matching job.
|
||||
*/
|
||||
{
|
||||
uint64_t* assigned_jobids = NULL;
|
||||
int n_assigned = 0;
|
||||
|
||||
if ((ha != NULL) && (ha->host != NULL)
|
||||
&& (strcmp(ha->host, w->name) == 0)) {
|
||||
char jobidStr[64];
|
||||
|
||||
/* bug-1591: fmt_jobid() renders the raw idgen()-packed
|
||||
* jobid in the same human-readable form already used by
|
||||
* every other jobid display in this codebase (scheduler.c/
|
||||
* handler.c/plugins, worker, xce_test's waldump, and
|
||||
* job.c's log lines since bug-1588) -- this REPL command
|
||||
* was one of the few remaining raw-integer holdouts. */
|
||||
fmt_jobid(jobidStr, sizeof(jobidStr), job->jobid);
|
||||
fprintf(stdout, "[sim] assigned job: jobid=%s\n", jobidStr);
|
||||
job = NULL;
|
||||
rc = iter_make(&it, sim->core.jobs);
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
rc = iter_next(&it, (void**)&job);
|
||||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
|
||||
break;
|
||||
}
|
||||
if (job->allocs == NULL) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < job->allocs->nhosts; i++) {
|
||||
xce_host_alloc_t* ha = job->allocs->allocs[i];
|
||||
|
||||
if ((ha != NULL) && (ha->host != NULL)
|
||||
&& (strcmp(ha->host, w->name) == 0)) {
|
||||
(void)repl_append_u64(&assigned_jobids, &n_assigned,
|
||||
job->jobid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter_destroy(&it);
|
||||
|
||||
if (n_assigned > 0) {
|
||||
/* design.md's Error Handling: a too-small compression buffer
|
||||
* falls back to the pre-existing uncompressed per-id listing,
|
||||
* same fallback repl_print_event_summary() (repl_break.c)
|
||||
* already establishes for its own JOB_PUSH listing. */
|
||||
char jobids_buf[1024];
|
||||
int jr_rc = repl_fmt_jobid_range(jobids_buf, sizeof(jobids_buf),
|
||||
assigned_jobids, n_assigned);
|
||||
|
||||
if (jr_rc >= 0) {
|
||||
fprintf(stdout, "[sim] assigned jobs: %s\n", jobids_buf);
|
||||
} else {
|
||||
/* bug-1591: fmt_jobid() renders the raw idgen()-packed
|
||||
* jobid in the same human-readable form already used by
|
||||
* every other jobid display in this codebase
|
||||
* (scheduler.c/handler.c/plugins, worker, xce_test's
|
||||
* waldump, and job.c's log lines since bug-1588). */
|
||||
for (i = 0; i < n_assigned; i++) {
|
||||
char jobidStr[64];
|
||||
|
||||
fmt_jobid(jobidStr, sizeof(jobidStr), assigned_jobids[i]);
|
||||
fprintf(stdout, "[sim] assigned job: jobid=%s\n",
|
||||
jobidStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(assigned_jobids);
|
||||
}
|
||||
iter_destroy(&it);
|
||||
}
|
||||
|
||||
/* repl_info_worker - `info worker <name|all>` dispatch, same "all" vs.
|
||||
|
|
@ -665,7 +918,16 @@ static void repl_info_resource_one(xce_sched_sim_driver_t* sim,
|
|||
fprintf(stdout, "[sim] value=%.2f used=%.2f\n",
|
||||
v->u.fvalue, used);
|
||||
} else {
|
||||
fprintf(stdout, "[sim] value=%.2f\n", v->u.fvalue);
|
||||
/* Requirement 6.1/6.2/6.5: same Load Metrics rendering
|
||||
* repl_info_worker_one() uses -- units_mode-aware for a
|
||||
* byte/megabyte-valued resource, whole-number-omits-.00
|
||||
* otherwise. */
|
||||
char valbuf[80];
|
||||
|
||||
repl_format_numeric_resource_value(
|
||||
repl_current_units_mode(sim), r->name, v->u.fvalue,
|
||||
valbuf, sizeof(valbuf));
|
||||
fprintf(stdout, "[sim] value=%s\n", valbuf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -688,26 +950,47 @@ static void repl_info_resource_one(xce_sched_sim_driver_t* sim,
|
|||
static void repl_info_resource(xce_sched_sim_driver_t* sim, const char* arg)
|
||||
{
|
||||
if (strcmp(arg, "all") == 0) {
|
||||
iterator_t it;
|
||||
xce_resource_t* r = NULL;
|
||||
int n_shown = 0;
|
||||
int rc = iter_make(&it, sim->core.resmgr);
|
||||
int n_shown = 0;
|
||||
int s;
|
||||
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
rc = iter_next(&it, (void**)&r);
|
||||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
|
||||
break;
|
||||
}
|
||||
if ((r->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) {
|
||||
/* Requirement 6.6: group the "all" listing into the same three
|
||||
* labeled sections info worker's own version uses (repl_classify_
|
||||
* resource_def_section() above), in the same fixed order -- one
|
||||
* full resmgr pass per section, printing that section's header
|
||||
* once, only if it has at least one GLOBAL/SHARED resource to show
|
||||
* (bug-1593's own scope restriction may leave a section genuinely
|
||||
* empty for a given replay). */
|
||||
for (s = 0; s < XCE_REPL_RES_SECTION_COUNT; s++) {
|
||||
iterator_t it;
|
||||
xce_resource_t* r = NULL;
|
||||
bool section_header_printed = false;
|
||||
int rc = iter_make(&it, sim->core.resmgr);
|
||||
|
||||
if (rc < 0) {
|
||||
continue;
|
||||
}
|
||||
repl_info_resource_one(sim, r);
|
||||
n_shown++;
|
||||
for (;;) {
|
||||
rc = iter_next(&it, (void**)&r);
|
||||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
|
||||
break;
|
||||
}
|
||||
if ((r->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (repl_classify_resource_def_section(r)
|
||||
!= XCE_REPL_RES_SECTION_TABLE[s].section) {
|
||||
continue;
|
||||
}
|
||||
if (!section_header_printed) {
|
||||
fprintf(stdout, "[sim] %s\n",
|
||||
XCE_REPL_RES_SECTION_TABLE[s].header);
|
||||
section_header_printed = true;
|
||||
}
|
||||
repl_info_resource_one(sim, r);
|
||||
n_shown++;
|
||||
}
|
||||
iter_destroy(&it);
|
||||
}
|
||||
iter_destroy(&it);
|
||||
|
||||
/* Every resource in this session was host-based (e.g. only 'slots'/
|
||||
* 'mem'/'cpus'-style consumables, no GLOBAL/SHARED resource at
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,7 @@ static void test_info_worker_shows_all_populated_resources_when_count_is_stale(
|
|||
strstr(captured, "resource mem: value=100.00"),
|
||||
"the first populated slot must still display");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(captured, "resource r1m: value=5.00"),
|
||||
strstr(captured, "resource r1m: value=5"),
|
||||
"a populated slot at an index >= the (stale) .count must still "
|
||||
"display -- iterating by .count instead of .caps (bug-1688) "
|
||||
"silently truncates output here");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue