From 5ee1d6cb08900c868b6a40521554c0a481acd144 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Wed, 2 Sep 2026 17:45:06 +0000 Subject: [PATCH] feat(scheduler-sim-repl-verbosity): task 7.2 - implement resource formatting helpers Implements repl_is_bytes_valued_resource()/repl_format_bytes_human() (mirroring xce_test's confirmed is_bytes_valued_resource()/ format_bytes_human() precedent exactly) and the units_mode setting (explicitly defaulted to both, not left to zero-init) wired into set units raw|human|both alongside the existing set speed/output/verify branches (Requirements 6.1-6.3). All 8 of task 7.1's tests pass. Split repl_format_ts_human(), the Jobid Range Compressor (task 1.2), and these two new helpers out of repl.c into a new repl_fmt.c: adding them in place pushed repl.c to 929 lines (16.1% over the 800-line cap), and unlike task 6.2's single-function split attempt (which didn't even achieve compliance), bundling these three pure-formatter groups together does - repl.c settles at 587 lines, repl_fmt.c at 402, both comfortably under cap. Required a matching #include line in the 20 test files that white-box #include repl.c, plus extending test_sim_isolation.c's forbidden-include/symbol scan to cover the new file, following the repl_rollup.c split precedent from task 2.2. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YAWMhfRJFnmmnG8h97yUDd --- .../scheduler-sim-repl-verbosity/design.md | 23 +- .../scheduler-sim-repl-verbosity/tasks.md | 2 +- src/scheduler/meson.build | 21 +- src/scheduler/src/repl.c | 299 +------------ src/scheduler/src/repl_fmt.c | 402 ++++++++++++++++++ src/scheduler/src/repl_internal.h | 108 ++++- src/scheduler/src/repl_params.c | 57 ++- src/scheduler/testsuite/meson.build | 3 + .../test_repl_breakpoints_inspection.c | 1 + .../testsuite/test_repl_equivalence.c | 1 + .../testsuite/test_repl_event_echo.c | 1 + .../testsuite/test_repl_execution_control.c | 1 + .../testsuite/test_repl_prompt_context.c | 1 + .../testsuite/test_repl_round_rollup.c | 1 + .../testsuite/test_repl_snapshot_params.c | 1 + src/scheduler/testsuite/test_sim_alt_events.c | 1 + .../testsuite/test_sim_contention_parity.c | 1 + .../test_sim_e2e_original_motivation.c | 1 + .../testsuite/test_sim_equivalence.c | 1 + src/scheduler/testsuite/test_sim_event_loop.c | 1 + src/scheduler/testsuite/test_sim_isolation.c | 16 + .../testsuite/test_sim_print_decision_gate.c | 1 + .../test_sim_reconcile_unaccounted.c | 1 + .../testsuite/test_sim_record_output.c | 1 + .../testsuite/test_sim_resource_contention.c | 1 + .../test_sim_round_boundary_reconciliation.c | 1 + .../testsuite/test_sim_session_ordering.c | 1 + .../test_sim_snapshot_reconcile_gap.c | 1 + src/scheduler/testsuite/test_sim_verify.c | 1 + 29 files changed, 634 insertions(+), 317 deletions(-) create mode 100644 src/scheduler/src/repl_fmt.c diff --git a/.kiro/specs/scheduler-sim-repl-verbosity/design.md b/.kiro/specs/scheduler-sim-repl-verbosity/design.md index ed332407..f58c2546 100644 --- a/.kiro/specs/scheduler-sim-repl-verbosity/design.md +++ b/.kiro/specs/scheduler-sim-repl-verbosity/design.md @@ -44,9 +44,9 @@ ### Allowed Dependencies - Existing `xce_sched_repl_state_s` fields (`mode`, `last_n_dispatch`/`last_n_pend`/`last_n_preempt`, `session_count`) — repl_internal.h. - The existing `core->jobs` iteration idiom (`iter_make`/`iter_next`, `JOB_IS_PEND`) already used by `repl_info_jobs()`. -- Existing helpers `fmt_jobid_ex()` (lib/utils.c) and `repl_format_ts_human()` (repl.c). +- Existing helpers `fmt_jobid_ex()` (lib/utils.c) and `repl_format_ts_human()` (`repl_fmt.c` post-review, task 7.2 Correction below — originally `repl.c`). - Must not add a new call site to `sim_loop.c`'s main loop beyond gating the two calls that already exist there (`sim_print_decision`, the `xce_repl_check_event` wrapper) — that file is documented at its line-budget ceiling (repl.h's own header comment). -- `xce_sched_repl_state_s.session_count` and `xce_core_now()`/`repl_format_ts_human()` (repl.c) for the prompt's session/timestamp content — no new state needed for that part. +- `xce_sched_repl_state_s.session_count` and `xce_core_now()`/`repl_format_ts_human()` (`repl_fmt.c` post-review, task 7.2 Correction below — originally `repl.c`) for the prompt's session/timestamp content — no new state needed for that part. - `repl_break.c`'s existing `BP_KIND_TABLE` (currently `static`/file-local) — exposed via one new shared accessor declared in `repl_internal.h`, rather than duplicated as a second hardcoded list, so `help` and the unrecognized-event-type error can never drift out of sync with each other or with the table itself. - The existing `repl_command_loop()` call site's `readline(XCE_REPL_PROMPT)` (repl.c:439) — this feature makes the prompt argument dynamic in place; no new call site. - `src/controller/testsuite/test.c`'s `is_bytes_valued_resource()`/`format_bytes_human()` as a design *precedent* only (same classification list, same adaptive B/KB/MB/GB/TB/PB algorithm) — not a shared symbol, since `xce_test` and `xce_scheduler` are separate binaries; the scheduler gets its own `repl_*`-named port of the same logic. @@ -88,7 +88,7 @@ flowchart TD **Architecture Integration**: - Selected pattern: a state accumulator attached to the existing round-boundary hook (`xce_repl_post_session_check`), exposing one shared flush entry point that `repl_break.c`'s event/breakpoint/decision hooks and `repl.c`'s replay-completion hook all call — no new hook call sites added to `sim_loop.c`. - Domain/feature boundaries: the Round Rollup's state and flush/print logic live in `repl_rollup.c` (split out of `repl.c` post-review — see Correction below); `repl_break.c` calls the flush entry point but does not own or mutate the collapse state directly; `sim_loop.c` only gains a mode gate on its two existing calls. -- Existing patterns preserved: `repl.h`'s `sim->interactive`-gated inline-wrapper convention; `repl.c`'s shared-helper convention (`repl_format_ts_human`, `repl_split_tokens`) extended with two new shared helpers (range compression, staying in `repl.c`; and the round-rollup flush entry point, now implemented in `repl_rollup.c` per the Correction below, declared in `repl_internal.h` for `repl.c`/`repl_break.c` to call). +- Existing patterns preserved: `repl.h`'s `sim->interactive`-gated inline-wrapper convention; `repl.c`'s shared-helper convention (`repl_format_ts_human`, `repl_split_tokens`) extended with new shared helpers (range compression and the resource-formatting helpers, now in `repl_fmt.c` per the task 7.2 Correction below; and the round-rollup flush entry point, now implemented in `repl_rollup.c` per the task 2.2 Correction below), declared in `repl_internal.h` for `repl.c`/`repl_break.c`/`repl_info.c`/`repl_params.c` to call. - New components rationale: deciding "print now vs. extend silently" requires comparing against the *previous* round's fingerprint, which must persist across separate `xce_repl_post_session_check()` invocations — hence new persistent fields on `xce_sched_repl_state_s` rather than function-local state. The flush entry point exists because three independent call sites (round boundary, breakpoint hit, replay completion) across two files all need to end an active range the same way. - Steering compliance: one domain per file preserved; `snake_case`/`repl_*` naming conventions followed throughout. **Correction (post-review, task 2.2, supersedes "no new production `.c` file needed" above)**: task 2.2's first pass kept the Round Rollup entirely inside `repl.c`, per this bullet's original text. That pushed `repl.c` to 1160 lines — 45% over this project's 800-line-per-file cap (`src/scheduler/CLAUDE.md`) — while review confirmed the logic itself was correct. The Round Rollup's fingerprint-build/pend-jobid-query/emit/flush/round-process functions (`repl_jobid_u64_cmp` through `repl_round_rollup_process`) now live in a new `src/scheduler/src/repl_rollup.c`, following the exact domain-split precedent this same file family already established for `repl_break.c`/`repl_info.c`/`repl_params.c` (`repl_internal.h`'s own "Why a split at all" section) — one more sibling sharing state via `repl_internal.h`, not a departure from that pattern. `repl.c`'s `repl_count_jobs()` becomes non-static so `repl_rollup.c`'s pend-jobid query can call it; both it and `repl_round_rollup_process()` gain `extern` declarations in `repl_internal.h`. `repl.c`'s own wiring calls (`xce_repl_post_session_check()`/`xce_repl_on_replay_complete()`) are unchanged. Tasks 6 (REPL Prompt Context) and 9 (Help & Breakpoint Discoverability/`BP_KIND_TABLE`) remain correctly targeted at `repl.c` — different domains (prompt construction, help/breakpoint-table rendering) with no Round Rollup logic of their own, unaffected by this correction. @@ -104,10 +104,13 @@ flowchart TD **Correction (post-review, task 2.2)**: keeping the Round Rollup's logic inside `repl.c`, as the original `repl.c`/`repl_internal.h` entries below described, pushed that file to 1160 lines — 45% over this project's 800-line cap (`src/scheduler/CLAUDE.md`) — even though review confirmed the logic itself was correct. The Round Rollup's fingerprint/query/emit/flush/process functions now live in a new `src/scheduler/src/repl_rollup.c` instead, matching this exact file family's established domain-split precedent (`repl_break.c`/`repl_info.c`/`repl_params.c` already split out of `repl.c` by domain, per `repl_internal.h`'s own "Why a split at all" section) rather than growing `repl.c` further. The `repl.c` and `repl_internal.h` entries below, and the new `repl_rollup.c` entry, reflect this. Tasks 6 (REPL Prompt Context) and 9 (Help & Breakpoint Discoverability) are unaffected — both remain correctly targeted at `repl.c`, a different domain with no Round Rollup logic of its own. **Accepted residual (post-review, task 2.2)**: after the split, `repl.c` settled at 808 lines — 1% over the 800-line cap, down from the original 1160-line (45% over) violation. Verified empirically (not assumed): trimming the two doc comments this task added to their practical minimum recovers only 9 of the needed 17+ lines, and deleting both entirely would still leave 801. Getting strictly under 800 would require another file split for single-digit lines, which is disproportionate churn given this codebase already tolerates comparable or larger legacy exceptions (`scheduler.c` at 1382 lines, `plugins/builtin_fcfs.c` at 1026 lines, both noted in `src/scheduler/CLAUDE.md` as "do not use... as precedent for new files" but tolerated in place). 808 lines is accepted as this task's final state; if tasks 6/9's later `repl.c` additions push it meaningfully further over, revisit with a fresh split decision at that point rather than trimming comments further now. -- `src/scheduler/src/repl_internal.h` — extend `xce_sched_repl_state_s` with round-collapse state fields (see Data Models), a new `units_mode` field (Requirement 6), and declare the new shared jobid-range-compression helper, resource-formatting helpers, and the `BP_KIND_TABLE` accessor (Requirement 7); post-review (Correction above), also declares `repl_count_jobs()` and `repl_round_rollup_process()` `extern` so `repl_rollup.c` can call the former and `repl.c` can call the latter. + +**Correction (post-review, task 7.2)**: task 7.2's Resource Value Formatter helpers (`repl_is_bytes_valued_resource()`/`repl_format_bytes_human()`) landed directly in `repl.c`, exactly as this section's original text directed, pushing it to 929 lines — 16.1% over the 800-line cap (`src/scheduler/CLAUDE.md`) — while review confirmed the logic itself was correct. Unlike task 6.2's `repl_build_prompt()`/`repl_prompt.c` extraction attempt (tried and reverted — see the REPL Prompt Context component's own "Accepted residual" below — because even after absorbing 19 test files' worth of new `#include` churn, `repl.c` still only dropped to 814 lines, still over cap, for a 15-line function), this remediation moves a materially larger, genuinely cohesive block: `repl_format_ts_human()` (pre-existing, bug-1587), the entire Jobid Range Compressor (`xce_repl_jobid_group_t`/`repl_jobid_groups_free`/`repl_jobid_group_add_idx`/`repl_jobid_idx_cmp`/`repl_jr_appendf`/`repl_fmt_jobid_range`, task 1.2), and this task's `repl_is_bytes_valued_resource()`/`repl_format_bytes_human()` — three groups of pure, read-only formatting helpers this file family's own shared-helper convention already treated as a natural unit (none of them depend on `xce_sched_sim_driver_t`/`xce_sched_core_t` or any REPL session state) — into a new `src/scheduler/src/repl_fmt.c`, following the same `repl_rollup.c` domain-split precedent (task 2.2's Correction above). This achieves genuine compliance: `repl.c` settles at 587 lines, well under the 800-line cap, and `repl_fmt.c` itself is a self-contained 402-line file. `repl_internal.h`'s existing `extern` declarations for all of these functions are unchanged (only their doc comments now note the new defining file) — only which `.c` file DEFINES them changes, so every caller elsewhere in `repl.c`/`repl_break.c`/`repl_info.c`/`repl_params.c` keeps working unmodified. Added to both `scheduler_srcs` and `sched_sim_isolation_lib`'s source list in `src/scheduler/meson.build`, immediately after `repl.c` (matching `repl_rollup.c`'s own placement); every white-box test file that `#include`s `repl.c` directly (20 files, confirmed via `grep -rl '#include "\.\./src/repl\.c"' src/scheduler/testsuite/`) also gains a matching `#include "../src/repl_fmt.c"` line immediately after its existing `#include "../src/repl.c"`; `test_sim_isolation.c` gains a matching `XCE_SIM_REPL_FMT_C_PATH`-driven read/strip/assert/free block in both `test_sim_never_includes_forbidden_headers` and `test_sim_never_calls_forbidden_symbols`, mirroring the existing `repl_rollup_c`/`repl_params_c` pattern (`repl_rollup.c` was already covered by this scan since task 2.2's own commit; the only gap found was in the separate `test_sim_source_paths_are_readable` diagnostic sanity-check function, which does not affect actual isolation coverage and was left as-is). `sched_repl_output_compression_test`/`sched_repl_resource_formatting_test` (tasks 1.1/7.1) need no `#include` change: per bug-1762's fix, both already link the real `scheduler_srcs` object list rather than white-box `#include`-ing `repl.c`, so they pick up `repl_fmt.c`'s new definitions automatically once it is added to `scheduler_srcs`. +- `src/scheduler/src/repl_internal.h` — extend `xce_sched_repl_state_s` with round-collapse state fields (see Data Models), a new `units_mode` field (Requirement 6), and declare the new shared jobid-range-compression helper, resource-formatting helpers, and the `BP_KIND_TABLE` accessor (Requirement 7); post-review (Correction above), also declares `repl_count_jobs()` and `repl_round_rollup_process()` `extern` so `repl_rollup.c` can call the former and `repl.c` can call the latter. Post-review (task 7.2 Correction above): the `extern` declarations for `repl_format_ts_human()`/`repl_fmt_jobid_range()`/`repl_is_bytes_valued_resource()`/`repl_format_bytes_human()` are unchanged in shape or signature — only their doc comments were updated to point at `repl_fmt.c` as the new defining file instead of `repl.c`. - `src/scheduler/src/repl_break.c` — `xce_repl_on_event()` gains routine-vs-structural event classification gated on `rs->mode` (Requirement 1); `repl_print_event_summary()`'s `JOB_PUSH`-style cases adopt the jobid-range-compression helper (Requirement 4); `xce_repl_on_event()`, `xce_repl_on_begin_round()`, and `xce_repl_on_decision()` each call the Round Rollup's flush entry point before printing a structural-event line or before re-entering `repl_command_loop()` on a breakpoint hit (Requirement 3.5, 3.6); `repl_cmd_break()`'s unrecognized-event-type error gains the full valid-event-type listing, rendered via a new shared accessor over the existing `BP_KIND_TABLE` (Requirement 7.5); `repl_cmd_delete()` already fixed independently (bug-1761, `strtol`+endptr validation) — no further change needed here for this spec. -- `src/scheduler/src/repl.c` — `xce_repl_post_session_check()` calls the Round Rollup's round-boundary fingerprint/collapse logic, and `xce_repl_on_replay_complete()` calls its flush entry point before printing the existing replay-complete banner (Requirements 2, 3; post-review Correction above: the Round Rollup's own logic — fingerprint build, collapse, flush, emit — now lives in the new `repl_rollup.c` below, not here); implements the new shared jobid-range-compression helper (Requirement 4), following the file's existing shared-helper convention; `repl_command_loop()`'s `readline(XCE_REPL_PROMPT)` call gains a per-iteration dynamically-built prompt (Requirement 5.5); `repl_print_help()` is rewritten into labeled, consistently-aligned sections, `info config` moves next to the other `info` lines, and the `break` line grows the same `BP_KIND_TABLE`-derived event-type listing `repl_break.c`'s error message uses (Requirement 7). +- `src/scheduler/src/repl.c` — `xce_repl_post_session_check()` calls the Round Rollup's round-boundary fingerprint/collapse logic, and `xce_repl_on_replay_complete()` calls its flush entry point before printing the existing replay-complete banner (Requirements 2, 3; post-review Correction above: the Round Rollup's own logic — fingerprint build, collapse, flush, emit — now lives in the new `repl_rollup.c` below, not here); `repl_command_loop()`'s `readline(XCE_REPL_PROMPT)` call gains a per-iteration dynamically-built prompt (Requirement 5.5); `repl_print_help()` is rewritten into labeled, consistently-aligned sections, `info config` moves next to the other `info` lines, and the `break` line grows the same `BP_KIND_TABLE`-derived event-type listing `repl_break.c`'s error message uses (Requirement 7). **Correction (post-review, task 7.2, above)**: this bullet's original text also claimed repl.c "implements the new shared jobid-range-compression helper (Requirement 4), following the file's existing shared-helper convention" — that helper, `repl_format_ts_human()`, and this task's own `repl_is_bytes_valued_resource()`/`repl_format_bytes_human()` now live in the new `repl_fmt.c` below instead, not here. - `src/scheduler/src/repl_rollup.c` (new, post-review — Correction above) — the Round Rollup's own fingerprint-build/pend-jobid-query/emit/flush/round-boundary-process logic (Requirements 2, 3), split out of `repl.c` to stay under the 800-line cap; shares state with `repl.c` via `repl_internal.h`, matching `repl_break.c`/`repl_info.c`/`repl_params.c`'s established split; added to `scheduler_srcs` in `src/scheduler/meson.build` immediately after `repl.c`. +- `src/scheduler/src/repl_fmt.c` (new, post-review, task 7.2 — Correction above) — `repl_format_ts_human()` (pre-existing), the Jobid Range Compressor's `repl_fmt_jobid_range()` and its private grouping helpers (Requirement 4), and the Resource Value Formatter's `repl_is_bytes_valued_resource()`/`repl_format_bytes_human()` (Requirement 6.1, 6.2) — three groups of pure, read-only formatting helpers with zero dependency on sim/core/repl state, split out of `repl.c` to stay under the 800-line cap; shares declarations with `repl.c`/`repl_break.c`/`repl_info.c`/`repl_params.c` via `repl_internal.h` (whose existing `extern` entries for these functions are unchanged); added to both `scheduler_srcs` and `sched_sim_isolation_lib`'s source list in `src/scheduler/meson.build`, immediately after `repl.c`. - `src/scheduler/src/repl_info.c` — new resource-value classification/scaling helpers (byte-denominated list, `swp`'s megabyte special case, whole-number formatting) and a table/grouping rewrite of `repl_info_worker()`'s and `repl_info_resource()`'s resource-listing loops, both reading `rs->units_mode` (Requirement 6); the assigned-job listing in `repl_info_worker()` adopts the jobid-range-compression helper (Requirement 4.5). - `src/scheduler/src/repl_params.c` — `repl_cmd_set()` gains a `units raw|human|both` branch alongside the existing `speed`/`output`/`verify` branches, storing the selection on the new `rs->units_mode` field (Requirement 6.2, 6.3). - `src/scheduler/src/sim_loop.c` — the existing call site invoking `sim_print_decision()` gains a `continue`-mode gate, mirroring `repl.h`'s inline-wrapper convention (Requirements 2, 5). **Correction (post-review, task 4.2 remediation)**: this bullet's original "the existing call site" (singular) undercounted the file's actual production call sites — `sim_loop.c` has TWO direct calls to `sim_print_decision()`, `sched_sim_driver_run()`'s main decision-drain loop (this bullet's original target) AND `sim_reconcile_unaccounted_jobs()`'s reconstructed-decision report (added by the sibling scheduler-sim-decision-parity/scheduler-sim-eager-reconciliation specs, unrelated in authorship but real production code that reports DISPATCH decisions through this exact channel). Since that reconciliation path is a primary source of DISPATCH decisions surfacing in replay mode, leaving it unguarded reproduced bug-1610's own duplication there too. Both call sites now route through `sim_print_decision_gate()` — see the `sim_print_decision` Gate component's own Correction below for the updated Boundary Commitment. @@ -150,10 +153,10 @@ See the Architecture Pattern & Boundary Map diagram above — it is the only non |-----------|---------------|--------|---------------|---------------------------|-----------| | Event Echo Gate | REPL / repl_break.c | Classify each event as routine or structural and gate the per-event echo by mode | 1.1-1.4 | `rs->mode` (P0), Round Rollup flush entry point (P0) | State | | Round Rollup | REPL / repl_rollup.c (post-review; see Correction, was repl.c) | Build a per-round fingerprint, print it, or extend/flush a collapsed range; expose a flush entry point callable from breakpoint/structural-event/replay-complete sites | 2.1-2.4, 3.1-3.6 | `core->jobs` query (P0), existing decision tally (P0) | State, Service | -| Jobid Range Compressor | REPL / repl.c (shared helper) | Compress array-job identifier lists into index ranges | 4.1-4.5 | `fmt_jobid_ex` decomposition (P0) | Service | +| Jobid Range Compressor | REPL / repl_fmt.c (shared helper; post-review, task 7.2 — was repl.c, see Correction) | Compress array-job identifier lists into index ranges | 4.1-4.5 | `fmt_jobid_ex` decomposition (P0) | Service | | `sim_print_decision` gate | Sim driver / sim_loop.c | Suppress the direct per-decision print when the Round Rollup will report it instead | 2.3, 5.1 | REPL mode state (P0) | State | | REPL Prompt Context | REPL / repl.c | Build the `readline()` prompt string from the current session and timestamp, falling back to the plain prompt before any session begins | 5.5 | `rs->session_count` (P0), `xce_core_now`/`repl_format_ts_human` (P0) | Service | -| Resource Value Formatter | REPL / repl_info.c, repl_params.c | Classify a resource's canonical wire unit (bytes/megabytes/unitless), render it per the operator's `units_mode`, and lay out `info worker`/`info resource`'s listings as a grouped table | 6.1-6.6 | `is_bytes_valued_resource`/`format_bytes_human` (test.c) as design precedent (P0), `rs->units_mode` (P0) | Service, State | +| Resource Value Formatter | REPL / repl_info.c, repl_params.c (consumers; the two shared pure helpers themselves live in repl_fmt.c, post-review, task 7.2 — was repl.c, see Correction) | Classify a resource's canonical wire unit (bytes/megabytes/unitless), render it per the operator's `units_mode`, and lay out `info worker`/`info resource`'s listings as a grouped table | 6.1-6.6 | `is_bytes_valued_resource`/`format_bytes_human` (test.c) as design precedent (P0), `rs->units_mode` (P0) | Service, State | | Help & Breakpoint Discoverability | REPL / repl.c, repl_break.c | Group and align `help`'s listing, and expose `BP_KIND_TABLE`'s full valid-event-type set to both `help` and `break`'s error message via one shared accessor | 7.1-7.6 | `BP_KIND_TABLE` (P0) | Service | ### REPL Output (repl.c / repl_break.c / repl_info.c / repl_params.c / sim_loop.c) @@ -197,7 +200,7 @@ See the Architecture Pattern & Boundary Map diagram above — it is the only non **Dependencies** - Inbound: `xce_repl_post_session_check` (repl.c) — the round-boundary call site the fingerprint/collapse logic attaches to, calling `repl_rollup.c`'s `repl_round_rollup_process()` (P0); the Event Echo Gate (`xce_repl_on_event`) and the breakpoint checks in `xce_repl_on_begin_round`/`xce_repl_on_decision` (all repl_break.c) — call the flush entry point (P0); `xce_repl_on_replay_complete` (repl.c) — calls the flush entry point before its existing banner (P0). All three inbound call sites live in a different file from this component's own implementation (post-review, task 2.2 — see Correction above) and reach it via `repl_internal.h`'s `extern` declarations. -- Outbound: `core->jobs` iterator (P0), `repl.c`'s `repl_count_jobs()` (P0, `extern`-declared in `repl_internal.h` post-review for this cross-file call), `xce_repl_on_decision`'s existing tally fields (P0), Jobid Range Compressor (repl.c) for rendering the pend-jobid list (P0) +- Outbound: `core->jobs` iterator (P0), `repl.c`'s `repl_count_jobs()` (P0, `extern`-declared in `repl_internal.h` post-review for this cross-file call), `xce_repl_on_decision`'s existing tally fields (P0), Jobid Range Compressor (repl_fmt.c, post-review, task 7.2 — was repl.c, see File Structure Plan Correction) for rendering the pend-jobid list (P0) **Contracts**: Service [x] / API [ ] / Event [ ] / Batch [ ] / State [x] @@ -227,6 +230,7 @@ void repl_round_collapse_flush(xce_sched_sim_driver_t* sim); |-------|--------| | Intent | Group a list of jobids by array base and render contiguous index runs as compressed ranges | | Requirements | 4.1, 4.2, 4.3, 4.4, 4.5 | +| File | `src/scheduler/src/repl_fmt.c` (post-review, task 7.2 — was `repl.c`; see the File Structure Plan Correction above for why) | **Responsibilities & Constraints** - Pure, read-only formatting function — never mutates its input list or any scheduler/job state. @@ -289,7 +293,7 @@ int repl_fmt_jobid_range(char* buf, size_t bufsz, **Dependencies** - Inbound: `repl_command_loop()` (repl.c) — calls this immediately before its existing `readline(XCE_REPL_PROMPT)` line -- Outbound: `repl_format_ts_human()` (repl.c, existing), `xce_core_now()` (core_clock.c, existing) +- Outbound: `repl_format_ts_human()` (`repl_fmt.c` post-review, task 7.2 Correction above — originally `repl.c`), `xce_core_now()` (core_clock.c, existing) **Contracts**: Service [x] / API [ ] / Event [ ] / Batch [ ] / State [ ] @@ -315,6 +319,7 @@ void repl_build_prompt(xce_sched_sim_driver_t* sim, char* buf, size_t bufsz); |-------|--------| | Intent | Classify a resource's canonical wire unit, render its value per the operator's selected display mode, and lay out `info worker`'s/`info resource`'s listings as a grouped table instead of a flat line-per-resource dump | | Requirements | 6.1, 6.2, 6.3, 6.4, 6.5, 6.6 | +| File | The classification/scaling helpers (`repl_is_bytes_valued_resource()`/`repl_format_bytes_human()`) live in `src/scheduler/src/repl_fmt.c` (post-review, task 7.2 — see the File Structure Plan Correction above); the table-rendering call sites (`repl_info_worker()`/`repl_info_resource()`) stay in `repl_info.c`, and `set units`' parsing stays in `repl_params.c`, both unaffected by this move | **Responsibilities & Constraints** - Classification is a small, explicit, hardcoded name list — `repl_is_bytes_valued_resource(name)` returns true for exactly `mem`, `tmp`, `maxmem`, `maxtmp`, `maxswap` (confirmed canonical-bytes on the wire: `node_information.c`'s `node_info_get_mem()`/`node_info_get_tmp()` docstrings, `xcecd.c`'s `init_host_attrs()` bug-1554 comment). This mirrors `src/controller/testsuite/test.c`'s `is_bytes_valued_resource()` exactly (same 5 names, same rationale: "a small, explicit, client-side list rather than a wire field"). diff --git a/.kiro/specs/scheduler-sim-repl-verbosity/tasks.md b/.kiro/specs/scheduler-sim-repl-verbosity/tasks.md index 0fbc7033..80995d56 100644 --- a/.kiro/specs/scheduler-sim-repl-verbosity/tasks.md +++ b/.kiro/specs/scheduler-sim-repl-verbosity/tasks.md @@ -78,7 +78,7 @@ - _Requirements: 6.1, 6.2, 6.3_ - _Boundary: Resource Value Formatter_ -- [ ] 7.2 (P) Implement the classification/scaling helpers, the `units_mode` setting, and `set units` parsing +- [x] 7.2 (P) Implement the classification/scaling helpers, the `units_mode` setting, and `set units` parsing - Add the bytes-valued classifier and the adaptive human-scaling formatter as pure functions; add the `units_mode` field to the REPL session state, explicitly defaulted to `both` (not left to zero-initialization); wire `set units raw|human|both` into the existing `set` command dispatch alongside `speed`/`output`/`verify` - Observable: all tests from 7.1 pass; the helpers and the new setting are ready for the resource-listing rendering work to adopt - _Requirements: 6.1, 6.2, 6.3_ diff --git a/src/scheduler/meson.build b/src/scheduler/meson.build index 330330ba..900196ea 100644 --- a/src/scheduler/meson.build +++ b/src/scheduler/meson.build @@ -20,6 +20,7 @@ scheduler_srcs = [ 'src/sim_verify.c', 'src/sim_record.c', 'src/repl.c', + 'src/repl_fmt.c', 'src/repl_rollup.c', 'src/repl_break.c', 'src/repl_info.c', @@ -96,7 +97,7 @@ sched_core_isolation_lib = static_library( # Task 11.3 (scheduler-core spec, Req 5 AC 17): isolated compile target for # the simulation driver's own files, consumed by # testsuite/test_sim_isolation.c to prove at build time that -# sim.c/sim_loop.c/sim_record.c/repl.c/repl_break.c/repl_info.c/ +# sim.c/sim_loop.c/sim_record.c/repl.c/repl_fmt.c/repl_break.c/repl_info.c/ # repl_params.c/sim_picker.c reference zero controller-facing symbols # (client/netio/xceapi/delay/idgen). All files are compiled together into # ONE archive -- task 11.2 added sim_loop.c as a sibling file to task @@ -105,19 +106,23 @@ sched_core_isolation_lib = static_library( # fifth/sixth (task 12.2's own 800-line-cap split of repl.c's # breakpoint/state-inspection logic -- repl_internal.h's own "Why a split # at all" section has the full rationale), task 12.3 added repl_params.c -# as a seventh (the same split, extended again for `snapshot`/`set`), and +# as a seventh (the same split, extended again for `snapshot`/`set`), # bug-1567's fix added sim_picker.c as an eighth (sim_loop.c's own # "zero line-budget headroom" -- same reason its siblings above exist -- # so the fix's picker-registration + post-load picker-rebuild-walk logic -# lives here instead of growing sim_loop.c past its cap); covering only a -# subset of them would repeat core.c's own siblings' (core_events.c/ -# core_session.c/core_clock.c) never-mechanically-covered gap for the sim -# driver. Same "compiling to an object is enough, no real linking needed" -# rationale as sched_core_isolation_lib above. +# lives here instead of growing sim_loop.c past its cap), and the +# scheduler-sim-repl-verbosity spec's task 7.2 remediation added repl_fmt.c +# as a ninth (repl.c's own 800-line-cap split of its shared, dependency-free +# formatting helpers -- repl_fmt.c's own header comment has the full +# rationale); covering only a subset of them would repeat core.c's own +# siblings' (core_events.c/core_session.c/core_clock.c) +# never-mechanically-covered gap for the sim driver. Same "compiling to an +# object is enough, no real linking needed" rationale as +# sched_core_isolation_lib above. sched_sim_isolation_lib = static_library( 'sched_sim_isolation', ['src/sim.c', 'src/sim_loop.c', 'src/sim_picker.c', 'src/sim_record.c', - 'src/repl.c', 'src/repl_break.c', 'src/repl_info.c', + 'src/repl.c', 'src/repl_fmt.c', 'src/repl_break.c', 'src/repl_info.c', 'src/repl_params.c', 'src/repl_rollup.c'], include_directories: incdir, dependencies: scheduler_deps, diff --git a/src/scheduler/src/repl.c b/src/scheduler/src/repl.c index 5bbadf99..d57c1906 100644 --- a/src/scheduler/src/repl.c +++ b/src/scheduler/src/repl.c @@ -1,9 +1,7 @@ #include -#include #include #include #include -#include #include /* libedit (BSD) -- readline()/add_history() */ @@ -90,6 +88,17 @@ static xce_sched_repl_state_t* repl_state_ensure(xce_sched_sim_driver_t* sim) sim->repl_state = (xce_sched_repl_state_t*)calloc(1, sizeof(*(sim->repl_state))); + if (sim->repl_state != NULL) { + /* + * units_mode (scheduler-sim-repl-verbosity spec, task 7.2, Req 6.3) + * -- the one field in this struct that cannot rely on calloc's + * zero-initialization to select the correct default; see + * xce_repl_units_mode_t's own doc comment (repl_internal.h) for why. + * Every other field above is left at calloc's zero value + * deliberately. + */ + sim->repl_state->units_mode = XCE_REPL_UNITS_BOTH; + } return sim->repl_state; } @@ -197,25 +206,13 @@ int repl_count_jobs(xce_sched_core_t* core) } /* - * repl_format_ts_human - bug-1587 fix: renders a raw xce_core_now()-style - * nanoseconds-since-epoch value as a human-readable UTC timestamp, purely a - * calendar conversion (gmtime_r()) with no clock read of its own -- same - * "not a wall-clock call" reasoning core_clock.c's ns_to_tlog_time() - * documents for the identical conversion. `sim->snapshot_ts` is genuinely - * epoch nanoseconds for a normal-mode-recorded .replay file (bug-1584); a - * simulation-authored .replay file's snapshot ts is whatever epoch value - * its own source core was anchored to when it wrote the file, so this - * rendering is well-defined either way -- never misleading, at worst an - * arbitrary-looking date for a purely synthetic recording. + * repl_format_ts_human/repl_is_bytes_valued_resource/ + * repl_format_bytes_human moved to repl_fmt.c (scheduler-sim-repl-verbosity + * spec, task 7.2 remediation) -- see that file's own header comment and + * design.md's File Structure Plan (Correction, post-review, task 7.2) for + * why. Declarations unchanged in repl_internal.h; every call site below + * (repl_print_banner(), etc.) is unaffected. */ -void repl_format_ts_human(uint64_t ns, char* buf, size_t bufsz) -{ - time_t secs = (time_t)(ns / 1000000000ULL); - struct tm gmt; - - gmtime_r(&secs, &gmt); - strftime(buf, bufsz, "%Y-%m-%dT%H:%M:%SZ", &gmt); -} static void repl_print_banner(const xce_sched_sim_driver_t* sim) { @@ -588,265 +585,3 @@ void xce_repl_on_replay_complete(xce_sched_sim_driver_t* sim) rs->replay_complete = true; repl_command_loop(sim); } - -/* - * ---- Job-identifier range compression (scheduler-sim-repl-verbosity spec, - * task 1.2, design.md's "Jobid Range Compressor" component, Req 4, ACs - * 4.1-4.5) ---- A pure, read-only formatter with zero dependency on - * xce_sched_sim_driver_t/xce_sched_core_t or any of this file's other REPL - * machinery, kept here per design.md's File Structure Plan ("... in repl.c, - * following the file's existing shared-helper convention" -- same - * convention repl_format_ts_human()/repl_split_tokens() above establish). - * Reuses fmt_jobid_ex()'s own jobid bit-layout macros (utils.h, already - * #included above) rather than re-deriving the layout. - */ - -/* - * xce_repl_jobid_group_t - one grouping unit while repl_fmt_jobid_range() - * walks its input: either a standalone non-array jobid (`is_array` false) - * or one array submission's (cluster, apiserver, timestamp, arraybase) - * identity plus every index seen for it so far. Kept in first-seen order - * (design.md: "distinct array bases appear in input order"). `idxs` grows - * by one realloc'd slot per newly-seen index, same idiom repl_break.c's - * repl_breakpoints_append() establishes for this file family. - */ -typedef struct { - bool is_array; - uint32_t cluster; - uint32_t apiserver; - uint32_t timestamp; - uint32_t arraybase; /* array groups only; already fmt_jobid_ex()-shifted */ - uint32_t plain_seq; /* non-array groups only */ - uint32_t* idxs; - int nidxs; -} xce_repl_jobid_group_t; - -static void repl_jobid_groups_free(xce_repl_jobid_group_t* groups, - int ngroups) -{ - int i; - - for (i = 0; i < ngroups; i++) { - free(groups[i].idxs); - } - free(groups); -} - -static bool repl_jobid_group_add_idx(xce_repl_jobid_group_t* g, uint32_t idx) -{ - uint32_t* grown = (uint32_t*)realloc( - g->idxs, (size_t)(g->nidxs + 1) * sizeof(*(g->idxs))); - - if (grown == NULL) { - return false; - } - - g->idxs = grown; - g->idxs[g->nidxs] = idx; - g->nidxs++; - - return true; -} - -static int repl_jobid_idx_cmp(const void* a, const void* b) -{ - uint32_t ua = *(const uint32_t*)a; - uint32_t ub = *(const uint32_t*)b; - - if (ua < ub) { - return -1; - } - if (ua > ub) { - return 1; - } - return 0; -} - -/* - * repl_jr_appendf - appends one snprintf()-formatted fragment onto a - * realloc'd dynamic string (`*out`/`*outlen` track the allocation and its - * used length; `*out` stays NUL-terminated), growing by exactly the - * fragment's length each time -- same realloc idiom as - * repl_jobid_group_add_idx() above. Every fragment this formats (a decimal - * number, dash, comma, or bracket) fits well inside the 63-byte local - * scratch below; exceeding that is a defensive failure (returns false, - * `*out`/`*outlen` unchanged), as is an allocation failure. - */ -static bool repl_jr_appendf(char** out, size_t* outlen, const char* fmt, ...) -{ - va_list ap; - char frag[64]; - int n; - char* grown; - - va_start(ap, fmt); - n = vsnprintf(frag, sizeof(frag), fmt, ap); - va_end(ap); - - if ((n < 0) || ((size_t)n >= sizeof(frag))) { - return false; - } - - grown = (char*)realloc(*out, *outlen + (size_t)n + 1); - if (grown == NULL) { - return false; - } - - memcpy(grown + *outlen, frag, (size_t)n + 1); /* fragment + its NUL */ - *out = grown; - *outlen += (size_t)n; - - return true; -} - -/* - * repl_fmt_jobid_range - design.md's "Jobid Range Compressor" (Req 4, ACs - * 4.1-4.5): formats `njobids` raw jobids into `buf` (bufsz-bounded), - * grouping by array submission (cluster, apiserver, timestamp, array base) - * and compressing contiguous [idx] runs into "[lo-hi]" form, comma-joining - * non-contiguous runs within one base; non-array jobids are listed - * individually. Distinct groups appear in the order each is FIRST seen in - * `jobids`; indices within a compressed range always ascend regardless of - * input order. Returns bytes written to `buf` (excluding the NUL), or - * negative if `buf` was too small -- mirroring fmt_jobid_ex()'s own pattern - * (src/lib/utils.c): the full rendering is built into a heap scratch buffer - * first, and `buf` is touched only once it is proven to fit -- never a - * partial/truncated write. - */ -int repl_fmt_jobid_range(char* buf, size_t bufsz, const uint64_t* jobids, - int njobids) -{ - xce_repl_jobid_group_t* groups = NULL; - int ngroups = 0; - char* out = NULL; - size_t outlen = 0; - bool ok = true; - int i; - int rc; - - if ((buf == NULL) || (bufsz == 0)) { - return -1; - } - if (njobids <= 0) { - buf[0] = '\0'; - return 0; - } - if (jobids == NULL) { - return -1; - } - - groups = (xce_repl_jobid_group_t*)calloc((size_t)njobids, - sizeof(*groups)); - if (groups == NULL) { - return -1; - } - - for (i = 0; (i < njobids) && ok; i++) { - uint64_t id = jobids[i]; - uint32_t tag = EXTRACT_ID_TAG(id); - uint32_t cluster = EXTRACT_ID_CLUSTER(id); - uint32_t apiserver = EXTRACT_ID_APISERVER(id); - uint32_t timestamp = EXTRACT_ID_TIMESTAMP(id); - - if ((tag & TAG_JOB_ARRAY) != 0) { - uint32_t arraybase = EXTRACT_ARRAY_JOB(id); - uint32_t idx = EXTRACT_ARRAY_IDX(id); - int g = -1; - int j; - - for (j = 0; j < ngroups; j++) { - if (groups[j].is_array && (groups[j].cluster == cluster) - && (groups[j].apiserver == apiserver) - && (groups[j].timestamp == timestamp) - && (groups[j].arraybase == arraybase)) { - g = j; - break; - } - } - if (g < 0) { - g = ngroups++; - groups[g].is_array = true; - groups[g].cluster = cluster; - groups[g].apiserver = apiserver; - groups[g].timestamp = timestamp; - groups[g].arraybase = arraybase; - } - ok = repl_jobid_group_add_idx(&groups[g], idx); - } else { - int g = ngroups++; - - groups[g].is_array = false; - groups[g].cluster = cluster; - groups[g].apiserver = apiserver; - groups[g].timestamp = timestamp; - groups[g].plain_seq = EXTRACT_ID_SEQUENCE(id); - } - } - - for (i = 0; (i < ngroups) && ok; i++) { - xce_repl_jobid_group_t* g = &groups[i]; - - if (i > 0) { - ok = repl_jr_appendf(&out, &outlen, ","); - if (!ok) { - break; - } - } - - if (!g->is_array) { - ok = repl_jr_appendf(&out, &outlen, "%u-%u-%u-%u", g->cluster, - g->apiserver, g->timestamp, g->plain_seq); - continue; - } - - qsort(g->idxs, (size_t)g->nidxs, sizeof(*(g->idxs)), - repl_jobid_idx_cmp); - - ok = repl_jr_appendf(&out, &outlen, "%u-%u-%u-%u[", g->cluster, - g->apiserver, g->timestamp, g->arraybase); - - { - int j = 0; - - while (ok && (j < g->nidxs)) { - uint32_t run_lo = g->idxs[j]; - uint32_t run_hi = run_lo; - int k = j + 1; - - while ((k < g->nidxs) && (g->idxs[k] == run_hi + 1)) { - run_hi = g->idxs[k]; - k++; - } - - if (j > 0) { - ok = repl_jr_appendf(&out, &outlen, ","); - } - if (ok && (run_lo == run_hi)) { - ok = repl_jr_appendf(&out, &outlen, "%u", run_lo); - } else if (ok) { - ok = repl_jr_appendf(&out, &outlen, "%u-%u", run_lo, - run_hi); - } - - j = k; - } - } - - if (ok) { - ok = repl_jr_appendf(&out, &outlen, "]"); - } - } - - repl_jobid_groups_free(groups, ngroups); - - if (!ok || (out == NULL) || ((outlen + 1) > bufsz)) { - free(out); - return -1; - } - - memcpy(buf, out, outlen + 1); - rc = (int)outlen; - free(out); - - return rc; -} diff --git a/src/scheduler/src/repl_fmt.c b/src/scheduler/src/repl_fmt.c new file mode 100644 index 00000000..e13be2b3 --- /dev/null +++ b/src/scheduler/src/repl_fmt.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include + +#include "repl.h" +#include "repl_internal.h" +#include "utils.h" + +/* + * repl_fmt.c - shared, pure, read-only formatting helpers for the REPL + * command family (scheduler-sim-repl-verbosity spec, task 7.2 remediation). + * New file per repl_internal.h's own "Why a split at all" section -- + * follows the exact domain-split precedent repl_rollup.c already + * established: task 7.2's Resource Value Formatter helpers + * (repl_is_bytes_valued_resource()/repl_format_bytes_human()) landed + * directly in repl.c per the original design.md plan and pushed it to 929 + * lines, 16.1% over this project's 800-line-per-file cap + * (src/scheduler/CLAUDE.md), even though review confirmed the logic itself + * was correct. This file now holds three groups of pure formatting helpers + * that repl.c's own file header already called "the file's existing + * shared-helper convention" -- none of them touch + * xce_sched_sim_driver_t/xce_sched_core_t or any REPL session state, so + * grouping them here (rather than splitting each into its own file) keeps + * one cohesive, dependency-free unit: + * + * - repl_format_ts_human() (pre-existing, bug-1587) -- epoch-nanoseconds + * to human-readable UTC timestamp. + * - The Jobid Range Compressor (task 1.2, design.md's "Jobid Range + * Compressor" component, Req 4): xce_repl_jobid_group_t and its private + * helpers, plus repl_fmt_jobid_range() itself. + * - The Resource Value Formatter's classification/scaling helpers (task + * 7.2, design.md's "Resource Value Formatter" component, Req 6.1, 6.2): + * repl_is_bytes_valued_resource()/repl_format_bytes_human(). + * + * Every one of these functions is already extern-declared in + * repl_internal.h -- this move changes only which .c file DEFINES them, + * not any call site elsewhere in repl.c/repl_break.c/repl_info.c/ + * repl_params.c. See design.md's File Structure Plan (Correction, + * post-review, task 7.2) for the full rationale, including why this split + * (unlike task 6.2's reverted repl_build_prompt()/repl_prompt.c attempt) + * achieves genuine compliance. + * + * Boundary (Req 5 AC 17, "Simulation isolation", same as repl.c/repl_break.c/ + * repl_info.c/repl_params.c): this file touches only stdio/stdlib/string/ + * stdarg/time plus repl.h/repl_internal.h/utils.h -- no xcecli_ctx_t, no + * xce_cluster_config_t, no capnp socket, no controller RPC. + * test_sim_isolation.c (task 12.1, extended by this remediation) scans this + * file too. + */ + +/* + * repl_format_ts_human - bug-1587 fix: renders a raw xce_core_now()-style + * nanoseconds-since-epoch value as a human-readable UTC timestamp, purely a + * calendar conversion (gmtime_r()) with no clock read of its own -- same + * "not a wall-clock call" reasoning core_clock.c's ns_to_tlog_time() + * documents for the identical conversion. `sim->snapshot_ts` is genuinely + * epoch nanoseconds for a normal-mode-recorded .replay file (bug-1584); a + * simulation-authored .replay file's snapshot ts is whatever epoch value + * its own source core was anchored to when it wrote the file, so this + * rendering is well-defined either way -- never misleading, at worst an + * arbitrary-looking date for a purely synthetic recording. + */ +void repl_format_ts_human(uint64_t ns, char* buf, size_t bufsz) +{ + time_t secs = (time_t)(ns / 1000000000ULL); + struct tm gmt; + + gmtime_r(&secs, &gmt); + strftime(buf, bufsz, "%Y-%m-%dT%H:%M:%SZ", &gmt); +} + +/* + * 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. + */ +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; +} + +/* + * 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]); +} + +/* + * ---- Job-identifier range compression (scheduler-sim-repl-verbosity spec, + * task 1.2, design.md's "Jobid Range Compressor" component, Req 4, ACs + * 4.1-4.5) ---- A pure, read-only formatter with zero dependency on + * xce_sched_sim_driver_t/xce_sched_core_t or any of this file's other REPL + * machinery, kept here per design.md's File Structure Plan ("... in + * repl_fmt.c, following the file family's existing shared-helper + * convention" -- post-review, task 7.2: moved out of repl.c along with + * repl_format_ts_human()/repl_is_bytes_valued_resource()/ + * repl_format_bytes_human() above, see this file's own header comment). + * Reuses fmt_jobid_ex()'s own jobid bit-layout macros (utils.h, already + * #included above) rather than re-deriving the layout. + */ + +/* + * xce_repl_jobid_group_t - one grouping unit while repl_fmt_jobid_range() + * walks its input: either a standalone non-array jobid (`is_array` false) + * or one array submission's (cluster, apiserver, timestamp, arraybase) + * identity plus every index seen for it so far. Kept in first-seen order + * (design.md: "distinct array bases appear in input order"). `idxs` grows + * by one realloc'd slot per newly-seen index, same idiom repl_break.c's + * repl_breakpoints_append() establishes for this file family. + */ +typedef struct { + bool is_array; + uint32_t cluster; + uint32_t apiserver; + uint32_t timestamp; + uint32_t arraybase; /* array groups only; already fmt_jobid_ex()-shifted */ + uint32_t plain_seq; /* non-array groups only */ + uint32_t* idxs; + int nidxs; +} xce_repl_jobid_group_t; + +static void repl_jobid_groups_free(xce_repl_jobid_group_t* groups, + int ngroups) +{ + int i; + + for (i = 0; i < ngroups; i++) { + free(groups[i].idxs); + } + free(groups); +} + +static bool repl_jobid_group_add_idx(xce_repl_jobid_group_t* g, uint32_t idx) +{ + uint32_t* grown = (uint32_t*)realloc( + g->idxs, (size_t)(g->nidxs + 1) * sizeof(*(g->idxs))); + + if (grown == NULL) { + return false; + } + + g->idxs = grown; + g->idxs[g->nidxs] = idx; + g->nidxs++; + + return true; +} + +static int repl_jobid_idx_cmp(const void* a, const void* b) +{ + uint32_t ua = *(const uint32_t*)a; + uint32_t ub = *(const uint32_t*)b; + + if (ua < ub) { + return -1; + } + if (ua > ub) { + return 1; + } + return 0; +} + +/* + * repl_jr_appendf - appends one snprintf()-formatted fragment onto a + * realloc'd dynamic string (`*out`/`*outlen` track the allocation and its + * used length; `*out` stays NUL-terminated), growing by exactly the + * fragment's length each time -- same realloc idiom as + * repl_jobid_group_add_idx() above. Every fragment this formats (a decimal + * number, dash, comma, or bracket) fits well inside the 63-byte local + * scratch below; exceeding that is a defensive failure (returns false, + * `*out`/`*outlen` unchanged), as is an allocation failure. + */ +static bool repl_jr_appendf(char** out, size_t* outlen, const char* fmt, ...) +{ + va_list ap; + char frag[64]; + int n; + char* grown; + + va_start(ap, fmt); + n = vsnprintf(frag, sizeof(frag), fmt, ap); + va_end(ap); + + if ((n < 0) || ((size_t)n >= sizeof(frag))) { + return false; + } + + grown = (char*)realloc(*out, *outlen + (size_t)n + 1); + if (grown == NULL) { + return false; + } + + memcpy(grown + *outlen, frag, (size_t)n + 1); /* fragment + its NUL */ + *out = grown; + *outlen += (size_t)n; + + return true; +} + +/* + * repl_fmt_jobid_range - design.md's "Jobid Range Compressor" (Req 4, ACs + * 4.1-4.5): formats `njobids` raw jobids into `buf` (bufsz-bounded), + * grouping by array submission (cluster, apiserver, timestamp, array base) + * and compressing contiguous [idx] runs into "[lo-hi]" form, comma-joining + * non-contiguous runs within one base; non-array jobids are listed + * individually. Distinct groups appear in the order each is FIRST seen in + * `jobids`; indices within a compressed range always ascend regardless of + * input order. Returns bytes written to `buf` (excluding the NUL), or + * negative if `buf` was too small -- mirroring fmt_jobid_ex()'s own pattern + * (src/lib/utils.c): the full rendering is built into a heap scratch buffer + * first, and `buf` is touched only once it is proven to fit -- never a + * partial/truncated write. + */ +int repl_fmt_jobid_range(char* buf, size_t bufsz, const uint64_t* jobids, + int njobids) +{ + xce_repl_jobid_group_t* groups = NULL; + int ngroups = 0; + char* out = NULL; + size_t outlen = 0; + bool ok = true; + int i; + int rc; + + if ((buf == NULL) || (bufsz == 0)) { + return -1; + } + if (njobids <= 0) { + buf[0] = '\0'; + return 0; + } + if (jobids == NULL) { + return -1; + } + + groups = (xce_repl_jobid_group_t*)calloc((size_t)njobids, + sizeof(*groups)); + if (groups == NULL) { + return -1; + } + + for (i = 0; (i < njobids) && ok; i++) { + uint64_t id = jobids[i]; + uint32_t tag = EXTRACT_ID_TAG(id); + uint32_t cluster = EXTRACT_ID_CLUSTER(id); + uint32_t apiserver = EXTRACT_ID_APISERVER(id); + uint32_t timestamp = EXTRACT_ID_TIMESTAMP(id); + + if ((tag & TAG_JOB_ARRAY) != 0) { + uint32_t arraybase = EXTRACT_ARRAY_JOB(id); + uint32_t idx = EXTRACT_ARRAY_IDX(id); + int g = -1; + int j; + + for (j = 0; j < ngroups; j++) { + if (groups[j].is_array && (groups[j].cluster == cluster) + && (groups[j].apiserver == apiserver) + && (groups[j].timestamp == timestamp) + && (groups[j].arraybase == arraybase)) { + g = j; + break; + } + } + if (g < 0) { + g = ngroups++; + groups[g].is_array = true; + groups[g].cluster = cluster; + groups[g].apiserver = apiserver; + groups[g].timestamp = timestamp; + groups[g].arraybase = arraybase; + } + ok = repl_jobid_group_add_idx(&groups[g], idx); + } else { + int g = ngroups++; + + groups[g].is_array = false; + groups[g].cluster = cluster; + groups[g].apiserver = apiserver; + groups[g].timestamp = timestamp; + groups[g].plain_seq = EXTRACT_ID_SEQUENCE(id); + } + } + + for (i = 0; (i < ngroups) && ok; i++) { + xce_repl_jobid_group_t* g = &groups[i]; + + if (i > 0) { + ok = repl_jr_appendf(&out, &outlen, ","); + if (!ok) { + break; + } + } + + if (!g->is_array) { + ok = repl_jr_appendf(&out, &outlen, "%u-%u-%u-%u", g->cluster, + g->apiserver, g->timestamp, g->plain_seq); + continue; + } + + qsort(g->idxs, (size_t)g->nidxs, sizeof(*(g->idxs)), + repl_jobid_idx_cmp); + + ok = repl_jr_appendf(&out, &outlen, "%u-%u-%u-%u[", g->cluster, + g->apiserver, g->timestamp, g->arraybase); + + { + int j = 0; + + while (ok && (j < g->nidxs)) { + uint32_t run_lo = g->idxs[j]; + uint32_t run_hi = run_lo; + int k = j + 1; + + while ((k < g->nidxs) && (g->idxs[k] == run_hi + 1)) { + run_hi = g->idxs[k]; + k++; + } + + if (j > 0) { + ok = repl_jr_appendf(&out, &outlen, ","); + } + if (ok && (run_lo == run_hi)) { + ok = repl_jr_appendf(&out, &outlen, "%u", run_lo); + } else if (ok) { + ok = repl_jr_appendf(&out, &outlen, "%u-%u", run_lo, + run_hi); + } + + j = k; + } + } + + if (ok) { + ok = repl_jr_appendf(&out, &outlen, "]"); + } + } + + repl_jobid_groups_free(groups, ngroups); + + if (!ok || (out == NULL) || ((outlen + 1) > bufsz)) { + free(out); + return -1; + } + + memcpy(buf, out, outlen + 1); + rc = (int)outlen; + free(out); + + return rc; +} diff --git a/src/scheduler/src/repl_internal.h b/src/scheduler/src/repl_internal.h index a665d09d..f8894e9b 100644 --- a/src/scheduler/src/repl_internal.h +++ b/src/scheduler/src/repl_internal.h @@ -119,6 +119,29 @@ typedef struct { int nfilters; } xce_repl_breakpoint_t; +/* + * xce_repl_units_mode_t (scheduler-sim-repl-verbosity spec, task 7.2, + * design.md's "Resource Value Formatter" component, Req 6.2/6.3) - the + * REPL's resource-value display mode: `raw` shows only the numeric wire + * value, `human` shows only the adaptively-scaled B/KB/MB/GB/TB/PB form + * (repl_format_bytes_human(), repl_fmt.c), `both` shows both. Settable via `set + * units raw|human|both` (repl_params.c's repl_cmd_set()); read by the (task + * 8, not yet implemented) `info worker`/`info resource` table renderer. + * + * Deliberately does NOT start at 0 -- and, more importantly, MUST NOT be + * relied upon to start at 0 -- for its default: repl_state_ensure() (repl.c) + * explicitly assigns `units_mode = XCE_REPL_UNITS_BOTH` after calloc, + * regardless of this enum's member order (design.md's own State Management + * section calls this out as the one field in xce_sched_repl_state_s that + * cannot rely on calloc's zero-initialization the way every other field in + * this struct deliberately does -- see that field's own doc comment below). + */ +typedef enum { + XCE_REPL_UNITS_RAW = 1, + XCE_REPL_UNITS_HUMAN, + XCE_REPL_UNITS_BOTH +} xce_repl_units_mode_t; + /* * xce_sched_repl_state_s - see this header's own "Why a split at all" * section above for why the full definition lives here now instead of @@ -223,6 +246,20 @@ struct xce_sched_repl_state_s { int collapse_preempt_count; uint64_t* collapse_pend_jobids; int collapse_n_pend_jobids; + + /* + * Resource Value Formatter field (scheduler-sim-repl-verbosity spec, + * task 7.2, design.md's "Resource Value Formatter" component, Req + * 6.2/6.3) - the operator's selected resource-value display mode, + * settable via `set units raw|human|both` (repl_params.c). Defaults to + * XCE_REPL_UNITS_BOTH (Req 6.3), explicitly assigned by + * repl_state_ensure() (repl.c) rather than left to calloc's + * zero-initialization -- see xce_repl_units_mode_t's own doc comment + * above for why. Every other field in this struct legitimately relies + * on calloc's zero value being the correct default; this is the one + * exception. + */ + xce_repl_units_mode_t units_mode; }; /* repl_split_tokens/XCE_REPL_MAX_TOKENS/XCE_REPL_TOKEN_LEN (repl.c) - @@ -235,13 +272,47 @@ struct xce_sched_repl_state_s { extern int repl_split_tokens(const char* line, char tokens[][XCE_REPL_TOKEN_LEN], int max_tokens); -/* repl_format_ts_human (repl.c) - bug-1587 fix: renders a raw +/* repl_format_ts_human (repl_fmt.c, moved from repl.c by the task 7.2 + * remediation -- see repl_fmt.c's own header comment and design.md's File + * Structure Plan Correction) - bug-1587 fix: renders a raw * xce_core_now()-style nanoseconds-since-epoch value as a human-readable * UTC timestamp ("YYYY-MM-DDTHH:MM:SSZ") into buf (must be >= 21 bytes). * Shared by every REPL/sim `ts=` print site (repl.c/repl_info.c/ * repl_params.c) so they render consistently. */ extern void repl_format_ts_human(uint64_t ns, char* buf, size_t bufsz); +/* + * repl_is_bytes_valued_resource / repl_format_bytes_human (repl_fmt.c, + * moved from repl.c by this same task 7.2 remediation -- see repl_fmt.c's + * own header comment and design.md's File Structure Plan Correction), + * scheduler-sim-repl-verbosity spec, task 7.2, design.md's "Resource Value + * Formatter" component, Req 6.1/6.2) - pure classification/scaling helpers + * mirroring src/controller/testsuite/test.c's own + * is_bytes_valued_resource()/format_bytes_human() exactly (design.md's + * cited precedent -- same 5-name classification list, same + * divide-by-1024-while->=1024-cap-at-PB adaptive scaling, same "%.2f %s" + * render format). + * + * repl_is_bytes_valued_resource(name) returns true for exactly "mem", + * "tmp", "maxmem", "maxtmp", "maxswap" (confirmed canonical-bytes on the + * wire) -- `swp` is a deliberate exception, NOT in this list: its own wire + * unit is megabytes (node_info_get_swap() doc comment), so a caller + * converts MB->bytes (x 1024.0 x 1024.0) before passing it to + * repl_format_bytes_human() below, rather than this classifier lying about + * swp's own canonical unit. + * + * repl_format_bytes_human(bytes, buf, bufsz) adaptively scales a raw byte + * count to the smallest B/KB/MB/GB/TB/PB unit it fits, writing e.g. "20.56 + * GB" into buf (bufsz-bounded). Display-only -- never touches the + * underlying stored/wire value, same discipline test.c's own + * format_bytes_human() doc comment states. + * + * Both are pure functions -- no dependency on sim/core/repl state, no side + * effects. + */ +extern bool repl_is_bytes_valued_resource(const char* name); +extern void repl_format_bytes_human(double bytes, char* buf, size_t bufsz); + /* repl_command_loop (repl.c) - the blocking prompt loop. Breakpoint hits * (repl_break.c's xce_repl_on_event()/on_begin_round()/on_decision()) call * back into it directly, the same re-entry xce_repl_post_session_check() @@ -273,7 +344,9 @@ extern bool repl_cmd_set(xce_sched_sim_driver_t* sim, const char* line); extern const char* repl_job_state_name(uint32_t state); /* - * repl_fmt_jobid_range (repl.c, scheduler-sim-repl-verbosity spec, task 1.2, + * repl_fmt_jobid_range (repl_fmt.c, moved from repl.c by the task 7.2 + * remediation -- see repl_fmt.c's own header comment and design.md's File + * Structure Plan Correction), scheduler-sim-repl-verbosity spec, task 1.2, * Requirement 4 "Job Identifier Range Compression", ACs 4.1-4.5) - formats * `njobids` raw jobids into `buf` (bufsz-bounded): groups by array * submission (cluster, apiserver, timestamp, array base) and compresses @@ -291,18 +364,25 @@ extern const char* repl_job_state_name(uint32_t state); * Compressor component, Requirement 4.5) -- one helper, no per-caller * variant. * - * Lives in repl.c itself, exactly as design.md's File Structure Plan - * directs ("implements the new shared jobid-range-compression helper ... in - * repl.c"), even though it has zero dependency on - * xce_sched_sim_driver_t/xce_sched_core_t or any of repl_internal.h's OTHER - * extern cross-references (repl_cmd_break/repl_cmd_info/repl_cmd_set/... - * implemented in the repl_break.c/repl_info.c/repl_params.c siblings). - * test_repl_output_compression.c (task 1.1) still declares this function as - * a plain extern rather than #include-ing repl.c -- see that test file's - * own header comment -- and its meson target links the real scheduler_srcs - * object list instead (sched_scheduler_connect_test's own established - * technique), so it exercises this real implementation without pulling - * repl.c's other undefined cross-references into a directly-linked .o. + * Originally landed in repl.c itself, per design.md's original File + * Structure Plan text ("implements the new shared jobid-range-compression + * helper ... in repl.c"); moved to repl_fmt.c by the task 7.2 remediation + * (bug-1762 notwithstanding -- that bug was about test-target linkage, not + * which file DEFINES this function) precisely because it has zero + * dependency on xce_sched_sim_driver_t/xce_sched_core_t or any of + * repl_internal.h's OTHER extern cross-references + * (repl_cmd_break/repl_cmd_info/repl_cmd_set/... implemented in the + * repl_break.c/repl_info.c/repl_params.c siblings) -- the same + * "pure/dependency-free" property that makes it a natural fit alongside + * repl_format_ts_human()/the Resource Value Formatter helpers in + * repl_fmt.c. test_repl_output_compression.c (task 1.1) still declares this + * function as a plain extern rather than #include-ing repl.c or repl_fmt.c + * -- see that test file's own header comment -- and its meson target links + * the real scheduler_srcs object list instead (sched_scheduler_connect_test's + * own established technique, per bug-1762's fix), so it exercises this real + * implementation (now in repl_fmt.c, part of scheduler_srcs) without + * pulling repl.c's other undefined cross-references into a + * directly-linked .o. */ extern int repl_fmt_jobid_range(char* buf, size_t bufsz, const uint64_t* jobids, int njobids); diff --git a/src/scheduler/src/repl_params.c b/src/scheduler/src/repl_params.c index b92de91f..9f679385 100644 --- a/src/scheduler/src/repl_params.c +++ b/src/scheduler/src/repl_params.c @@ -339,10 +339,58 @@ static bool repl_set_output_file(xce_sched_sim_driver_t* sim, const char* path) return false; } +/* + * repl_set_units - scheduler-sim-repl-verbosity spec, task 7.2, design.md's + * "Resource Value Formatter" component (Req 6.2/6.3), "set units + * raw|human|both": selects the operator's resource-value display mode + * (rs->units_mode), read by the (task 8, not yet implemented) `info + * worker`/`info resource` table renderer. Same per-key + * usage-message-on-invalid-value pattern repl_set_speed()/`set verify` + * (above) already establish -- an unrecognized VALUE is rejected with a + * units-specific usage message and leaves the current mode unchanged, never + * the generic "unrecognized parameter" message repl_cmd_set() falls through + * to below (that message fires only for an unrecognized KEY; `units` IS a + * recognized key, only its value here is invalid). + * + * sim->repl_state is assumed non-NULL, same discipline every other + * repl_cmd_* handler in this file family follows (repl.c's + * repl_state_ensure() doc comment: by the time any repl_dispatch() command + * runs, xce_repl_start() has already guaranteed it) -- still defensively + * checked rather than dereferenced blindly, matching repl_set_output_off()'s + * own NULL-tolerant style just above. + */ +static bool repl_set_units(xce_sched_sim_driver_t* sim, const char* value) +{ + xce_repl_units_mode_t mode; + + if (strcmp(value, "raw") == 0) { + mode = XCE_REPL_UNITS_RAW; + } else if (strcmp(value, "human") == 0) { + mode = XCE_REPL_UNITS_HUMAN; + } else if (strcmp(value, "both") == 0) { + mode = XCE_REPL_UNITS_BOTH; + } else { + fprintf(stdout, "[sim] set units: usage: set units raw|human|both\n"); + return false; + } + + if (sim->repl_state == NULL) { + fprintf(stdout, "[sim] set units: REPL state unavailable\n"); + return false; + } + + sim->repl_state->units_mode = mode; + fprintf(stdout, "[sim] units set to %s\n", value); + + return false; +} + /* * repl_cmd_set - Req 5 AC 22's "set ..." family: "set speed " / - * "set output |off" / "set verify on|off". `line` re-tokenized via - * repl_split_tokens() (repl.c), same as break/delete/info. + * "set output |off" / "set verify on|off" / "set units + * raw|human|both" (the last added by scheduler-sim-repl-verbosity's task + * 7.2, Req 6.2/6.3). `line` re-tokenized via repl_split_tokens() (repl.c), + * same as break/delete/info. */ bool repl_cmd_set(xce_sched_sim_driver_t* sim, const char* line) { @@ -351,7 +399,7 @@ bool repl_cmd_set(xce_sched_sim_driver_t* sim, const char* line) if (ntok < 3) { fprintf(stdout, "[sim] set: usage: set speed | set output |off | " - "set verify on|off\n"); + "set verify on|off | set units raw|human|both\n"); return false; } @@ -376,6 +424,9 @@ bool repl_cmd_set(xce_sched_sim_driver_t* sim, const char* line) fprintf(stdout, "[sim] verify set to %s\n", sim->verify ? "on" : "off"); return false; } + if (strcmp(tokens[1], "units") == 0) { + return repl_set_units(sim, tokens[2]); + } fprintf(stdout, "[sim] set: unrecognized parameter '%s'\n", tokens[1]); diff --git a/src/scheduler/testsuite/meson.build b/src/scheduler/testsuite/meson.build index e6a1edd2..887eb5a4 100644 --- a/src/scheduler/testsuite/meson.build +++ b/src/scheduler/testsuite/meson.build @@ -628,6 +628,9 @@ sched_sim_isolation_test = executable( '-DXCE_SIM_REPL_ROLLUP_C_PATH="' + (meson.current_source_dir() / '..' / 'src' / 'repl_rollup.c') + '"', + '-DXCE_SIM_REPL_FMT_C_PATH="' + + (meson.current_source_dir() / '..' / 'src' / 'repl_fmt.c') + + '"', '-DXCE_SIM_LIB_PATH="' + sched_sim_isolation_lib.full_path() + '"'], diff --git a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c index 366c0670..09871af0 100644 --- a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c +++ b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c @@ -80,6 +80,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_equivalence.c b/src/scheduler/testsuite/test_repl_equivalence.c index 5908fc87..18ec97e2 100644 --- a/src/scheduler/testsuite/test_repl_equivalence.c +++ b/src/scheduler/testsuite/test_repl_equivalence.c @@ -129,6 +129,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_event_echo.c b/src/scheduler/testsuite/test_repl_event_echo.c index 65fbfff3..b340c45e 100644 --- a/src/scheduler/testsuite/test_repl_event_echo.c +++ b/src/scheduler/testsuite/test_repl_event_echo.c @@ -97,6 +97,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_execution_control.c b/src/scheduler/testsuite/test_repl_execution_control.c index 7261e927..d0cfa843 100644 --- a/src/scheduler/testsuite/test_repl_execution_control.c +++ b/src/scheduler/testsuite/test_repl_execution_control.c @@ -94,6 +94,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_prompt_context.c b/src/scheduler/testsuite/test_repl_prompt_context.c index c4f6ca01..e9855054 100644 --- a/src/scheduler/testsuite/test_repl_prompt_context.c +++ b/src/scheduler/testsuite/test_repl_prompt_context.c @@ -93,6 +93,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_round_rollup.c b/src/scheduler/testsuite/test_repl_round_rollup.c index d9f9595c..d4191514 100644 --- a/src/scheduler/testsuite/test_repl_round_rollup.c +++ b/src/scheduler/testsuite/test_repl_round_rollup.c @@ -56,6 +56,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_repl_snapshot_params.c b/src/scheduler/testsuite/test_repl_snapshot_params.c index ca1dcdb0..103bd7e6 100644 --- a/src/scheduler/testsuite/test_repl_snapshot_params.c +++ b/src/scheduler/testsuite/test_repl_snapshot_params.c @@ -69,6 +69,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_alt_events.c b/src/scheduler/testsuite/test_sim_alt_events.c index 754748fb..0bbb033c 100644 --- a/src/scheduler/testsuite/test_sim_alt_events.c +++ b/src/scheduler/testsuite/test_sim_alt_events.c @@ -87,6 +87,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_contention_parity.c b/src/scheduler/testsuite/test_sim_contention_parity.c index 4ac1caaf..725c511c 100644 --- a/src/scheduler/testsuite/test_sim_contention_parity.c +++ b/src/scheduler/testsuite/test_sim_contention_parity.c @@ -171,6 +171,7 @@ #include "../src/recording_events.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c index 5123890c..d9a4974b 100644 --- a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c +++ b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c @@ -194,6 +194,7 @@ #include "../src/recording_events.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_equivalence.c b/src/scheduler/testsuite/test_sim_equivalence.c index b67b921f..3226cf9a 100644 --- a/src/scheduler/testsuite/test_sim_equivalence.c +++ b/src/scheduler/testsuite/test_sim_equivalence.c @@ -156,6 +156,7 @@ #include "../src/recording_events.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_event_loop.c b/src/scheduler/testsuite/test_sim_event_loop.c index 494d5679..a715dff6 100644 --- a/src/scheduler/testsuite/test_sim_event_loop.c +++ b/src/scheduler/testsuite/test_sim_event_loop.c @@ -103,6 +103,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_isolation.c b/src/scheduler/testsuite/test_sim_isolation.c index 87247e43..8caad713 100644 --- a/src/scheduler/testsuite/test_sim_isolation.c +++ b/src/scheduler/testsuite/test_sim_isolation.c @@ -447,6 +447,7 @@ static void test_sim_never_includes_forbidden_headers(void) char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH); char* repl_params_c_raw = read_whole_file(XCE_SIM_REPL_PARAMS_C_PATH); char* repl_rollup_c_raw = read_whole_file(XCE_SIM_REPL_ROLLUP_C_PATH); + char* repl_fmt_c_raw = read_whole_file(XCE_SIM_REPL_FMT_C_PATH); char* h; char* c; char* loop; @@ -459,6 +460,7 @@ static void test_sim_never_includes_forbidden_headers(void) char* repl_int_h; char* repl_params_c; char* repl_rollup_c; + char* repl_fmt_c; TEST_ASSERT_NOT_NULL(h_raw); TEST_ASSERT_NOT_NULL(c_raw); @@ -472,6 +474,7 @@ static void test_sim_never_includes_forbidden_headers(void) TEST_ASSERT_NOT_NULL(repl_int_h_raw); TEST_ASSERT_NOT_NULL(repl_params_c_raw); TEST_ASSERT_NOT_NULL(repl_rollup_c_raw); + TEST_ASSERT_NOT_NULL(repl_fmt_c_raw); h = strip_c_comments(h_raw); c = strip_c_comments(c_raw); @@ -485,6 +488,7 @@ static void test_sim_never_includes_forbidden_headers(void) repl_int_h = strip_c_comments(repl_int_h_raw); repl_params_c = strip_c_comments(repl_params_c_raw); repl_rollup_c = strip_c_comments(repl_rollup_c_raw); + repl_fmt_c = strip_c_comments(repl_fmt_c_raw); TEST_ASSERT_NOT_NULL(h); TEST_ASSERT_NOT_NULL(c); TEST_ASSERT_NOT_NULL(loop); @@ -497,6 +501,7 @@ static void test_sim_never_includes_forbidden_headers(void) TEST_ASSERT_NOT_NULL(repl_int_h); TEST_ASSERT_NOT_NULL(repl_params_c); TEST_ASSERT_NOT_NULL(repl_rollup_c); + TEST_ASSERT_NOT_NULL(repl_fmt_c); assert_no_forbidden_includes(h, "sim.h"); assert_no_forbidden_includes(c, "sim.c"); @@ -510,6 +515,7 @@ static void test_sim_never_includes_forbidden_headers(void) assert_no_forbidden_includes(repl_int_h, "repl_internal.h"); assert_no_forbidden_includes(repl_params_c, "repl_params.c"); assert_no_forbidden_includes(repl_rollup_c, "repl_rollup.c"); + assert_no_forbidden_includes(repl_fmt_c, "repl_fmt.c"); free(h_raw); free(c_raw); @@ -523,6 +529,7 @@ static void test_sim_never_includes_forbidden_headers(void) free(repl_int_h_raw); free(repl_params_c_raw); free(repl_rollup_c_raw); + free(repl_fmt_c_raw); free(h); free(c); free(loop); @@ -535,6 +542,7 @@ static void test_sim_never_includes_forbidden_headers(void) free(repl_int_h); free(repl_params_c); free(repl_rollup_c); + free(repl_fmt_c); } static void test_sim_never_calls_forbidden_symbols(void) @@ -551,6 +559,7 @@ static void test_sim_never_calls_forbidden_symbols(void) char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH); char* repl_params_c_raw = read_whole_file(XCE_SIM_REPL_PARAMS_C_PATH); char* repl_rollup_c_raw = read_whole_file(XCE_SIM_REPL_ROLLUP_C_PATH); + char* repl_fmt_c_raw = read_whole_file(XCE_SIM_REPL_FMT_C_PATH); char* h; char* c; char* loop; @@ -563,6 +572,7 @@ static void test_sim_never_calls_forbidden_symbols(void) char* repl_int_h; char* repl_params_c; char* repl_rollup_c; + char* repl_fmt_c; TEST_ASSERT_NOT_NULL(h_raw); TEST_ASSERT_NOT_NULL(c_raw); @@ -576,6 +586,7 @@ static void test_sim_never_calls_forbidden_symbols(void) TEST_ASSERT_NOT_NULL(repl_int_h_raw); TEST_ASSERT_NOT_NULL(repl_params_c_raw); TEST_ASSERT_NOT_NULL(repl_rollup_c_raw); + TEST_ASSERT_NOT_NULL(repl_fmt_c_raw); h = strip_c_comments(h_raw); c = strip_c_comments(c_raw); @@ -589,6 +600,7 @@ static void test_sim_never_calls_forbidden_symbols(void) repl_int_h = strip_c_comments(repl_int_h_raw); repl_params_c = strip_c_comments(repl_params_c_raw); repl_rollup_c = strip_c_comments(repl_rollup_c_raw); + repl_fmt_c = strip_c_comments(repl_fmt_c_raw); TEST_ASSERT_NOT_NULL(h); TEST_ASSERT_NOT_NULL(c); TEST_ASSERT_NOT_NULL(loop); @@ -601,6 +613,7 @@ static void test_sim_never_calls_forbidden_symbols(void) TEST_ASSERT_NOT_NULL(repl_int_h); TEST_ASSERT_NOT_NULL(repl_params_c); TEST_ASSERT_NOT_NULL(repl_rollup_c); + TEST_ASSERT_NOT_NULL(repl_fmt_c); assert_no_forbidden_symbols(h, "sim.h"); assert_no_forbidden_symbols(c, "sim.c"); @@ -614,6 +627,7 @@ static void test_sim_never_calls_forbidden_symbols(void) assert_no_forbidden_symbols(repl_int_h, "repl_internal.h"); assert_no_forbidden_symbols(repl_params_c, "repl_params.c"); assert_no_forbidden_symbols(repl_rollup_c, "repl_rollup.c"); + assert_no_forbidden_symbols(repl_fmt_c, "repl_fmt.c"); free(h_raw); free(c_raw); @@ -627,6 +641,7 @@ static void test_sim_never_calls_forbidden_symbols(void) free(repl_int_h_raw); free(repl_params_c_raw); free(repl_rollup_c_raw); + free(repl_fmt_c_raw); free(h); free(c); free(loop); @@ -639,6 +654,7 @@ static void test_sim_never_calls_forbidden_symbols(void) free(repl_int_h); free(repl_params_c); free(repl_rollup_c); + free(repl_fmt_c); } /* diff --git a/src/scheduler/testsuite/test_sim_print_decision_gate.c b/src/scheduler/testsuite/test_sim_print_decision_gate.c index b0940fd3..5d5999c4 100644 --- a/src/scheduler/testsuite/test_sim_print_decision_gate.c +++ b/src/scheduler/testsuite/test_sim_print_decision_gate.c @@ -115,6 +115,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c index a562ee74..7ddbf832 100644 --- a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c +++ b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c @@ -106,6 +106,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_record_output.c b/src/scheduler/testsuite/test_sim_record_output.c index 2c911728..6653bd96 100644 --- a/src/scheduler/testsuite/test_sim_record_output.c +++ b/src/scheduler/testsuite/test_sim_record_output.c @@ -74,6 +74,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_resource_contention.c b/src/scheduler/testsuite/test_sim_resource_contention.c index f4362068..75cb4f60 100644 --- a/src/scheduler/testsuite/test_sim_resource_contention.c +++ b/src/scheduler/testsuite/test_sim_resource_contention.c @@ -138,6 +138,7 @@ #include "../src/recording_events.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c index 223ddda4..158c1746 100644 --- a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c +++ b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c @@ -106,6 +106,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_session_ordering.c b/src/scheduler/testsuite/test_sim_session_ordering.c index b92e2f18..8d4ede3d 100644 --- a/src/scheduler/testsuite/test_sim_session_ordering.c +++ b/src/scheduler/testsuite/test_sim_session_ordering.c @@ -139,6 +139,7 @@ #include "../src/recording_events.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c index 5146db0d..f869c6df 100644 --- a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c +++ b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c @@ -93,6 +93,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c" diff --git a/src/scheduler/testsuite/test_sim_verify.c b/src/scheduler/testsuite/test_sim_verify.c index 1801c93e..df747ee6 100644 --- a/src/scheduler/testsuite/test_sim_verify.c +++ b/src/scheduler/testsuite/test_sim_verify.c @@ -94,6 +94,7 @@ #include "../src/recording.c" #include "../src/recording_session.c" #include "../src/repl.c" +#include "../src/repl_fmt.c" #include "../src/repl_rollup.c" #include "../src/repl_break.c" #include "../src/repl_info.c"