feat(scheduler-sim-decision-trace): capture shortfall and limit rows from the satisfaction check
Task 2.2: each checked row carries name, flags, value type, total, in-use, reserved, requested and met (same comparison as unsatisfy_()); limit rows resolve through the scope's matched limits and never count as shortfalls; the cluster-wide check keeps every row, a host only unmet rows, which set its shortfall/limit verdict. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
90d417bb1b
commit
258267fa48
6 changed files with 977 additions and 38 deletions
|
|
@ -13,8 +13,22 @@
|
|||
* structures afterwards leaves the record unchanged (design "Invariants":
|
||||
* a record never references scheduler-owned memory).
|
||||
*
|
||||
* Task 2.2 covers the satisfaction check's rows (Requirements 4.1, 4.2,
|
||||
* 5.4, 5.5), over a fixture core whose resmgr registers plain resources and
|
||||
* limit vres resources, with a cluster-wide limit in the schedobj's
|
||||
* glimits and a host-scoped limit in its hlimits for the test worker:
|
||||
* - every row copies the resource name, flags, value type, total,
|
||||
* in-use, provisionally reserved, requested and met;
|
||||
* - a row whose resource has XCE_RESF_LIMIT is a limit row, resolved
|
||||
* through the scope's matched limits to (limit name, scope instance,
|
||||
* limited resource), "h=<host>" appended in a host scope; it is never
|
||||
* reported as a shortfall;
|
||||
* - the cluster-wide scope keeps every row; a host scope keeps only the
|
||||
* unmet rows, stored after the host's verdict line, and they set its
|
||||
* SHORTFALL and/or LIMIT verdict.
|
||||
*
|
||||
* Links src/sched_trace.c and src/sched_trace_capture.c, plus libxce for
|
||||
* the pend-reason helpers.
|
||||
* the pend-reason, resmgr and container helpers.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -23,9 +37,13 @@
|
|||
#include <unity/unity.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "kvmgr.h"
|
||||
#include "preason.h"
|
||||
#include "resobj.h"
|
||||
#include "sched/trace.h"
|
||||
#include "xlist.h"
|
||||
#include "xstring.h"
|
||||
#include "zarray.h"
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
|
@ -486,8 +504,528 @@ static void test_attempt_reasons_counts_against_the_budget(void)
|
|||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* ---- satisfaction check rows (4.1, 4.2, 5.4, 5.5) -------------------- */
|
||||
|
||||
typedef struct {
|
||||
xce_sched_core_t core;
|
||||
xce_sched_obj_t obj;
|
||||
xce_sched_worker_t w;
|
||||
xlist_t hl; /* the worker's matched host limits */
|
||||
/* registered resources (resmgr copies) */
|
||||
xce_resource_t* mem;
|
||||
xce_resource_t* lic;
|
||||
xce_resource_t* slots;
|
||||
xce_resource_t* lim_lic; /* cluster-wide limit vres, scoped */
|
||||
xce_resource_t* lim_orphan; /* limit vres matched by no limit */
|
||||
xce_resource_t* lim_host; /* host limit vres, scoped */
|
||||
xce_resource_t* lim_hcap; /* host limit vres, no consumer scope */
|
||||
xce_limit_t limit_lic;
|
||||
xce_limit_t limit_host;
|
||||
xce_limit_t limit_hcap;
|
||||
xce_vres_t vres_lic;
|
||||
xce_vres_t vres_host;
|
||||
xce_vres_t vres_hcap;
|
||||
} rows_fx_t;
|
||||
|
||||
#define ROWS_HOST_IDX 2
|
||||
|
||||
static xce_resource_t* reg_res(var resmgr, const char* name, int flags,
|
||||
int valtype)
|
||||
{
|
||||
xce_resource_t res;
|
||||
xce_resource_t* r;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.name = (char*)name;
|
||||
res.flags = flags;
|
||||
res.valtype = valtype;
|
||||
r = makeResource(resmgr, &res);
|
||||
TEST_ASSERT_NOT_NULL(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static rows_fx_t* rows_fx_new(void)
|
||||
{
|
||||
kvmgr_params_t res_params = { .key_type = KVMGR_KEY_STRING,
|
||||
.nslots = 16,
|
||||
.delta = 8,
|
||||
.datakiller = NULL };
|
||||
rows_fx_t* fx = (rows_fx_t*)calloc(1, sizeof(*fx));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(fx);
|
||||
fx->core.resmgr = makeInstance(&xkvmgr_type, &res_params);
|
||||
TEST_ASSERT_NOT_NULL(fx->core.resmgr);
|
||||
|
||||
fx->mem = reg_res(fx->core.resmgr, "mem", XCE_RESF_CONSUMABLE, 1);
|
||||
fx->lic = reg_res(fx->core.resmgr, "lic",
|
||||
XCE_RESF_SHARED | XCE_RESF_CONSUMABLE, 1);
|
||||
fx->slots = reg_res(fx->core.resmgr, "slots", XCE_RESF_CONSUMABLE, 2);
|
||||
fx->lim_lic = reg_res(fx->core.resmgr, "limit=lic_cap/u=alice/q=normal",
|
||||
XCE_RESF_LIMIT | XCE_RESF_SHARED, 1);
|
||||
fx->lim_orphan = reg_res(fx->core.resmgr, "limit=site_total/p=proj1",
|
||||
XCE_RESF_LIMIT | XCE_RESF_SHARED, 1);
|
||||
fx->lim_host = reg_res(fx->core.resmgr, "limit=host_slots/u=alice",
|
||||
XCE_RESF_LIMIT, 2);
|
||||
fx->lim_hcap = reg_res(fx->core.resmgr, "limit=hcap", XCE_RESF_LIMIT,
|
||||
2);
|
||||
|
||||
fx->limit_lic.name = "lic_cap";
|
||||
fx->limit_host.name = "host_slots";
|
||||
fx->limit_hcap.name = "hcap";
|
||||
fx->vres_lic.vres = fx->lim_lic;
|
||||
fx->vres_lic.res = fx->lic;
|
||||
fx->vres_lic.limit = &fx->limit_lic;
|
||||
fx->vres_host.vres = fx->lim_host;
|
||||
fx->vres_host.res = fx->slots;
|
||||
fx->vres_host.limit = &fx->limit_host;
|
||||
fx->vres_hcap.vres = fx->lim_hcap;
|
||||
fx->vres_hcap.res = fx->slots;
|
||||
fx->vres_hcap.limit = &fx->limit_hcap;
|
||||
|
||||
xlist_init(&fx->obj.glimits, 4);
|
||||
xlist_store(&fx->obj.glimits, &fx->vres_lic);
|
||||
xlist_init(&fx->hl, 4);
|
||||
xlist_store(&fx->hl, &fx->vres_host);
|
||||
xlist_store(&fx->hl, &fx->vres_hcap);
|
||||
zarray_init(&fx->obj.hlimits, 8, NULL);
|
||||
TEST_ASSERT_EQUAL_INT(0, zarray_put(&fx->obj.hlimits, ROWS_HOST_IDX,
|
||||
&fx->hl));
|
||||
|
||||
fx->w.name = "n01";
|
||||
fx->w.idx = ROWS_HOST_IDX;
|
||||
return fx;
|
||||
}
|
||||
|
||||
static void rows_fx_free(rows_fx_t* fx)
|
||||
{
|
||||
zarray_fini(&fx->obj.hlimits);
|
||||
xlist_fini(&fx->hl);
|
||||
xlist_fini(&fx->obj.glimits);
|
||||
free(fx);
|
||||
}
|
||||
|
||||
/* A dense uncommitted vector keyed by resource index (as schedobj.c fills
|
||||
* the context's), all zero except `idx` = `val`. */
|
||||
static void make_uncomit(xvec_t* v, float* buf, int n, int idx, float val)
|
||||
{
|
||||
memset(buf, 0, sizeof(float) * (size_t)n);
|
||||
if ((idx >= 0) && (idx < n)) {
|
||||
buf[idx] = val;
|
||||
}
|
||||
v->count = n;
|
||||
v->elsize = sizeof(float);
|
||||
v->data = buf;
|
||||
}
|
||||
|
||||
static sched_trace_entry_t* entry_n(xce_sched_trace_rec_t* rec, int k)
|
||||
{
|
||||
sched_trace_entry_t* e = rec->entries;
|
||||
|
||||
while ((e != NULL) && (k-- > 0)) {
|
||||
e = e->next;
|
||||
}
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void assert_resource_row(const sched_trace_entry_t* e, int type,
|
||||
const char* res, int flags, int valtype,
|
||||
float total, float inuse, float reserved,
|
||||
float requested, bool met)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(type, e->type);
|
||||
TEST_ASSERT_EQUAL_STRING(res, e->u.row.res);
|
||||
TEST_ASSERT_EQUAL_INT(flags, e->u.row.flags);
|
||||
TEST_ASSERT_EQUAL_INT(valtype, e->u.row.valtype);
|
||||
TEST_ASSERT_EQUAL_FLOAT(total, e->u.row.total);
|
||||
TEST_ASSERT_EQUAL_FLOAT(inuse, e->u.row.inuse);
|
||||
TEST_ASSERT_EQUAL_FLOAT(reserved, e->u.row.reserved);
|
||||
TEST_ASSERT_EQUAL_FLOAT(requested, e->u.row.requested);
|
||||
TEST_ASSERT_EQUAL(met, e->u.row.met);
|
||||
}
|
||||
|
||||
static void assert_limit_row(const sched_trace_entry_t* e, const char* limit,
|
||||
const char* scope, const char* limited)
|
||||
{
|
||||
TEST_ASSERT_TRUE((e->u.row.flags & XCE_RESF_LIMIT) != 0);
|
||||
TEST_ASSERT_EQUAL_STRING(limit, e->u.row.limit);
|
||||
TEST_ASSERT_EQUAL_STRING(scope, e->u.row.scope);
|
||||
if (limited == NULL) {
|
||||
TEST_ASSERT_NULL(e->u.row.limited_res);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_STRING(limited, e->u.row.limited_res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cluster-wide scope: every row is kept, met or not, and limit rows are
|
||||
* resolved through obj->glimits (4.1, 4.2). */
|
||||
static void test_check_rows_cluster_scope_keeps_every_row(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
float ubuf[16];
|
||||
xvec_t uncomit;
|
||||
int idxs[4] = { fx->mem->idx, fx->lic->idx, fx->lim_lic->idx,
|
||||
fx->lim_orphan->idx };
|
||||
float total[4] = { 64, 10, 5, 3 };
|
||||
float inuse[4] = { 60, 9, 4, 0 };
|
||||
float wanted[4] = { 8, 1, 2, 1 };
|
||||
sched_trace_entry_t* e;
|
||||
int k;
|
||||
|
||||
make_uncomit(&uncomit, ubuf, 16, fx->lim_lic->idx, 1);
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_check_begin(rec, &fx->obj);
|
||||
sched_trace_check_rows(rec, &fx->core, 4, idxs, total, inuse, &uncomit,
|
||||
wanted);
|
||||
sched_trace_check_end(rec, 0, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(5, rec->nentries);
|
||||
/* mem: 64 < 60 + 0 + 8: not met, a resource row */
|
||||
e = entry_n(rec, 0);
|
||||
assert_resource_row(e, SCHED_TRACE_E_CHECK_ROW, "mem", XCE_RESF_CONSUMABLE,
|
||||
1, 64, 60, 0, 8, false);
|
||||
TEST_ASSERT_NULL(e->u.row.limit);
|
||||
TEST_ASSERT_EQUAL_INT(fx->mem->idx, e->u.row.idx);
|
||||
/* lic: 10 < 9 + 0 + 1 is false: met, kept anyway */
|
||||
e = entry_n(rec, 1);
|
||||
assert_resource_row(e, SCHED_TRACE_E_CHECK_ROW, "lic",
|
||||
XCE_RESF_SHARED | XCE_RESF_CONSUMABLE, 1, 10, 9, 0, 1,
|
||||
true);
|
||||
TEST_ASSERT_NULL(e->u.row.limit);
|
||||
/* the cluster-wide limit: 5 < 4 + 1 + 2, reserved from the dense
|
||||
* vector at its own index; a limit row, not a shortfall */
|
||||
e = entry_n(rec, 2);
|
||||
assert_resource_row(e, SCHED_TRACE_E_CHECK_ROW,
|
||||
"limit=lic_cap/u=alice/q=normal",
|
||||
XCE_RESF_LIMIT | XCE_RESF_SHARED, 1, 5, 4, 1, 2, false);
|
||||
assert_limit_row(e, "lic_cap", "u=alice,q=normal", "lic");
|
||||
/* a limit vres in no matched limit: name and scope parsed from its
|
||||
* resource name, limited resource unknown */
|
||||
e = entry_n(rec, 3);
|
||||
assert_resource_row(e, SCHED_TRACE_E_CHECK_ROW, "limit=site_total/p=proj1",
|
||||
XCE_RESF_LIMIT | XCE_RESF_SHARED, 1, 3, 0, 0, 1, true);
|
||||
assert_limit_row(e, "site_total", "p=proj1", NULL);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_CHECK_END, entry_n(rec, 4)->type);
|
||||
/* rows sit at the check's level, with the check-end line */
|
||||
TEST_ASSERT_EQUAL_INT(entry_n(rec, 4)->depth, entry_n(rec, 0)->depth);
|
||||
|
||||
/* no host verdict and no limit tally from the cluster-wide check */
|
||||
TEST_ASSERT_EQUAL_UINT(0, rec->host_row_verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->pos_nlimits);
|
||||
|
||||
/* copies: the record survives its sources */
|
||||
for (k = 0; k < 4; k++) {
|
||||
e = entry_n(rec, k);
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.res));
|
||||
if (e->u.row.limit != NULL) {
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.limit));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.scope));
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_TRUE(in_arena(rec, entry_n(rec, 2)->u.row.limited_res));
|
||||
total[0] = 1;
|
||||
inuse[0] = 1;
|
||||
ubuf[fx->lim_lic->idx] = 9;
|
||||
TEST_ASSERT_EQUAL_FLOAT(64, entry_n(rec, 0)->u.row.total);
|
||||
TEST_ASSERT_EQUAL_FLOAT(1, entry_n(rec, 2)->u.row.reserved);
|
||||
rows_fx_free(fx);
|
||||
TEST_ASSERT_EQUAL_STRING("lic_cap", entry_n(rec, 2)->u.row.limit);
|
||||
TEST_ASSERT_FALSE(rec->incomplete);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* Host scope: only unmet rows are kept, after the host's verdict line,
|
||||
* one level below it; the limit row resolves through obj->hlimits with
|
||||
* the host appended to its scope, and sets LIMIT, never SHORTFALL (5.4,
|
||||
* 5.5). */
|
||||
static void test_check_rows_host_scope_keeps_unmet_rows(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
float ubuf[16];
|
||||
xvec_t uncomit;
|
||||
int idxs[4] = { fx->mem->idx, fx->slots->idx, fx->lim_host->idx,
|
||||
fx->lim_hcap->idx };
|
||||
float total[4] = { 64, 8, 10, 100 };
|
||||
float inuse[4] = { 60, 2, 9, 3 };
|
||||
float wanted[4] = { 8, 4, 2, 4 };
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
make_uncomit(&uncomit, ubuf, 16, fx->mem->idx, 0.5f);
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 4, idxs, total, inuse, &uncomit,
|
||||
wanted);
|
||||
/* the rows wait for the verdict: nothing stored yet */
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SHORTFALL | XCE_TRACE_V_LIMIT,
|
||||
rec->host_row_verdicts);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(3, rec->nentries);
|
||||
e = entry_n(rec, 0);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_HOST, e->type);
|
||||
TEST_ASSERT_EQUAL_STRING("n01", e->u.host.host);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SHORTFALL | XCE_TRACE_V_LIMIT,
|
||||
e->u.host.verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_PHASE_PERHOST, e->u.host.phase);
|
||||
|
||||
/* mem: 64 < 60 + 0.5 + 8 */
|
||||
e = entry_n(rec, 1);
|
||||
assert_resource_row(e, SCHED_TRACE_E_HOST_ROW, "mem", XCE_RESF_CONSUMABLE,
|
||||
1, 64, 60, 0.5f, 8, false);
|
||||
TEST_ASSERT_NULL(e->u.row.limit);
|
||||
TEST_ASSERT_EQUAL_INT(entry_n(rec, 0)->depth + 1, e->depth);
|
||||
/* slots (met) and hcap (met) are not kept; host_slots is */
|
||||
e = entry_n(rec, 2);
|
||||
assert_resource_row(e, SCHED_TRACE_E_HOST_ROW, "limit=host_slots/u=alice",
|
||||
XCE_RESF_LIMIT, 2, 10, 9, 0, 2, false);
|
||||
assert_limit_row(e, "host_slots", "u=alice,h=n01", "slots");
|
||||
TEST_ASSERT_EQUAL_INT(entry_n(rec, 0)->depth + 1, e->depth);
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.res));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.scope));
|
||||
TEST_ASSERT_TRUE(in_arena(rec, e->u.row.limited_res));
|
||||
|
||||
/* one evaluation, counted under each kind it matched */
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->counters.host_evals[2]); /* SHORTFALL */
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->counters.host_evals[3]); /* LIMIT */
|
||||
rows_fx_free(fx);
|
||||
TEST_ASSERT_EQUAL_STRING("u=alice,h=n01", entry_n(rec, 2)->u.row.scope);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
|
||||
/* A host whose only unmet row is a limit is a limit rejection alone: the
|
||||
* limit is never also reported as a resource shortfall (5.5). A limit with
|
||||
* no consumer scope gets the host as its whole scope. */
|
||||
static void test_check_rows_host_limit_only_is_not_a_shortfall(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[2] = { fx->slots->idx, fx->lim_hcap->idx };
|
||||
float total[2] = { 8, 4 };
|
||||
float inuse[2] = { 2, 3 };
|
||||
float wanted[2] = { 4, 4 };
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_LIMIT, entry_n(rec, 0)->u.host.verdicts);
|
||||
e = entry_n(rec, 1);
|
||||
assert_resource_row(e, SCHED_TRACE_E_HOST_ROW, "limit=hcap",
|
||||
XCE_RESF_LIMIT, 2, 4, 3, 0, 4, false);
|
||||
assert_limit_row(e, "hcap", "h=n01", "slots");
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->counters.host_evals[2]); /* SHORTFALL */
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->counters.host_evals[3]); /* LIMIT */
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
/* a shortfall alone is a shortfall alone */
|
||||
{
|
||||
int i2[1] = { fx->mem->idx };
|
||||
float t2[1] = { 4 };
|
||||
float u2[1] = { 3 };
|
||||
float w2[1] = { 2 };
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 1, i2, t2, u2, NULL, w2);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SHORTFALL,
|
||||
entry_n(rec, 0)->u.host.verdicts);
|
||||
TEST_ASSERT_NULL(entry_n(rec, 1)->u.row.limit);
|
||||
sched_trace_rec_free(rec);
|
||||
}
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* Every row met: nothing kept, and the verdict falls back to the per-host
|
||||
* check's own selection branch (the model's "from rows" rule). */
|
||||
static void test_check_rows_host_all_met_keeps_nothing(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[2] = { fx->mem->idx, fx->lim_host->idx };
|
||||
float total[2] = { 64, 10 };
|
||||
float inuse[2] = { 0, 0 };
|
||||
float wanted[2] = { 8, 2 };
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
TEST_ASSERT_EQUAL_UINT(0, rec->host_row_verdicts);
|
||||
sched_trace_host_verdict(rec, XCE_TRACE_V_ACCEPTED, 0);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_HOST, entry_n(rec, 0)->type);
|
||||
sched_trace_rec_free(rec);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* A worker with no matched host limits: the limit row still resolves its
|
||||
* name and scope from the vres resource name, host appended. */
|
||||
static void test_check_rows_host_limit_without_matched_limits(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_worker_t w2;
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[1] = { fx->lim_host->idx };
|
||||
float total[1] = { 1 };
|
||||
float inuse[1] = { 1 };
|
||||
float wanted[1] = { 1 };
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
memset(&w2, 0, sizeof(w2));
|
||||
w2.name = "n77";
|
||||
w2.idx = 7; /* no hlimits list at this index */
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &w2);
|
||||
sched_trace_check_rows(rec, &fx->core, 1, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_LIMIT, entry_n(rec, 0)->u.host.verdicts);
|
||||
e = entry_n(rec, 1);
|
||||
assert_limit_row(e, "host_slots", "u=alice,h=n77", NULL);
|
||||
sched_trace_rec_free(rec);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* Summary level: no host lines and no rows, but each limit a host hit is
|
||||
* tallied with its scope instance for the position summary (5.9). */
|
||||
static void test_check_rows_host_summary_tallies_limit(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[2] = { fx->mem->idx, fx->lim_host->idx };
|
||||
float total[2] = { 64, 10 };
|
||||
float inuse[2] = { 60, 9 };
|
||||
float wanted[2] = { 8, 2 };
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
ctl.level = XCE_TRACE_LEVEL_SUMMARY;
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_pos_begin(rec, 1, 1);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
sched_trace_pos_end(rec, XCE_TRACE_POS_UNFILLED, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
|
||||
e = entry_n(rec, 1);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_POS_SUMMARY, e->type);
|
||||
TEST_ASSERT_EQUAL_INT(1, e->u.pos_summary.counts[2]); /* SHORTFALL */
|
||||
TEST_ASSERT_EQUAL_INT(1, e->u.pos_summary.counts[3]); /* LIMIT */
|
||||
TEST_ASSERT_EQUAL_INT(1, e->u.pos_summary.nlimits);
|
||||
TEST_ASSERT_EQUAL_STRING("host_slots", e->u.pos_summary.limits[0].limit);
|
||||
TEST_ASSERT_EQUAL_STRING("u=alice,h=n01", e->u.pos_summary.limits[0].scope);
|
||||
TEST_ASSERT_EQUAL_INT(1, e->u.pos_summary.limits[0].count);
|
||||
sched_trace_rec_free(rec);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* Rows outside any check scope are not attributed to anything; a resource
|
||||
* index the resmgr does not know is kept as an unnamed resource row. */
|
||||
static void test_check_rows_scope_edges(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[1] = { fx->mem->idx };
|
||||
int unknown[1] = { 99 };
|
||||
float total[1] = { 1 };
|
||||
float inuse[1] = { 1 };
|
||||
float wanted[1] = { 1 };
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_check_rows(rec, &fx->core, 1, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
TEST_ASSERT_EQUAL_INT(0, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_UINT(0, rec->host_row_verdicts);
|
||||
|
||||
sched_trace_check_begin(rec, &fx->obj);
|
||||
sched_trace_check_end(rec, 1, true);
|
||||
sched_trace_check_rows(rec, &fx->core, 1, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->nentries); /* the check end only */
|
||||
|
||||
sched_trace_check_begin(rec, &fx->obj);
|
||||
sched_trace_check_rows(rec, &fx->core, 1, unknown, total, inuse, NULL,
|
||||
wanted);
|
||||
TEST_ASSERT_EQUAL_INT(2, rec->nentries);
|
||||
e = entry_n(rec, 1);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_CHECK_ROW, e->type);
|
||||
TEST_ASSERT_NULL(e->u.row.res);
|
||||
TEST_ASSERT_EQUAL_INT(99, e->u.row.idx);
|
||||
TEST_ASSERT_EQUAL_INT(0, e->u.row.flags);
|
||||
TEST_ASSERT_NULL(e->u.row.limit);
|
||||
TEST_ASSERT_FALSE(e->u.row.met);
|
||||
sched_trace_check_end(rec, 0, false);
|
||||
sched_trace_rec_free(rec);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
/* Host rows count against the line budget like every other line. */
|
||||
static void test_check_rows_host_rows_count_against_budget(void)
|
||||
{
|
||||
rows_fx_t* fx = rows_fx_new();
|
||||
xce_sched_trace_ctl_t ctl = make_ctl("fcfs");
|
||||
xce_sched_trace_rec_t* rec;
|
||||
int idxs[2] = { fx->mem->idx, fx->lim_host->idx };
|
||||
float total[2] = { 1, 1 };
|
||||
float inuse[2] = { 1, 1 };
|
||||
float wanted[2] = { 1, 1 };
|
||||
int i;
|
||||
|
||||
ctl.line_bound = XCE_TRACE_LINE_BOUND_MIN;
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
for (i = 0; i < XCE_TRACE_LINE_BOUND_MIN - 2; i++) {
|
||||
sched_trace_alt_skipped(rec, 1, 1);
|
||||
}
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_host_verdict(rec, 0, 0);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LINE_BOUND_MIN, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->counters.omitted_lines);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_HOST_ROW, rec->entries_tail->type);
|
||||
TEST_ASSERT_EQUAL_STRING("mem", rec->entries_tail->u.row.res);
|
||||
|
||||
/* a host scope that ends without its verdict leaves no rows behind */
|
||||
sched_trace_rec_free(rec);
|
||||
ctl = make_ctl("fcfs");
|
||||
rec = sched_trace_rec_new(&ctl, 0x77, XCE_TRACE_REC_DECISION);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_host_verdict(rec, XCE_TRACE_V_CLAIMED, 0);
|
||||
TEST_ASSERT_EQUAL_INT(1, rec->nentries);
|
||||
/* and a record freed with rows still pending frees them (checked only
|
||||
* under ASAN with detect_leaks=1; the suite runs with leaks off) */
|
||||
sched_trace_host_begin(rec, &fx->obj, &fx->w);
|
||||
sched_trace_check_rows(rec, &fx->core, 2, idxs, total, inuse, NULL,
|
||||
wanted);
|
||||
sched_trace_rec_free(rec);
|
||||
rows_fx_free(fx);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
xstring_table_init(1024);
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_selection_copies_ordering_keys);
|
||||
RUN_TEST(test_selection_policy_ignores_queue_override);
|
||||
|
|
@ -502,5 +1040,13 @@ int main(void)
|
|||
RUN_TEST(test_attempt_reasons_main_reason_only);
|
||||
RUN_TEST(test_attempt_reasons_none_recorded);
|
||||
RUN_TEST(test_attempt_reasons_counts_against_the_budget);
|
||||
RUN_TEST(test_check_rows_cluster_scope_keeps_every_row);
|
||||
RUN_TEST(test_check_rows_host_scope_keeps_unmet_rows);
|
||||
RUN_TEST(test_check_rows_host_limit_only_is_not_a_shortfall);
|
||||
RUN_TEST(test_check_rows_host_all_met_keeps_nothing);
|
||||
RUN_TEST(test_check_rows_host_limit_without_matched_limits);
|
||||
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);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1092,8 +1092,12 @@ static int emit_every_kind(xce_sched_trace_rec_t* rec)
|
|||
xce_alloc_plan_t plan = make_plan(slots, 1);
|
||||
xce_sched_worker_t w = make_worker(hname, 0);
|
||||
sched_trace_reason_count_t rc = { 1, 1 };
|
||||
sched_trace_row_t row;
|
||||
|
||||
make_primary(&fp, 1, 1);
|
||||
memset(&row, 0, sizeof(row));
|
||||
row.res = "mem";
|
||||
row.total = 1;
|
||||
sched_trace_path_push(rec, XCE_TRACE_PATH_ALT, 1, 2);
|
||||
sched_trace_alt_skipped(rec, 1, 2); /* 1 */
|
||||
sched_trace_path_pop(rec);
|
||||
|
|
@ -1102,29 +1106,31 @@ static int emit_every_kind(xce_sched_trace_rec_t* rec)
|
|||
sched_trace_path_push(rec, XCE_TRACE_PATH_MEMBER, 1, 1);
|
||||
sched_trace_resreq_begin(rec, &fp.p, 0); /* 3 */
|
||||
sched_trace_check_begin(rec, NULL);
|
||||
sched_trace_check_end(rec, 1, true); /* 4 */
|
||||
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_DECLINED); /* 5 */
|
||||
sched_trace_plan_discarded(rec, &plan); /* 6 */
|
||||
sched_trace_plan_not_tried(rec, &plan, -12); /* 7 */
|
||||
sched_trace_plan_begin(rec, &plan); /* 8 */
|
||||
sched_trace_pos_begin(rec, 1, 1); /* 9 */
|
||||
sched_trace_row(rec, &row); /* 4 */
|
||||
sched_trace_check_end(rec, 1, true); /* 5 */
|
||||
sched_trace_no_plan(rec, XCE_TRACE_NOPLAN_DECLINED); /* 6 */
|
||||
sched_trace_plan_discarded(rec, &plan); /* 7 */
|
||||
sched_trace_plan_not_tried(rec, &plan, -12); /* 8 */
|
||||
sched_trace_plan_begin(rec, &plan); /* 9 */
|
||||
sched_trace_pos_begin(rec, 1, 1); /* 10 */
|
||||
sched_trace_host_begin(rec, NULL, &w);
|
||||
sched_trace_host_verdict(rec, XCE_TRACE_V_ACCEPTED, 0); /* 10 */
|
||||
sched_trace_pos_end(rec, XCE_TRACE_POS_FILLED, hname); /* 11 */
|
||||
sched_trace_plan_end(rec, 1, 0); /* 12 */
|
||||
sched_trace_rollback_host(rec, hname); /* 13 */
|
||||
sched_trace_row(rec, &row); /* 11, held */
|
||||
sched_trace_host_verdict(rec, XCE_TRACE_V_ACCEPTED, 0); /* 12 */
|
||||
sched_trace_pos_end(rec, XCE_TRACE_POS_FILLED, hname); /* 13 */
|
||||
sched_trace_plan_end(rec, 1, 0); /* 14 */
|
||||
sched_trace_rollback_host(rec, hname); /* 15 */
|
||||
sched_trace_rollback_host(rec, hname); /* merged */
|
||||
sched_trace_commit(rec); /* 14 */
|
||||
sched_trace_finalize_failed(rec, -5); /* 15 */
|
||||
sched_trace_search_end(rec, 1); /* 16 */
|
||||
sched_trace_resreq_end(rec, 1, 0); /* 17 */
|
||||
sched_trace_commit(rec); /* 16 */
|
||||
sched_trace_finalize_failed(rec, -5); /* 17 */
|
||||
sched_trace_search_end(rec, 1); /* 18 */
|
||||
sched_trace_resreq_end(rec, 1, 0); /* 19 */
|
||||
sched_trace_path_pop(rec);
|
||||
sched_trace_member_failed(rec, 1, 1, -12); /* 18 */
|
||||
sched_trace_attempt_reasons(rec, &rc, 1, NULL, 0, NULL, 0); /* 19 */
|
||||
sched_trace_resreq_end(rec, 0, 0); /* 20 */
|
||||
sched_trace_member_failed(rec, 1, 1, -12); /* 20 */
|
||||
sched_trace_attempt_reasons(rec, &rc, 1, NULL, 0, NULL, 0); /* 21 */
|
||||
sched_trace_resreq_end(rec, 0, 0); /* 22 */
|
||||
sched_trace_path_pop(rec);
|
||||
sched_trace_alt_result(rec, 0, 2); /* 21 */
|
||||
return 21;
|
||||
sched_trace_alt_result(rec, 0, 2); /* 23 */
|
||||
return 23;
|
||||
}
|
||||
|
||||
static void test_every_entry_kind_is_one_entry(void)
|
||||
|
|
@ -1152,14 +1158,16 @@ static void test_every_entry_kind_is_one_entry(void)
|
|||
TEST_ASSERT_EQUAL_INT(0, seen[SCHED_TRACE_E_POS_SUMMARY]);
|
||||
sched_trace_rec_free(rec);
|
||||
|
||||
/* summary level: the position summary replaces the host lines and the
|
||||
* position end, so the same calls make one entry fewer */
|
||||
/* summary level: the position summary replaces the host lines (the
|
||||
* host verdict and its row) and the position end, so the same calls
|
||||
* make two entries fewer */
|
||||
rec = new_rec_level(XCE_TRACE_LINE_BOUND_DEFAULT, XCE_TRACE_LEVEL_SUMMARY);
|
||||
TEST_ASSERT_NOT_NULL(rec);
|
||||
TEST_ASSERT_EQUAL_INT(n, emit_every_kind(rec));
|
||||
TEST_ASSERT_EQUAL_INT(n - 1, rec->nentries);
|
||||
TEST_ASSERT_EQUAL_INT(n - 2, rec->nentries);
|
||||
for (e = rec->entries; e != NULL; e = e->next) {
|
||||
TEST_ASSERT_TRUE(e->type != SCHED_TRACE_E_HOST);
|
||||
TEST_ASSERT_TRUE(e->type != SCHED_TRACE_E_HOST_ROW);
|
||||
TEST_ASSERT_TRUE(e->type != SCHED_TRACE_E_POS_END);
|
||||
seen[e->type]++;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue