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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAWMhfRJFnmmnG8h97yUDd
This commit is contained in:
Rongsong Shen 2026-09-02 17:45:06 +00:00
parent 11fe6194ce
commit 5ee1d6cb08
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
29 changed files with 634 additions and 317 deletions

View file

@ -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,

View file

@ -1,9 +1,7 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <editline/readline.h> /* 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;
}

View file

@ -0,0 +1,402 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}

View file

@ -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);

View file

@ -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 <N>" /
* "set output <FILE>|off" / "set verify on|off". `line` re-tokenized via
* repl_split_tokens() (repl.c), same as break/delete/info.
* "set output <FILE>|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 <N> | set output <FILE>|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]);

View file

@ -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()
+ '"'],

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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);
}
/*

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"