feat(scheduler-sim-decision-trace): capture decision outcomes and reconstructed dispatches
Task 2.3: dispatch rows with limit charges listed separately and resolved through the job's matched limits (raw-number fallback), pend reason set deep-copied, 50 hosts in detail with the rest aggregated, and RECONSTRUCTED records holding the dispatch outcome. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
79c5a0cbda
commit
8efe142bad
4 changed files with 1269 additions and 68 deletions
|
|
@ -27,6 +27,19 @@
|
|||
* unmet rows, stored after the host's verdict line, and they set its
|
||||
* SHORTFALL and/or LIMIT verdict.
|
||||
*
|
||||
* Task 2.3 covers the decision outcomes (Requirements 6.1-6.4, 9.4) and the
|
||||
* reconstructed-dispatch record (2.5), over the same fixture plus a workers
|
||||
* table:
|
||||
* - a dispatch keeps per-host and shared resource rows apart from the
|
||||
* limit charges, each charge resolved through the job's matched limits
|
||||
* (or, unresolved, parsed from its name with an unknown limited
|
||||
* resource: the raw-number fallback);
|
||||
* - past 50 hosts, the rest become per-resource and per-limit totals and
|
||||
* an omitted-host count; a pend keeps the first 50 hosts' entries and
|
||||
* the host-less ones and counts the rest per pend-reason code;
|
||||
* - with 50 or fewer hosts the pend copy equals its source under
|
||||
* job_preason_eq(), the equality libxce defines for pend reasons.
|
||||
*
|
||||
* Links src/sched_trace.c and src/sched_trace_capture.c, plus libxce for
|
||||
* the pend-reason, resmgr and container helpers.
|
||||
*/
|
||||
|
|
@ -525,6 +538,7 @@ typedef struct {
|
|||
xce_vres_t vres_lic;
|
||||
xce_vres_t vres_host;
|
||||
xce_vres_t vres_hcap;
|
||||
xce_sched_worker_t w_bare; /* n02: registered, no matched host limits */
|
||||
} rows_fx_t;
|
||||
|
||||
#define ROWS_HOST_IDX 2
|
||||
|
|
@ -593,6 +607,25 @@ static rows_fx_t* rows_fx_new(void)
|
|||
|
||||
fx->w.name = "n01";
|
||||
fx->w.idx = ROWS_HOST_IDX;
|
||||
|
||||
/* workers by name, for the dispatch capture's host-limit lookup */
|
||||
{
|
||||
kvmgr_params_t wk_params = { .key_type = KVMGR_KEY_STRING,
|
||||
.nslots = 16,
|
||||
.delta = 8,
|
||||
.datakiller = NULL };
|
||||
|
||||
fx->core.workers = makeInstance(&xkvmgr_type, &wk_params);
|
||||
TEST_ASSERT_NOT_NULL(fx->core.workers);
|
||||
fx->w_bare.name = "n02";
|
||||
TEST_ASSERT_TRUE(key_add(fx->core.workers, (void*)fx->w.name, &fx->w)
|
||||
>= 0);
|
||||
TEST_ASSERT_TRUE(key_add(fx->core.workers, (void*)fx->w_bare.name,
|
||||
&fx->w_bare)
|
||||
>= 0);
|
||||
fx->w.idx = ROWS_HOST_IDX; /* key_add's slot is not the idx */
|
||||
fx->w_bare.idx = ROWS_HOST_IDX + 1;
|
||||
}
|
||||
return fx;
|
||||
}
|
||||
|
||||
|
|
@ -1023,6 +1056,510 @@ static void test_check_rows_host_rows_count_against_budget(void)
|
|||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* ---- decision outcomes (6.1-6.4, 9.4) and reconstructed records (2.5) - */
|
||||
|
||||
typedef struct {
|
||||
const char* res;
|
||||
double value;
|
||||
} t_av_t;
|
||||
|
||||
static xce_alloc_t* t_alloc(const char* res, double value)
|
||||
{
|
||||
xce_alloc_t* a = (xce_alloc_t*)calloc(1, sizeof(*a));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(a);
|
||||
a->resname = strdup(res);
|
||||
a->reserve = strdup("");
|
||||
a->value = value;
|
||||
return a;
|
||||
}
|
||||
|
||||
static xce_alloc_t** t_allocs(int n, const t_av_t* av)
|
||||
{
|
||||
xce_alloc_t** v = (xce_alloc_t**)calloc((size_t)n + 1, sizeof(*v));
|
||||
int i;
|
||||
|
||||
TEST_ASSERT_NOT_NULL(v);
|
||||
for (i = 0; i < n; i++) {
|
||||
v[i] = t_alloc(av[i].res, av[i].value);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static xce_host_alloc_t* t_host(const char* host, int n, const t_av_t* av)
|
||||
{
|
||||
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
h->host = strdup(host);
|
||||
h->nallocs = n;
|
||||
h->allocs = t_allocs(n, av);
|
||||
return h;
|
||||
}
|
||||
|
||||
static void t_free_allocs(int n, xce_alloc_t** v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; (v != NULL) && (i < n); i++) {
|
||||
if (v[i] != NULL) {
|
||||
free(v[i]->resname);
|
||||
free(v[i]->reserve);
|
||||
free(v[i]);
|
||||
}
|
||||
}
|
||||
free(v);
|
||||
}
|
||||
|
||||
static void t_free_job(xce_job_alloc_t* j)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < j->nhosts; i++) {
|
||||
t_free_allocs(j->allocs[i]->nallocs, j->allocs[i]->allocs);
|
||||
free(j->allocs[i]->host);
|
||||
free(j->allocs[i]);
|
||||
}
|
||||
free(j->allocs);
|
||||
t_free_allocs(j->nres_shared, j->allocs_shared);
|
||||
free(j);
|
||||
}
|
||||
|
||||
static xce_job_alloc_t* t_job(int nhosts)
|
||||
{
|
||||
xce_job_alloc_t* j = (xce_job_alloc_t*)calloc(1, sizeof(*j));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(j);
|
||||
j->jobid = 0x77;
|
||||
j->nhosts = nhosts;
|
||||
j->allocs = (xce_host_alloc_t**)calloc((size_t)nhosts + 1,
|
||||
sizeof(*j->allocs));
|
||||
TEST_ASSERT_NOT_NULL(j->allocs);
|
||||
return j;
|
||||
}
|
||||
|
||||
static void assert_alloc_row(const sched_trace_alloc_row_t* row,
|
||||
const char* res, double value)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_STRING(res, row->res);
|
||||
TEST_ASSERT_EQUAL_FLOAT((float)value, (float)row->value);
|
||||
TEST_ASSERT_NULL(row->limit);
|
||||
TEST_ASSERT_NULL(row->scope);
|
||||
TEST_ASSERT_NULL(row->limited_res);
|
||||
}
|
||||
|
||||
static void assert_charge(const sched_trace_alloc_row_t* row, const char* res,
|
||||
double value, const char* limit, const char* scope,
|
||||
const char* limited)
|
||||
{
|
||||
if (res == NULL) {
|
||||
TEST_ASSERT_NULL(row->res);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_STRING(res, row->res);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_FLOAT((float)value, (float)row->value);
|
||||
TEST_ASSERT_EQUAL_STRING(limit, row->limit);
|
||||
TEST_ASSERT_EQUAL_STRING(scope, row->scope);
|
||||
if (limited == NULL) {
|
||||
TEST_ASSERT_NULL(row->limited_res);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_STRING(limited, row->limited_res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Per-host rows and shared rows, limit charges listed apart: resolved
|
||||
* through obj->hlimits[w->idx] (w by host name) and obj->glimits; a
|
||||
* registered limit flag or, for an unregistered name, the "limit=" prefix
|
||||
* makes a charge; an unresolved charge keeps its parsed name and scope and
|
||||
* an unknown limited resource, so it renders as a raw number (6.1-6.3). */
|
||||
static void test_dispatch_separates_limit_charges(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
xce_job_alloc_t* job = t_job(2);
|
||||
const t_av_t h1[] = { { "mem", 8 }, { "limit=host_slots/u=alice", 4 },
|
||||
{ "slots", 4 }, { "limit=hcap", 4 } };
|
||||
const t_av_t h2[] = { { "mem", 2 }, { "limit=host_slots/u=alice", 1 },
|
||||
{ "gpu", 1 }, { "limit=ghost/u=alice", 1 } };
|
||||
const t_av_t sh[] = { { "lic", 1 },
|
||||
{ "limit=lic_cap/u=alice/q=normal", 1 },
|
||||
{ "limit=site_total/p=proj1", 1 } };
|
||||
const sched_trace_dispatch_out_t* d;
|
||||
|
||||
job->allocs[0] = t_host("n01", 4, h1);
|
||||
job->allocs[1] = t_host("n02", 4, h2);
|
||||
job->nres_shared = 3;
|
||||
job->allocs_shared = t_allocs(3, sh);
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_NONE, rec->outcome);
|
||||
sched_trace_capture_dispatch(rec, &fx->core, &fx->obj, job);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_DISPATCH, rec->outcome);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->nentries); /* outside the line budget */
|
||||
d = &rec->dispatch;
|
||||
TEST_ASSERT_EQUAL_INT(2, d->nhosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, d->omitted_hosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, d->nagg_res);
|
||||
TEST_ASSERT_EQUAL_INT(0, d->nagg_charges);
|
||||
|
||||
/* n01: resources in allocation order, charges resolved through its
|
||||
* host limits, the host appended to the scope */
|
||||
TEST_ASSERT_EQUAL_STRING("n01", d->hosts[0].host);
|
||||
TEST_ASSERT_EQUAL_INT(2, d->hosts[0].nres);
|
||||
assert_alloc_row(&d->hosts[0].res[0], "mem", 8);
|
||||
assert_alloc_row(&d->hosts[0].res[1], "slots", 4);
|
||||
TEST_ASSERT_EQUAL_INT(2, d->hosts[0].ncharges);
|
||||
assert_charge(&d->hosts[0].charges[0], "limit=host_slots/u=alice", 4,
|
||||
"host_slots", "u=alice,h=n01", "slots");
|
||||
assert_charge(&d->hosts[0].charges[1], "limit=hcap", 4, "hcap", "h=n01",
|
||||
"slots");
|
||||
|
||||
/* n02 has no matched host limits: the charge is unresolved; "gpu" is
|
||||
* not registered and has no limit prefix: a resource; "limit=ghost..."
|
||||
* is not registered: a charge by its prefix */
|
||||
TEST_ASSERT_EQUAL_STRING("n02", d->hosts[1].host);
|
||||
TEST_ASSERT_EQUAL_INT(2, d->hosts[1].nres);
|
||||
assert_alloc_row(&d->hosts[1].res[0], "mem", 2);
|
||||
assert_alloc_row(&d->hosts[1].res[1], "gpu", 1);
|
||||
TEST_ASSERT_EQUAL_INT(2, d->hosts[1].ncharges);
|
||||
assert_charge(&d->hosts[1].charges[0], "limit=host_slots/u=alice", 1,
|
||||
"host_slots", "u=alice,h=n02", NULL);
|
||||
assert_charge(&d->hosts[1].charges[1], "limit=ghost/u=alice", 1, "ghost",
|
||||
"u=alice,h=n02", NULL);
|
||||
|
||||
/* shared: once for the job, charges resolved through glimits */
|
||||
TEST_ASSERT_EQUAL_INT(1, d->nshared);
|
||||
assert_alloc_row(&d->shared[0], "lic", 1);
|
||||
TEST_ASSERT_EQUAL_INT(2, d->nshared_charges);
|
||||
assert_charge(&d->shared_charges[0], "limit=lic_cap/u=alice/q=normal", 1,
|
||||
"lic_cap", "u=alice,q=normal", "lic");
|
||||
assert_charge(&d->shared_charges[1], "limit=site_total/p=proj1", 1,
|
||||
"site_total", "p=proj1", NULL);
|
||||
|
||||
/* a self-contained copy */
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts[0].host));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts[0].res[0].res));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts[0].charges[0].limit));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts[0].charges[0].scope));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->hosts[0].charges[0].limited_res));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->shared_charges[0].limited_res));
|
||||
t_free_job(job);
|
||||
rows_fx_free(fx);
|
||||
TEST_ASSERT_EQUAL_STRING("n01", d->hosts[0].host);
|
||||
TEST_ASSERT_EQUAL_STRING("u=alice,h=n01", d->hosts[0].charges[0].scope);
|
||||
TEST_ASSERT_EQUAL_STRING("lic", d->shared_charges[0].limited_res);
|
||||
TEST_ASSERT_FALSE(rec->incomplete);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* No schedobj (a job this replay never evaluated): every charge is still
|
||||
* listed as a charge, by its flag, with the name and scope parsed from the
|
||||
* resource name and the amount left as a raw number (design "TraceCapture",
|
||||
* the raw-number fallback). */
|
||||
static void test_dispatch_raw_number_fallback(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
xce_job_alloc_t* job = t_job(1);
|
||||
const t_av_t h1[] = { { "limit=host_slots/u=alice", 3 }, { "mem", 1 } };
|
||||
const t_av_t sh[] = { { "limit=lic_cap/u=alice/q=normal", 2 } };
|
||||
const sched_trace_dispatch_out_t* d;
|
||||
|
||||
job->allocs[0] = t_host("n01", 2, h1);
|
||||
job->nres_shared = 1;
|
||||
job->allocs_shared = t_allocs(1, sh);
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_dispatch(rec, &fx->core, NULL, job);
|
||||
d = &rec->dispatch;
|
||||
TEST_ASSERT_EQUAL_INT(1, d->hosts[0].nres);
|
||||
assert_alloc_row(&d->hosts[0].res[0], "mem", 1);
|
||||
TEST_ASSERT_EQUAL_INT(1, d->hosts[0].ncharges);
|
||||
assert_charge(&d->hosts[0].charges[0], "limit=host_slots/u=alice", 3,
|
||||
"host_slots", "u=alice,h=n01", NULL);
|
||||
TEST_ASSERT_EQUAL_INT(0, d->nshared);
|
||||
TEST_ASSERT_EQUAL_INT(1, d->nshared_charges);
|
||||
assert_charge(&d->shared_charges[0], "limit=lic_cap/u=alice/q=normal", 2,
|
||||
"lic_cap", "u=alice,q=normal", NULL);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
/* no resmgr entry and no prefix either way: no core at all */
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_dispatch(rec, NULL, NULL, job);
|
||||
d = &rec->dispatch;
|
||||
TEST_ASSERT_EQUAL_INT(1, d->hosts[0].nres);
|
||||
TEST_ASSERT_EQUAL_INT(1, d->hosts[0].ncharges);
|
||||
assert_charge(&d->hosts[0].charges[0], "limit=host_slots/u=alice", 3,
|
||||
"host_slots", "u=alice,h=n01", NULL);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
t_free_job(job);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* A reconstructed dispatch (2.5): a RECONSTRUCTED record with the session
|
||||
* header, no entries and the dispatch outcome, its charges resolved the same
|
||||
* way. */
|
||||
static void test_reconstructed_record_holds_dispatch(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
xce_job_alloc_t* job = t_job(1);
|
||||
const t_av_t h1[] = { { "slots", 2 }, { "limit=host_slots/u=alice", 2 } };
|
||||
|
||||
job->allocs[0] = t_host("n01", 2, h1);
|
||||
|
||||
TEST_ASSERT_NULL(
|
||||
sched_trace_rec_reconstructed(NULL, &fx->core, 0x99, &fx->obj, job));
|
||||
rec = sched_trace_rec_reconstructed(&ctl, &fx->core, 0x99, &fx->obj, job);
|
||||
TEST_ASSERT_NOT_NULL(rec);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_REC_RECONSTRUCTED, rec->kind);
|
||||
TEST_ASSERT_EQUAL_UINT64(0x99, rec->jobid);
|
||||
TEST_ASSERT_EQUAL_UINT64(2, rec->session_no);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_DISPATCH, rec->outcome);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->dispatch.nhosts);
|
||||
assert_alloc_row(&rec->dispatch.hosts[0].res[0], "slots", 2);
|
||||
assert_charge(&rec->dispatch.hosts[0].charges[0],
|
||||
"limit=host_slots/u=alice", 2, "host_slots", "u=alice,h=n01",
|
||||
"slots");
|
||||
t_free_job(job);
|
||||
rows_fx_free(fx);
|
||||
TEST_ASSERT_EQUAL_STRING("n01", rec->dispatch.hosts[0].host);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
/* a job with no schedobj: still built, charges unresolved */
|
||||
fx = rows_fx_new();
|
||||
job = t_job(1);
|
||||
job->allocs[0] = t_host("n01", 2, h1);
|
||||
rec = sched_trace_rec_reconstructed(&ctl, &fx->core, 0x99, NULL, job);
|
||||
TEST_ASSERT_NOT_NULL(rec);
|
||||
TEST_ASSERT_NULL(rec->dispatch.hosts[0].charges[0].limited_res);
|
||||
sched_trace_rec_free(rec);
|
||||
t_free_job(job);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* More than 50 hosts: the first 50 in detail, the rest as an omitted-host
|
||||
* count, per-resource totals without the limits, and per-limit totals with
|
||||
* the host-free scope (9.4). */
|
||||
static void test_dispatch_aggregates_hosts_past_50(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int nh;
|
||||
|
||||
for (nh = XCE_TRACE_OUTCOME_HOSTS + 1; nh <= XCE_TRACE_OUTCOME_HOSTS + 3;
|
||||
nh += 2) {
|
||||
xce_job_alloc_t* job = t_job(nh);
|
||||
const sched_trace_dispatch_out_t* d;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nh; i++) {
|
||||
char name[16];
|
||||
t_av_t av[3] = { { "mem", 2 + i },
|
||||
{ "limit=host_slots/u=alice", 1 },
|
||||
{ "slots", 1 } };
|
||||
|
||||
snprintf(name, sizeof(name), "h%02d", i);
|
||||
job->allocs[i] = t_host(name, 3, av);
|
||||
}
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_dispatch(rec, &fx->core, &fx->obj, job);
|
||||
d = &rec->dispatch;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_OUTCOME_HOSTS, d->nhosts);
|
||||
TEST_ASSERT_EQUAL_STRING("h49", d->hosts[49].host);
|
||||
TEST_ASSERT_EQUAL_INT(1, d->hosts[49].ncharges);
|
||||
TEST_ASSERT_EQUAL_INT(nh - XCE_TRACE_OUTCOME_HOSTS, d->omitted_hosts);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, d->nagg_res);
|
||||
if (nh == XCE_TRACE_OUTCOME_HOSTS + 1) {
|
||||
assert_alloc_row(&d->agg_res[0], "mem", 52); /* h50 only */
|
||||
} else {
|
||||
assert_alloc_row(&d->agg_res[0], "mem", 52 + 53 + 54);
|
||||
}
|
||||
assert_alloc_row(&d->agg_res[1], "slots", nh - XCE_TRACE_OUTCOME_HOSTS);
|
||||
TEST_ASSERT_EQUAL_INT(1, d->nagg_charges);
|
||||
assert_charge(&d->agg_charges[0], NULL, nh - XCE_TRACE_OUTCOME_HOSTS,
|
||||
"host_slots", "u=alice", NULL);
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->agg_res[0].res));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, d->agg_charges[0].limit));
|
||||
t_free_job(job);
|
||||
TEST_ASSERT_EQUAL_STRING("mem", d->agg_res[0].res);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* exactly 50: nothing omitted */
|
||||
{
|
||||
xce_job_alloc_t* job = t_job(XCE_TRACE_OUTCOME_HOSTS);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < XCE_TRACE_OUTCOME_HOSTS; i++) {
|
||||
char name[16];
|
||||
t_av_t av[1] = { { "mem", 1 } };
|
||||
|
||||
snprintf(name, sizeof(name), "h%02d", i);
|
||||
job->allocs[i] = t_host(name, 1, av);
|
||||
}
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_dispatch(rec, &fx->core, &fx->obj, job);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_OUTCOME_HOSTS, rec->dispatch.nhosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->dispatch.omitted_hosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->dispatch.nagg_res);
|
||||
sched_trace_rec_free(rec);
|
||||
t_free_job(job);
|
||||
}
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
static void test_dispatch_null_alloc_is_empty(void)
|
||||
{
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec = sched_trace_rec_new(&ctl, 0x77,
|
||||
XCE_TRACE_REC_DECISION);
|
||||
|
||||
sched_trace_capture_dispatch(rec, NULL, NULL, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_DISPATCH, rec->outcome);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->dispatch.nhosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->dispatch.nshared);
|
||||
TEST_ASSERT_FALSE(rec->incomplete);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* 50 or fewer hosts: the pend copy is the decision's set, equal under the
|
||||
* scheduler's own pend-reason equality, deep-copied into the arena (6.4). */
|
||||
static void test_pend_copy_equals_source(void)
|
||||
{
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec = sched_trace_rec_new(&ctl, 0x55,
|
||||
XCE_TRACE_REC_DECISION);
|
||||
xce_pendreason_t* r = make_pendreason(0x55);
|
||||
char* why = NULL;
|
||||
int i;
|
||||
|
||||
add_job_preason(r->reasons, PREASON_BAD_RESREQ, NULL, NULL); /* main */
|
||||
for (i = 0; i < XCE_TRACE_OUTCOME_HOSTS; i++) {
|
||||
char name[16];
|
||||
|
||||
snprintf(name, sizeof(name), "h%02d", i);
|
||||
add_job_preason(r->reasons, PREASON_MATCH_FAIL, name, NULL);
|
||||
if ((i % 7) == 0) {
|
||||
add_job_preason(r->reasons, PREASON_NO_RESOURCE, name, "mem");
|
||||
}
|
||||
}
|
||||
add_job_preason(r->reasons, PREASON_NO_RESOURCE, NULL, "lic"); /* shared */
|
||||
|
||||
sched_trace_capture_pend(rec, r);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_PEND, rec->outcome);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->nentries);
|
||||
TEST_ASSERT_NOT_NULL(rec->pend.reasons);
|
||||
TEST_ASSERT_TRUE_MESSAGE(job_preason_eq(rec->pend.reasons, r->reasons,
|
||||
&why),
|
||||
(why != NULL) ? why : "");
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->pend.omitted_hosts);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->pend.ncodes);
|
||||
|
||||
/* no aliasing: every level is the record's own */
|
||||
TEST_ASSERT_TRUE(in_arena(rec, rec->pend.reasons));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, rec->pend.reasons->host_based_reasons));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, rec->pend.reasons->host_based_reasons[0]));
|
||||
TEST_ASSERT_TRUE(
|
||||
in_arena(rec, rec->pend.reasons->host_based_reasons[0]->host));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, rec->pend.reasons->res_based_reasons[0]));
|
||||
TEST_ASSERT_TRUE(
|
||||
in_arena(rec, rec->pend.reasons->res_based_reasons[0]->resource));
|
||||
free_pendreason(r);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_BAD_RESREQ, rec->pend.reasons->main_reason);
|
||||
TEST_ASSERT_EQUAL_STRING("h49",
|
||||
rec->pend.reasons->host_based_reasons[49]->host);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
/* main reason only, and an empty set: still equal */
|
||||
r = make_pendreason(0x55);
|
||||
rec = sched_trace_rec_new(&ctl, 0x55, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_pend(rec, r);
|
||||
TEST_ASSERT_TRUE(job_preason_eq(rec->pend.reasons, r->reasons, NULL));
|
||||
sched_trace_rec_free(rec);
|
||||
free_pendreason(r);
|
||||
}
|
||||
|
||||
/* More than 50 distinct hosts: the main reason, host-less entries and
|
||||
* every entry of the first 50 hosts (also later ones) are kept; the other
|
||||
* hosts' entries are counted per pend-reason code (9.4). */
|
||||
static void test_pend_aggregates_hosts_past_50(void)
|
||||
{
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec = sched_trace_rec_new(&ctl, 0x55,
|
||||
XCE_TRACE_REC_DECISION);
|
||||
xce_pendreason_t* r = make_pendreason(0x55);
|
||||
const xce_job_preason_t* c;
|
||||
int i;
|
||||
|
||||
add_job_preason(r->reasons, PREASON_BAD_RESREQ, NULL, NULL);
|
||||
for (i = 0; i < XCE_TRACE_OUTCOME_HOSTS + 1; i++) {
|
||||
char name[16];
|
||||
|
||||
snprintf(name, sizeof(name), "h%02d", i);
|
||||
add_job_preason(r->reasons, PREASON_MATCH_FAIL, name, NULL);
|
||||
}
|
||||
/* resource entries: a kept host, the omitted host, a host-less one */
|
||||
add_job_preason(r->reasons, PREASON_NO_RESOURCE, "h03", "mem");
|
||||
add_job_preason(r->reasons, PREASON_NO_RESOURCE, "h50", "mem");
|
||||
add_job_preason(r->reasons, PREASON_NO_RESOURCE, NULL, "lic");
|
||||
|
||||
sched_trace_capture_pend(rec, r);
|
||||
c = rec->pend.reasons;
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_BAD_RESREQ, c->main_reason);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_OUTCOME_HOSTS, c->nhost_reasons);
|
||||
TEST_ASSERT_EQUAL_STRING("h00", c->host_based_reasons[0]->host);
|
||||
TEST_ASSERT_EQUAL_STRING("h49", c->host_based_reasons[49]->host);
|
||||
TEST_ASSERT_EQUAL_INT(2, c->nres_reasons);
|
||||
TEST_ASSERT_EQUAL_STRING("h03", c->res_based_reasons[0]->host);
|
||||
TEST_ASSERT_EQUAL_STRING("mem", c->res_based_reasons[0]->resource);
|
||||
TEST_ASSERT_EQUAL_STRING("", c->res_based_reasons[1]->host); /* shared */
|
||||
TEST_ASSERT_EQUAL_STRING("lic", c->res_based_reasons[1]->resource);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_NO_RESOURCE, c->res_based_reasons[1]->reason);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->pend.omitted_hosts);
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->pend.ncodes);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_MATCH_FAIL, rec->pend.omitted_codes[0].code);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->pend.omitted_codes[0].count);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_NO_RESOURCE, rec->pend.omitted_codes[1].code);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->pend.omitted_codes[1].count);
|
||||
TEST_ASSERT_TRUE(in_arena(rec, rec->pend.omitted_codes));
|
||||
free_pendreason(r);
|
||||
TEST_ASSERT_EQUAL_STRING("h49", c->host_based_reasons[49]->host);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
static void test_pend_null_reason_has_no_set(void)
|
||||
{
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec = sched_trace_rec_new(&ctl, 0x55,
|
||||
XCE_TRACE_REC_DECISION);
|
||||
xce_pendreason_t empty;
|
||||
|
||||
sched_trace_capture_pend(rec, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_OUTCOME_PEND, rec->outcome);
|
||||
TEST_ASSERT_NULL(rec->pend.reasons);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
memset(&empty, 0, sizeof(empty));
|
||||
rec = sched_trace_rec_new(&ctl, 0x55, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_capture_pend(rec, &empty);
|
||||
TEST_ASSERT_NULL(rec->pend.reasons);
|
||||
TEST_ASSERT_FALSE(rec->incomplete);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
xstring_table_init(1024);
|
||||
|
|
@ -1048,5 +1585,13 @@ int main(void)
|
|||
RUN_TEST(test_check_rows_host_summary_tallies_limit);
|
||||
RUN_TEST(test_check_rows_scope_edges);
|
||||
RUN_TEST(test_check_rows_host_rows_count_against_budget);
|
||||
RUN_TEST(test_dispatch_separates_limit_charges);
|
||||
RUN_TEST(test_dispatch_raw_number_fallback);
|
||||
RUN_TEST(test_reconstructed_record_holds_dispatch);
|
||||
RUN_TEST(test_dispatch_aggregates_hosts_past_50);
|
||||
RUN_TEST(test_dispatch_null_alloc_is_empty);
|
||||
RUN_TEST(test_pend_copy_equals_source);
|
||||
RUN_TEST(test_pend_aggregates_hosts_past_50);
|
||||
RUN_TEST(test_pend_null_reason_has_no_set);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue