feat(scheduler-sim-diagnostics): promote byte-unit formatting to shared lib (task 7)

Adds Requirement 6 (Human-Readable Units for Byte-Valued Limit Amounts)
to the spec: requirements, design, research, and tasks 7-10.

Task 7 (Foundation): promotes the REPL's byte-unit scaling algorithm
and byte-valued-resource classifier into src/lib/utils.c/utils.h
(xce_format_bytes_human, xce_is_bytes_valued_resource), alongside a
new display-mode-aware composer (xce_format_units_value,
xce_units_mode_t) -- following the fmt_jobid()/fmt_jobid_ex()
precedent. repl_fmt.c's scheduler-only functions become one-line
delegating wrappers. repl_info.c's repl_format_numeric_resource_value()
(shared by info worker/info resource, not just the upcoming info
limit) is retyped to delegate to the new shared composer via a small
translate_units_mode() bridge, so every consumer benefits from one
implementation going forward.

No existing symbol's signature changed; only new symbols were added.
Confirmed behavior-preserving: full scheduler suite 85/85 unchanged,
including info worker/info resource's own regression tests. One
narrow, untested, out-of-requirement-scope corner case found and
documented (swp's RAW/BOTH mode rendering under the new single-value
composer interface) -- logged in tasks.md's Implementation Notes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-20 04:40:44 +00:00
parent 8c363de75f
commit eb23c158ae
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
10 changed files with 535 additions and 85 deletions

View file

@ -72,69 +72,26 @@ void repl_format_ts_human(uint64_t ns, char* buf, size_t bufsz)
}
/*
* repl_is_bytes_valued_resource - scheduler-sim-repl-verbosity spec, task
* 7.2, design.md's "Resource Value Formatter" component (Req 6.1). Mirrors
* src/controller/testsuite/test.c's own is_bytes_valued_resource() exactly
* -- same 5-name list, same "small, explicit, client-side list rather than
* a wire field" rationale that file's own doc comment gives: mem/tmp's
* canonical wire unit is bytes (node_information.c's node_info_get_mem()/
* node_info_get_tmp() doc comments); maxmem/maxtmp/maxswap's canonical wire
* unit is bytes too (xcecd.c's init_host_attrs() bug-1554 comment). `swp`
* is deliberately NOT in this list -- its own wire unit is megabytes, not
* bytes (node_info_get_swap() doc comment); see repl_format_bytes_human()'s
* own doc comment below and repl_params.c's `set units` caller-side
* MB->bytes conversion.
* repl_is_bytes_valued_resource / repl_format_bytes_human -
* scheduler-sim-repl-verbosity spec, task 7.2, design.md's "Resource Value
* Formatter" component (Req 6.1/6.2). scheduler-sim-diagnostics spec, task
* 7 (Req 6.1, 6.3, 6.5) promoted both functions' bodies verbatim into the
* shared library (`xce_is_bytes_valued_resource()`/`xce_format_bytes_human()`,
* `utils.h`/`utils.c`), so any `libxce` consumer -- not only the
* scheduler's REPL -- can reach this display-only formatting. These two
* remain in place as thin, behavior-preserving wrappers: every existing
* call site in this file family keeps calling them by these exact names/
* signatures, unchanged. See utils.h's doc comment on xce_units_mode_t for
* the full algorithm/classification-list contract now owned there.
*/
bool repl_is_bytes_valued_resource(const char* name)
{
static const char* const BYTES_VALUED_RESOURCES[]
= { "mem", "tmp", "maxmem", "maxtmp", "maxswap" };
const int nres = (int)(sizeof(BYTES_VALUED_RESOURCES)
/ sizeof(BYTES_VALUED_RESOURCES[0]));
int i;
if (name == NULL) {
return false;
}
for (i = 0; i < nres; i++) {
if (strcmp(name, BYTES_VALUED_RESOURCES[i]) == 0) {
return true;
}
}
return false;
return xce_is_bytes_valued_resource(name);
}
/*
* repl_format_bytes_human - scheduler-sim-repl-verbosity spec, task 7.2,
* design.md's "Resource Value Formatter" component (Req 6.2). Adaptively
* scales `bytes` to the smallest B/KB/MB/GB/TB/PB unit it fits (dividing by
* 1024 while >= 1024.0, capping at PB once no larger unit remains), writing
* e.g. "20.56 GB" into `buf` (bufsz-bounded) -- mirrors
* src/controller/testsuite/test.c's own format_bytes_human() algorithm
* exactly, same "%.2f %s" render format. Display-only -- never touches the
* underlying stored/wire value, same discipline that file's own doc comment
* states ("the stored/wire value stays raw bytes always, this never feeds
* back into anything"). `swp`'s wire value is megabytes, not bytes
* (node_info_get_swap() doc comment, repl_is_bytes_valued_resource() above)
* -- callers convert MB->bytes (x 1024.0 x 1024.0) BEFORE calling this, so
* it renders on the same adaptive scale a byte-native resource of
* equivalent magnitude would (repl_params.c).
*/
void repl_format_bytes_human(double bytes, char* buf, size_t bufsz)
{
static const char* const UNITS[] = { "B", "KB", "MB", "GB", "TB", "PB" };
const int nunits = (int)(sizeof(UNITS) / sizeof(UNITS[0]));
double val = bytes;
int i = 0;
while ((val >= 1024.0) && (i < nunits - 1)) {
val /= 1024.0;
i++;
}
snprintf(buf, bufsz, "%.2f %s", val, UNITS[i]);
xce_format_bytes_human(bytes, buf, bufsz);
}
/*

View file

@ -369,6 +369,32 @@ repl_classify_resource_def_section(const xce_resource_t* r)
: XCE_REPL_RES_SECTION_LOAD;
}
/*
* translate_units_mode - scheduler-sim-diagnostics spec, task 7 (Req 6.1,
* 6.3, 6.5): bridges the REPL's own units-mode selection
* (xce_repl_units_mode_t, repl_internal.h) to the shared library's
* independently-defined xce_units_mode_t (utils.h) at the one seam that
* needs to cross between them. Deliberately NOT a shared enum -- a
* src/lib type must not be defined in terms of a scheduler-internal
* concept, nor should the scheduler's own already-tested
* xce_repl_units_mode_t plumbing need to change for this (design.md's
* "Where the byte-formatter should live" decision). If
* xce_repl_units_mode_t's three values ever change, this mapping must be
* re-checked by hand -- nothing enforces it mechanically.
*/
static xce_units_mode_t translate_units_mode(xce_repl_units_mode_t mode)
{
switch (mode) {
case XCE_REPL_UNITS_RAW:
return XCE_UNITS_RAW;
case XCE_REPL_UNITS_HUMAN:
return XCE_UNITS_HUMAN;
case XCE_REPL_UNITS_BOTH:
default:
return XCE_UNITS_BOTH;
}
}
/*
* 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
@ -383,6 +409,13 @@ repl_classify_resource_def_section(const xce_resource_t* r)
* 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.
*
* scheduler-sim-diagnostics spec, task 7 (Req 6.1, 6.3, 6.5): the
* byte-valued branch now delegates its raw/human/both composition to the
* shared library's xce_format_units_value() via translate_units_mode()
* above, instead of switching on `mode` itself -- so this function, its
* `info worker`/`info resource` callers, AND the `info limit` command
* this spec is adding all funnel through one single implementation.
*/
static void repl_format_numeric_resource_value(xce_repl_units_mode_t mode,
const char* rname,
@ -393,22 +426,9 @@ static void repl_format_numeric_resource_value(xce_repl_units_mode_t mode,
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;
}
xce_format_units_value(translate_units_mode(mode), bytes_value, buf,
bufsz);
return;
}