refactor(scheduler-sim-decision-trace): split trace sources under the 800-line cap
Task 2.4: outcome capture moves to sched_trace_outcome.c and the position/host/summary/rollback half of the model to sched_trace_scan.c; shared helpers go behind sched_trace_internal.h (sti_ prefix). Code is moved unchanged; every target that compiles the trace sources gets all four, and the core isolation scan covers the new files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eacb1fa599
commit
144a01982e
40 changed files with 1355 additions and 1094 deletions
|
|
@ -17,9 +17,10 @@
|
|||
* Only the simulator REPL ever publishes a control block (`trace_ctl`), so
|
||||
* the live scheduler never builds a record (Requirement 11.2).
|
||||
*
|
||||
* The model (src/sched_trace.c) and the capture helpers
|
||||
* (src/sched_trace_capture.c) do no I/O and no logging; test_core_isolation.c
|
||||
* scans both files for stdio and file I/O.
|
||||
* The model (src/sched_trace.c, src/sched_trace_scan.c) and the capture
|
||||
* helpers (src/sched_trace_capture.c, src/sched_trace_outcome.c) do no I/O
|
||||
* and no logging; test_core_isolation.c scans these files and their shared
|
||||
* src/sched_trace_internal.h for stdio and file I/O.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
|
@ -697,7 +698,8 @@ extern void sched_trace_finalize_failed(xce_sched_trace_rec_t* rec, int err);
|
|||
extern void sched_trace_search_end(xce_sched_trace_rec_t* rec, int rc);
|
||||
|
||||
/*
|
||||
* TraceCapture (src/sched_trace_capture.c, design.md "TraceCapture"). Each
|
||||
* TraceCapture (src/sched_trace_capture.c and, for the outcomes,
|
||||
* src/sched_trace_outcome.c; design.md "TraceCapture"). Each
|
||||
* reads scheduler state on the thread that owns it, read-only, and copies
|
||||
* what it reports into `rec` (never NULL: the SCHED_TRACE gate).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ scheduler_srcs = [
|
|||
'src/host_iter.c',
|
||||
'src/alloc_runner.c',
|
||||
'src/sched_trace.c',
|
||||
'src/sched_trace_scan.c',
|
||||
'src/sched_trace_capture.c',
|
||||
'src/sched_trace_outcome.c',
|
||||
'src/plugins/fcfs.c',
|
||||
'src/plugins/builtin_fcfs.c',
|
||||
'src/plugins/seqjob_allocator.c',
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@
|
|||
* level host verdicts are not stored: they are tallied per position, and
|
||||
* the position's end becomes one position-summary entry (5.9).
|
||||
*
|
||||
* This file holds the record, the entry sequence and the requirement, plan
|
||||
* and check entries; positions, hosts, tallies, rollback and the search
|
||||
* end are in sched_trace_scan.c (sched_trace_internal.h, task 2.4).
|
||||
*
|
||||
* No I/O and no logging: test_core_isolation.c scans this file for stdio
|
||||
* and file I/O.
|
||||
*/
|
||||
|
|
@ -33,6 +37,7 @@
|
|||
|
||||
#include "sched/alloc_iter.h"
|
||||
#include "sched/trace.h"
|
||||
#include "sched_trace_internal.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Smallest chunk the arena allocates; a larger request gets a chunk of its
|
||||
|
|
@ -57,17 +62,8 @@ static unsigned char* chunk_data(sched_trace_chunk_t* c)
|
|||
return ((unsigned char*)c) + chunk_hdr_size();
|
||||
}
|
||||
|
||||
/* Summary level: one limit hit by hosts of the current position. Builder
|
||||
* memory, owned by the record until the position ends. */
|
||||
struct sched_trace_tally_build_s {
|
||||
char* limit;
|
||||
char* scope;
|
||||
int count; /* hosts with a limit-exceeded verdict */
|
||||
unsigned last_host; /* host_seq of the last host that hit it */
|
||||
};
|
||||
|
||||
/* Drops the current position's limit tallies (keeps the array). */
|
||||
static void pos_limits_clear(xce_sched_trace_rec_t* rec)
|
||||
void sti_pos_limits_clear(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
int k;
|
||||
|
||||
|
|
@ -79,7 +75,7 @@ static void pos_limits_clear(xce_sched_trace_rec_t* rec)
|
|||
}
|
||||
|
||||
/* Drops the current host's held rows (keeps the array). */
|
||||
static void host_rows_clear(xce_sched_trace_rec_t* rec)
|
||||
void sti_host_rows_clear(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
int k;
|
||||
|
||||
|
|
@ -92,13 +88,6 @@ static void host_rows_clear(xce_sched_trace_rec_t* rec)
|
|||
rec->host_nrows = 0;
|
||||
}
|
||||
|
||||
/* Starts the current position's tallies over. */
|
||||
static void pos_tally_reset(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
memset(rec->pos_counts, 0, sizeof(rec->pos_counts));
|
||||
pos_limits_clear(rec);
|
||||
}
|
||||
|
||||
bool sched_trace_ctl_wants(const xce_sched_trace_ctl_t* ctl, uint64_t jobid)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -246,9 +235,9 @@ void sched_trace_rec_free(xce_sched_trace_rec_t* rec)
|
|||
return;
|
||||
}
|
||||
|
||||
pos_limits_clear(rec);
|
||||
sti_pos_limits_clear(rec);
|
||||
free(rec->pos_limits);
|
||||
host_rows_clear(rec);
|
||||
sti_host_rows_clear(rec);
|
||||
free(rec->host_rows);
|
||||
|
||||
c = rec->arena;
|
||||
|
|
@ -301,8 +290,8 @@ size_t sched_trace_rec_bytes(const xce_sched_trace_rec_t* rec)
|
|||
* budget (counted as one omitted line), or after an arena failure (the
|
||||
* record is already marked incomplete and appending has stopped).
|
||||
*/
|
||||
static sched_trace_entry_t* entry_add(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
sched_trace_entry_t* sti_entry_add(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
|
|
@ -338,30 +327,30 @@ static sched_trace_entry_t* entry_add(xce_sched_trace_rec_t* rec,
|
|||
}
|
||||
|
||||
/* A call that stores no entry still ends a run of released hosts. */
|
||||
static void rollback_run_end(xce_sched_trace_rec_t* rec)
|
||||
void sti_rollback_run_end(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
rec->rollback_open = false;
|
||||
rec->rollback_e = NULL;
|
||||
}
|
||||
|
||||
/* A scope begin is stored at the current level and opens the next one. */
|
||||
static sched_trace_entry_t* scope_begin(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
sched_trace_entry_t* sti_scope_begin(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, type);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, type);
|
||||
|
||||
rec->depth++;
|
||||
return e;
|
||||
}
|
||||
|
||||
/* A scope end closes the level and is stored at its begin's level. */
|
||||
static sched_trace_entry_t* scope_end(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
sched_trace_entry_t* sti_scope_end(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type)
|
||||
{
|
||||
if (rec->depth > 0) {
|
||||
rec->depth--;
|
||||
}
|
||||
return entry_add(rec, type);
|
||||
return sti_entry_add(rec, type);
|
||||
}
|
||||
|
||||
/* Arena copy of `n` ints; NULL for none. */
|
||||
|
|
@ -398,7 +387,7 @@ void sched_trace_path_push(xce_sched_trace_rec_t* rec,
|
|||
{
|
||||
sched_trace_path_t* p = &rec->path;
|
||||
|
||||
rollback_run_end(rec);
|
||||
sti_rollback_run_end(rec);
|
||||
if (p->depth >= 2) {
|
||||
/* deeper than container > member: not kept, but balanced */
|
||||
rec->path_overflow++;
|
||||
|
|
@ -412,7 +401,7 @@ void sched_trace_path_push(xce_sched_trace_rec_t* rec,
|
|||
|
||||
void sched_trace_path_pop(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
rollback_run_end(rec);
|
||||
sti_rollback_run_end(rec);
|
||||
if (rec->path_overflow > 0) {
|
||||
rec->path_overflow--;
|
||||
return;
|
||||
|
|
@ -438,7 +427,7 @@ void sched_trace_resreq_begin(xce_sched_trace_rec_t* rec,
|
|||
rec->scope_not_tried = 0;
|
||||
rec->scope_plan_err = false;
|
||||
|
||||
e = scope_begin(rec, SCHED_TRACE_E_RESREQ_BEGIN);
|
||||
e = sti_scope_begin(rec, SCHED_TRACE_E_RESREQ_BEGIN);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -486,7 +475,7 @@ void sched_trace_resreq_end(xce_sched_trace_rec_t* rec, int rc,
|
|||
rec->resreq_open--;
|
||||
}
|
||||
|
||||
e = scope_end(rec, SCHED_TRACE_E_RESREQ_END);
|
||||
e = sti_scope_end(rec, SCHED_TRACE_E_RESREQ_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -497,7 +486,7 @@ void sched_trace_resreq_end(xce_sched_trace_rec_t* rec, int rc,
|
|||
void sched_trace_member_failed(xce_sched_trace_rec_t* rec, int j, int m,
|
||||
int err)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_MEMBER_FAILED);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_MEMBER_FAILED);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
|
|
@ -509,7 +498,7 @@ void sched_trace_member_failed(xce_sched_trace_rec_t* rec, int j, int m,
|
|||
|
||||
void sched_trace_alt_result(xce_sched_trace_rec_t* rec, int chosen_i, int n)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_ALT_RESULT);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_ALT_RESULT);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
|
|
@ -520,7 +509,7 @@ void sched_trace_alt_result(xce_sched_trace_rec_t* rec, int chosen_i, int n)
|
|||
|
||||
void sched_trace_alt_skipped(xce_sched_trace_rec_t* rec, int i, int n)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_ALT_SKIPPED);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_ALT_SKIPPED);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
|
|
@ -534,7 +523,7 @@ void sched_trace_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
int ncodes, const char* const* hosts,
|
||||
int nhosts, const char* const* res, int nres)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_ATTEMPT_REASONS);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_ATTEMPT_REASONS);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
|
|
@ -560,7 +549,7 @@ void sched_trace_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
void sched_trace_check_begin(xce_sched_trace_rec_t* rec,
|
||||
const xce_sched_obj_t* obj)
|
||||
{
|
||||
rollback_run_end(rec);
|
||||
sti_rollback_run_end(rec);
|
||||
rec->scope_obj = obj;
|
||||
rec->scope_w = NULL;
|
||||
rec->check_open = true;
|
||||
|
|
@ -574,7 +563,7 @@ void sched_trace_check_end(xce_sched_trace_rec_t* rec, int rc,
|
|||
rec->scope_obj = NULL;
|
||||
rec->check_open = false;
|
||||
|
||||
e = entry_add(rec, SCHED_TRACE_E_CHECK_END);
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_CHECK_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -585,7 +574,7 @@ void sched_trace_check_end(xce_sched_trace_rec_t* rec, int rc,
|
|||
void sched_trace_no_plan(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_trace_noplan_t why)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_NO_PLAN);
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_NO_PLAN);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
|
|
@ -614,7 +603,7 @@ void sched_trace_plan_begin(xce_sched_trace_rec_t* rec,
|
|||
rec->cur_plan_no = ++rec->plan_no;
|
||||
rec->scope_tried++;
|
||||
rec->counters.plans_tried++;
|
||||
e = scope_begin(rec, SCHED_TRACE_E_PLAN_BEGIN);
|
||||
e = sti_scope_begin(rec, SCHED_TRACE_E_PLAN_BEGIN);
|
||||
if (e != NULL) {
|
||||
plan_fill(rec, e, plan, rec->cur_plan_no, 0);
|
||||
}
|
||||
|
|
@ -628,7 +617,7 @@ void sched_trace_plan_discarded(xce_sched_trace_rec_t* rec,
|
|||
rec->plan_no++;
|
||||
rec->scope_discarded++;
|
||||
rec->counters.plans_discarded++;
|
||||
e = entry_add(rec, SCHED_TRACE_E_PLAN_DISCARDED);
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_PLAN_DISCARDED);
|
||||
if (e != NULL) {
|
||||
plan_fill(rec, e, plan, rec->plan_no, 0);
|
||||
}
|
||||
|
|
@ -641,7 +630,7 @@ void sched_trace_plan_not_tried(xce_sched_trace_rec_t* rec,
|
|||
|
||||
rec->plan_no++;
|
||||
rec->scope_not_tried++;
|
||||
e = entry_add(rec, SCHED_TRACE_E_PLAN_NOT_TRIED);
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_PLAN_NOT_TRIED);
|
||||
if (e != NULL) {
|
||||
plan_fill(rec, e, plan, rec->plan_no, err);
|
||||
}
|
||||
|
|
@ -655,7 +644,7 @@ void sched_trace_plan_end(xce_sched_trace_rec_t* rec, int rc, int failed_pos)
|
|||
rec->scope_plan_err = true;
|
||||
}
|
||||
|
||||
e = scope_end(rec, SCHED_TRACE_E_PLAN_END);
|
||||
e = sti_scope_end(rec, SCHED_TRACE_E_PLAN_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -663,468 +652,3 @@ void sched_trace_plan_end(xce_sched_trace_rec_t* rec, int rc, int failed_pos)
|
|||
e->u.plan_end.rc = rc;
|
||||
e->u.plan_end.failed_pos = failed_pos;
|
||||
}
|
||||
|
||||
void sched_trace_pos_begin(xce_sched_trace_rec_t* rec, int pos, int slots)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
rec->cur_pos = pos;
|
||||
rec->scan_err = 0;
|
||||
rec->scan_err_set = false;
|
||||
pos_tally_reset(rec);
|
||||
|
||||
e = scope_begin(rec, SCHED_TRACE_E_POS_BEGIN);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_begin.pos = pos;
|
||||
e->u.pos_begin.slots = slots;
|
||||
}
|
||||
|
||||
void sched_trace_pos_scan_error(xce_sched_trace_rec_t* rec, int err)
|
||||
{
|
||||
rollback_run_end(rec);
|
||||
rec->scan_err = err;
|
||||
rec->scan_err_set = true;
|
||||
}
|
||||
|
||||
/* Fills a POS_SUMMARY entry from the current position's tallies; only
|
||||
* limits some host was rejected for are kept. */
|
||||
static void pos_summary_fill(xce_sched_trace_rec_t* rec, sched_trace_entry_t* e,
|
||||
xce_sched_trace_pos_end_t how, const char* host,
|
||||
int err)
|
||||
{
|
||||
int n = 0;
|
||||
int k;
|
||||
|
||||
e->u.pos_summary.pos = rec->cur_pos;
|
||||
e->u.pos_summary.how = how;
|
||||
e->u.pos_summary.err = err;
|
||||
e->u.pos_summary.host = sched_trace_arena_strdup(rec, host);
|
||||
memcpy(e->u.pos_summary.counts, rec->pos_counts,
|
||||
sizeof(e->u.pos_summary.counts));
|
||||
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
if (rec->pos_limits[k].count > 0) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_summary.limits = (sched_trace_limit_tally_t*)
|
||||
sched_trace_arena_alloc(rec, sizeof(sched_trace_limit_tally_t)
|
||||
* (size_t)n);
|
||||
if (e->u.pos_summary.limits == NULL) {
|
||||
return;
|
||||
}
|
||||
n = 0;
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
const struct sched_trace_tally_build_s* b = &rec->pos_limits[k];
|
||||
sched_trace_limit_tally_t* t;
|
||||
|
||||
if (b->count <= 0) {
|
||||
continue;
|
||||
}
|
||||
t = &e->u.pos_summary.limits[n++];
|
||||
t->limit = sched_trace_arena_strdup(rec, b->limit);
|
||||
t->scope = sched_trace_arena_strdup(rec, b->scope);
|
||||
t->count = b->count;
|
||||
}
|
||||
e->u.pos_summary.nlimits = n;
|
||||
}
|
||||
|
||||
void sched_trace_pos_end(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_trace_pos_end_t how, const char* host)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
int err = 0;
|
||||
|
||||
if (rec->scan_err_set) {
|
||||
/* the host scan stopped: the runner's UNFILLED is a scan error */
|
||||
err = rec->scan_err;
|
||||
if (how == XCE_TRACE_POS_UNFILLED) {
|
||||
how = XCE_TRACE_POS_SCAN_ERROR;
|
||||
}
|
||||
}
|
||||
rec->scan_err = 0;
|
||||
rec->scan_err_set = false;
|
||||
|
||||
if (rec->level == XCE_TRACE_LEVEL_SUMMARY) {
|
||||
/* the position's end carries its host tallies (5.9) */
|
||||
e = scope_end(rec, SCHED_TRACE_E_POS_SUMMARY);
|
||||
if (e != NULL) {
|
||||
pos_summary_fill(rec, e, how, host, err);
|
||||
}
|
||||
pos_tally_reset(rec);
|
||||
return;
|
||||
}
|
||||
|
||||
e = scope_end(rec, SCHED_TRACE_E_POS_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_end.pos = rec->cur_pos;
|
||||
e->u.pos_end.how = how;
|
||||
e->u.pos_end.err = err;
|
||||
e->u.pos_end.host = sched_trace_arena_strdup(rec, host);
|
||||
}
|
||||
|
||||
void sched_trace_host_begin(xce_sched_trace_rec_t* rec,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_sched_worker_t* w)
|
||||
{
|
||||
rollback_run_end(rec);
|
||||
rec->host_seq++;
|
||||
rec->scope_obj = obj;
|
||||
rec->scope_w = w;
|
||||
rec->host_row_verdicts = 0;
|
||||
host_rows_clear(rec); /* a scope that ended without its verdict */
|
||||
}
|
||||
|
||||
/* Counts one host evaluation: decision-wide per verdict kind, and at
|
||||
* summary level per position, with each limit the host hit counted once
|
||||
* when its verdict is limit exceeded (5.9, 9.3). */
|
||||
static void host_count(xce_sched_trace_rec_t* rec, unsigned verdicts)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < XCE_TRACE_V_NKINDS; k++) {
|
||||
if ((verdicts & (1u << k)) != 0) {
|
||||
rec->counters.host_evals[k]++;
|
||||
rec->pos_counts[k]++;
|
||||
}
|
||||
}
|
||||
if ((verdicts & XCE_TRACE_V_LIMIT) == 0) {
|
||||
return;
|
||||
}
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
if (rec->pos_limits[k].last_host == rec->host_seq) {
|
||||
rec->pos_limits[k].count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies a row's strings into the arena (NULL stays NULL). */
|
||||
static void row_to_arena(xce_sched_trace_rec_t* rec, sched_trace_row_t* dst,
|
||||
const sched_trace_row_t* src)
|
||||
{
|
||||
*dst = *src;
|
||||
dst->res = sched_trace_arena_strdup(rec, src->res);
|
||||
dst->limit = sched_trace_arena_strdup(rec, src->limit);
|
||||
dst->scope = sched_trace_arena_strdup(rec, src->scope);
|
||||
dst->limited_res = sched_trace_arena_strdup(rec, src->limited_res);
|
||||
}
|
||||
|
||||
/* Stores the current host's held rows after its verdict line, one level
|
||||
* below it, then drops them. Each counts against the budget. */
|
||||
static void host_rows_flush(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < rec->host_nrows; k++) {
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_HOST_ROW);
|
||||
|
||||
if (e == NULL) {
|
||||
continue;
|
||||
}
|
||||
e->depth++;
|
||||
row_to_arena(rec, &e->u.row, &rec->host_rows[k]);
|
||||
}
|
||||
host_rows_clear(rec);
|
||||
}
|
||||
|
||||
/* NULL-preserving heap copy for held rows; sets *failed on failure. */
|
||||
static const char* row_strdup(const char* s, bool* failed)
|
||||
{
|
||||
char* d;
|
||||
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
d = strdup(s);
|
||||
if (d == NULL) {
|
||||
*failed = true;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void sched_trace_row(xce_sched_trace_rec_t* rec, const sched_trace_row_t* row)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_row_t* h;
|
||||
bool failed = false;
|
||||
|
||||
rollback_run_end(rec);
|
||||
if (row == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rec->scope_w == NULL) {
|
||||
/* the cluster-wide check: stored now, before its check-end line */
|
||||
e = entry_add(rec, SCHED_TRACE_E_CHECK_ROW);
|
||||
if (e != NULL) {
|
||||
row_to_arena(rec, &e->u.row, row);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* a host scope: no host lines at summary level; at full level the row
|
||||
* waits for the host's verdict line, which it belongs under */
|
||||
if ((rec->level == XCE_TRACE_LEVEL_SUMMARY) || rec->incomplete) {
|
||||
return;
|
||||
}
|
||||
if (rec->host_nrows == rec->host_rows_cap) {
|
||||
int cap = (rec->host_rows_cap > 0) ? rec->host_rows_cap * 2 : 4;
|
||||
void* p = realloc(rec->host_rows, sizeof(*h) * (size_t)cap);
|
||||
|
||||
if (p == NULL) {
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
return;
|
||||
}
|
||||
rec->host_rows = (sched_trace_row_t*)p;
|
||||
rec->host_rows_cap = cap;
|
||||
}
|
||||
h = &rec->host_rows[rec->host_nrows];
|
||||
*h = *row;
|
||||
h->res = row_strdup(row->res, &failed);
|
||||
h->limit = row_strdup(row->limit, &failed);
|
||||
h->scope = row_strdup(row->scope, &failed);
|
||||
h->limited_res = row_strdup(row->limited_res, &failed);
|
||||
if (failed) {
|
||||
free((void*)h->res);
|
||||
free((void*)h->limit);
|
||||
free((void*)h->scope);
|
||||
free((void*)h->limited_res);
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->host_nrows++;
|
||||
}
|
||||
|
||||
void sched_trace_host_verdict(xce_sched_trace_rec_t* rec, unsigned verdicts,
|
||||
int err)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_phase_t phase = SCHED_TRACE_PHASE_NONE;
|
||||
const char* name
|
||||
= (rec->scope_w != NULL) ? rec->scope_w->name : NULL;
|
||||
|
||||
if (verdicts == 0) {
|
||||
/* from the rows the per-host check recorded; none means the
|
||||
* check's own selection branch rejected the host */
|
||||
verdicts = rec->host_row_verdicts & (XCE_TRACE_V_SHORTFALL
|
||||
| XCE_TRACE_V_LIMIT);
|
||||
if (verdicts == 0) {
|
||||
verdicts = XCE_TRACE_V_SELECT;
|
||||
}
|
||||
phase = SCHED_TRACE_PHASE_PERHOST;
|
||||
} else if ((verdicts & XCE_TRACE_V_SELECT) != 0) {
|
||||
phase = SCHED_TRACE_PHASE_PREFILTER;
|
||||
} else if ((verdicts & (XCE_TRACE_V_SHORTFALL | XCE_TRACE_V_LIMIT)) != 0) {
|
||||
phase = SCHED_TRACE_PHASE_PERHOST;
|
||||
}
|
||||
|
||||
/* one host evaluation, counted once under each kind it matched (9.3) */
|
||||
host_count(rec, verdicts);
|
||||
|
||||
/* the host scope ends here */
|
||||
rec->scope_obj = NULL;
|
||||
rec->scope_w = NULL;
|
||||
rec->host_row_verdicts = 0;
|
||||
rec->host_seq++; /* later hits belong to no host until the next one */
|
||||
rollback_run_end(rec);
|
||||
|
||||
if (rec->level == XCE_TRACE_LEVEL_SUMMARY) {
|
||||
host_rows_clear(rec);
|
||||
return; /* tallied for the position summary, no host line */
|
||||
}
|
||||
|
||||
e = entry_add(rec, SCHED_TRACE_E_HOST);
|
||||
if (e != NULL) {
|
||||
e->u.host.host = sched_trace_arena_strdup(rec, name);
|
||||
e->u.host.verdicts = verdicts;
|
||||
e->u.host.phase = phase;
|
||||
e->u.host.err = err;
|
||||
}
|
||||
/* the host's unmet rows, under its verdict line (5.4, 5.5) */
|
||||
host_rows_flush(rec);
|
||||
}
|
||||
|
||||
void sched_trace_commit(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_COMMIT);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.commit.plan_no = rec->cur_plan_no;
|
||||
}
|
||||
|
||||
void sched_trace_finalize_failed(xce_sched_trace_rec_t* rec, int err)
|
||||
{
|
||||
sched_trace_entry_t* e = entry_add(rec, SCHED_TRACE_E_FINALIZE_FAILED);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.commit.plan_no = rec->cur_plan_no;
|
||||
e->u.commit.err = err;
|
||||
}
|
||||
|
||||
|
||||
static bool tally_str_eq(const char* a, const char* b)
|
||||
{
|
||||
if ((a == NULL) || (b == NULL)) {
|
||||
return a == b;
|
||||
}
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
/* NULL-preserving heap copy for the builder's tallies; sets *failed when a
|
||||
* non-NULL string could not be copied. */
|
||||
static char* tally_strdup(const char* s, bool* failed)
|
||||
{
|
||||
char* d;
|
||||
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
d = strdup(s);
|
||||
if (d == NULL) {
|
||||
*failed = true;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void sched_trace_host_limit_hit(xce_sched_trace_rec_t* rec, const char* limit,
|
||||
const char* scope)
|
||||
{
|
||||
struct sched_trace_tally_build_s* b;
|
||||
bool failed = false;
|
||||
int k;
|
||||
|
||||
rollback_run_end(rec);
|
||||
if (rec->scope_w == NULL) {
|
||||
return; /* not a host scope: the cluster-wide check's rows */
|
||||
}
|
||||
rec->host_row_verdicts |= XCE_TRACE_V_LIMIT;
|
||||
if ((rec->level != XCE_TRACE_LEVEL_SUMMARY) || rec->incomplete) {
|
||||
return; /* full level shows the rows themselves (2.2) */
|
||||
}
|
||||
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
b = &rec->pos_limits[k];
|
||||
if (tally_str_eq(b->limit, limit) && tally_str_eq(b->scope, scope)) {
|
||||
b->last_host = rec->host_seq; /* once per host, at its verdict */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rec->pos_nlimits == rec->pos_limits_cap) {
|
||||
int cap = (rec->pos_limits_cap > 0) ? rec->pos_limits_cap * 2 : 4;
|
||||
void* p = realloc(rec->pos_limits, sizeof(*b) * (size_t)cap);
|
||||
|
||||
if (p == NULL) {
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->pos_limits = (struct sched_trace_tally_build_s*)p;
|
||||
rec->pos_limits_cap = cap;
|
||||
}
|
||||
b = &rec->pos_limits[rec->pos_nlimits];
|
||||
b->limit = tally_strdup(limit, &failed);
|
||||
b->scope = tally_strdup(scope, &failed);
|
||||
b->count = 0;
|
||||
b->last_host = rec->host_seq;
|
||||
if (failed) {
|
||||
free(b->limit);
|
||||
free(b->scope);
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->pos_nlimits++;
|
||||
}
|
||||
|
||||
/* Appends one host to a ROLLBACK entry, growing its array in the arena. */
|
||||
static void rollback_append(xce_sched_trace_rec_t* rec, sched_trace_entry_t* e,
|
||||
const char* host)
|
||||
{
|
||||
if (e->u.rollback.nhosts == e->u.rollback.cap) {
|
||||
int cap = (e->u.rollback.cap > 0) ? e->u.rollback.cap * 2 : 4;
|
||||
const char** h = (const char**)sched_trace_arena_alloc(
|
||||
rec, sizeof(char*) * (size_t)cap);
|
||||
|
||||
if (h == NULL) {
|
||||
return;
|
||||
}
|
||||
if (e->u.rollback.nhosts > 0) {
|
||||
memcpy(h, e->u.rollback.hosts,
|
||||
sizeof(char*) * (size_t)e->u.rollback.nhosts);
|
||||
}
|
||||
e->u.rollback.hosts = h;
|
||||
e->u.rollback.cap = cap;
|
||||
}
|
||||
e->u.rollback.hosts[e->u.rollback.nhosts++]
|
||||
= sched_trace_arena_strdup(rec, host);
|
||||
}
|
||||
|
||||
void sched_trace_rollback_host(xce_sched_trace_rec_t* rec, const char* host)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
if (rec->rollback_open) {
|
||||
/* the same run of released hosts: the same line */
|
||||
if ((rec->rollback_e != NULL) && !rec->incomplete) {
|
||||
rollback_append(rec, rec->rollback_e, host);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
e = entry_add(rec, SCHED_TRACE_E_ROLLBACK);
|
||||
rec->rollback_open = true; /* also when omitted: one line either way */
|
||||
rec->rollback_e = e;
|
||||
if (e != NULL) {
|
||||
rollback_append(rec, e, host);
|
||||
}
|
||||
}
|
||||
|
||||
void sched_trace_search_end(xce_sched_trace_rec_t* rec, int rc)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_search_stmt_t stmt = SCHED_TRACE_SEARCH_NO_STMT;
|
||||
int t = rec->scope_tried;
|
||||
int d = rec->scope_discarded;
|
||||
int x = rec->scope_not_tried;
|
||||
int g = t + d + x;
|
||||
|
||||
if (rc == 0) {
|
||||
if (g == 0) {
|
||||
stmt = SCHED_TRACE_SEARCH_NONE_GENERATED;
|
||||
rec->counters.reqs_none_generated++;
|
||||
} else if (d == g) {
|
||||
stmt = SCHED_TRACE_SEARCH_ALL_DISCARDED;
|
||||
rec->counters.reqs_all_discarded++;
|
||||
} else if ((t > 0) && !rec->scope_plan_err) {
|
||||
stmt = SCHED_TRACE_SEARCH_NONE_PLACED;
|
||||
rec->counters.reqs_none_placed++;
|
||||
}
|
||||
}
|
||||
|
||||
/* the search is over: a later search in this requirement counts anew */
|
||||
rec->scope_tried = 0;
|
||||
rec->scope_discarded = 0;
|
||||
rec->scope_not_tried = 0;
|
||||
rec->scope_plan_err = false;
|
||||
|
||||
e = entry_add(rec, SCHED_TRACE_E_SEARCH_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.search_end.generated = g;
|
||||
e->u.search_end.tried = t;
|
||||
e->u.search_end.discarded = d;
|
||||
e->u.search_end.not_tried = x;
|
||||
e->u.search_end.rc = rc;
|
||||
e->u.search_end.stmt = stmt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,18 +22,20 @@
|
|||
* librtti's vget()/key_find(), which every scheduler target links), so
|
||||
* every white-box test that #includes this file links without extra
|
||||
* sources.
|
||||
*
|
||||
* The outcome capture (task 2.3) is in sched_trace_outcome.c; the helpers
|
||||
* it shares with this file are declared in sched_trace_internal.h (task
|
||||
* 2.4).
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "sched/trace.h"
|
||||
#include "sched_trace_internal.h"
|
||||
|
||||
/* ---- limit name and scope instance ------------------------------------ */
|
||||
|
||||
#define VRES_PREFIX "limit="
|
||||
#define VRES_PREFIX_LEN (sizeof(VRES_PREFIX) - 1)
|
||||
|
||||
/*
|
||||
* Splits a limit vres name, "limit=<name>[/u=..][/q=..][/p=..]" (lib
|
||||
* xcelimit.c build_vres_name()), into the limit name and the scope part.
|
||||
|
|
@ -74,8 +76,8 @@ static bool split_vres_name(const char* vname, const char* known,
|
|||
/* Arena copy of a vres scope part, "u=a/q=b" -> "u=a,q=b", with
|
||||
* ",h=<host>" ("h=<host>" when the scope is empty) appended for a non-NULL
|
||||
* host: host-limit vres names do not encode the host. */
|
||||
static char* copy_scope(xce_sched_trace_rec_t* rec, const char* scope,
|
||||
const char* host)
|
||||
char* sti_copy_scope(xce_sched_trace_rec_t* rec, const char* scope,
|
||||
const char* host)
|
||||
{
|
||||
size_t n = strlen(scope);
|
||||
size_t hn = (host != NULL) ? strlen(host) : 0;
|
||||
|
|
@ -103,7 +105,7 @@ static char* copy_scope(xce_sched_trace_rec_t* rec, const char* scope,
|
|||
}
|
||||
|
||||
/* Arena copy of the first `n` bytes of `s`, NUL-terminated. */
|
||||
static char* copy_prefix(xce_sched_trace_rec_t* rec, const char* s, size_t n)
|
||||
char* sti_copy_prefix(xce_sched_trace_rec_t* rec, const char* s, size_t n)
|
||||
{
|
||||
char* out = (char*)sched_trace_arena_alloc(rec, n + 1);
|
||||
|
||||
|
|
@ -148,9 +150,9 @@ static bool limit_ref(xce_sched_trace_rec_t* rec, const xce_vres_t* vres,
|
|||
if (known != NULL) {
|
||||
out->limit = sched_trace_arena_strdup(rec, known);
|
||||
} else {
|
||||
out->limit = copy_prefix(rec, name, name_len);
|
||||
out->limit = sti_copy_prefix(rec, name, name_len);
|
||||
}
|
||||
out->scope = copy_scope(rec, scope, NULL);
|
||||
out->scope = sti_copy_scope(rec, scope, NULL);
|
||||
return (out->limit != NULL) && (out->scope != NULL);
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +236,7 @@ static float row_uncomit_at(const xvec_t* uncomitted, int idx)
|
|||
/* Worker `idx`'s matched host limits, obj->hlimits[idx] (sparse: NULL
|
||||
* when the worker has none). Read directly: zarray_get() takes a
|
||||
* non-const array. */
|
||||
static const xlist_t* worker_limits(const xce_sched_obj_t* obj, int idx)
|
||||
const xlist_t* sti_worker_limits(const xce_sched_obj_t* obj, int idx)
|
||||
{
|
||||
const zarray_t* hl = &obj->hlimits;
|
||||
|
||||
|
|
@ -254,13 +256,13 @@ static const xlist_t* scope_limits(const xce_sched_trace_rec_t* rec)
|
|||
if (rec->scope_w == NULL) {
|
||||
return &rec->scope_obj->glimits;
|
||||
}
|
||||
return worker_limits(rec->scope_obj, rec->scope_w->idx);
|
||||
return sti_worker_limits(rec->scope_obj, rec->scope_w->idx);
|
||||
}
|
||||
|
||||
/* The matched limit in `list` whose vres resource is `r` or is named
|
||||
* `name`, or NULL. Read directly: xlist_get() takes a non-const list. */
|
||||
static const xce_vres_t* list_vres(const xlist_t* list,
|
||||
const xce_resource_t* r, const char* name)
|
||||
const xce_vres_t* sti_list_vres(const xlist_t* list, const xce_resource_t* r,
|
||||
const char* name)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -282,21 +284,12 @@ static const xce_vres_t* list_vres(const xlist_t* list,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* A limit resource named `resname`, resolved: the limit name (not
|
||||
* NUL-terminated), the vres-form scope part and the limited resource. */
|
||||
typedef struct {
|
||||
const char* name;
|
||||
size_t name_len;
|
||||
const char* scope;
|
||||
const char* limited_res; /* NULL: no matched limit names it */
|
||||
} charge_ref_t;
|
||||
|
||||
/* Resolves `resname` through its matched limit `v` (NULL: none, so the
|
||||
* limit name and scope are parsed from the name, and the limited resource
|
||||
* is unknown). A name without the "limit=" prefix names the limit
|
||||
* itself. */
|
||||
static void charge_resolve(const xce_vres_t* v, const char* resname,
|
||||
charge_ref_t* c)
|
||||
void sti_charge_resolve(const xce_vres_t* v, const char* resname,
|
||||
charge_ref_t* c)
|
||||
{
|
||||
const char* known = ((v != NULL) && (v->limit != NULL)) ? v->limit->name
|
||||
: NULL;
|
||||
|
|
@ -328,7 +321,8 @@ static bool limit_row(const xce_sched_trace_rec_t* rec, const xce_resource_t* r,
|
|||
char* p;
|
||||
size_t k;
|
||||
|
||||
charge_resolve(list_vres(scope_limits(rec), r, r->name), r->name, &c);
|
||||
sti_charge_resolve(sti_list_vres(scope_limits(rec), r, r->name), r->name,
|
||||
&c);
|
||||
if (rec->scope_w != NULL) {
|
||||
/* host-limit vres names do not encode the host */
|
||||
host = (rec->scope_w->name != NULL) ? rec->scope_w->name : "";
|
||||
|
|
@ -430,8 +424,7 @@ void sched_trace_check_rows(xce_sched_trace_rec_t* rec, xce_sched_core_t* core,
|
|||
|
||||
/* ---- attempt failure reasons (3.5) ------------------------------------ */
|
||||
|
||||
static void count_code(sched_trace_reason_count_t* codes, int* ncodes,
|
||||
int code)
|
||||
void sti_count_code(sched_trace_reason_count_t* codes, int* ncodes, int code)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -510,7 +503,7 @@ void sched_trace_capture_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
}
|
||||
|
||||
if (r->main_reason != 0) {
|
||||
count_code(codes, &ncodes, r->main_reason);
|
||||
sti_count_code(codes, &ncodes, r->main_reason);
|
||||
}
|
||||
for (i = 0; i < nh; i++) {
|
||||
const xce_host_reason_t* h = r->host_based_reasons[i];
|
||||
|
|
@ -518,7 +511,7 @@ void sched_trace_capture_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
if (h == NULL) {
|
||||
continue;
|
||||
}
|
||||
count_code(codes, &ncodes, h->reason);
|
||||
sti_count_code(codes, &ncodes, h->reason);
|
||||
add_distinct(hosts, &nhosts, h->host);
|
||||
}
|
||||
for (i = 0; i < nr; i++) {
|
||||
|
|
@ -527,7 +520,7 @@ void sched_trace_capture_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
if (x == NULL) {
|
||||
continue;
|
||||
}
|
||||
count_code(codes, &ncodes, x->reason);
|
||||
sti_count_code(codes, &ncodes, x->reason);
|
||||
add_distinct(hosts, &nhosts, x->host);
|
||||
add_distinct(res, &nres, x->resource);
|
||||
}
|
||||
|
|
@ -538,528 +531,3 @@ void sched_trace_capture_attempt_reasons(xce_sched_trace_rec_t* rec,
|
|||
free((void*)hosts);
|
||||
free((void*)res);
|
||||
}
|
||||
|
||||
/* ---- dispatch outcome (6.1-6.3, 9.4) ---------------------------------- */
|
||||
|
||||
/* True iff the allocated resource `resname` is a limit charge: its
|
||||
* registered resource has XCE_RESF_LIMIT, or, when the name is not
|
||||
* registered in this replay, it has the "limit=" prefix. */
|
||||
static bool is_limit_charge(xce_sched_core_t* core, const char* resname)
|
||||
{
|
||||
xce_resource_t* r = NULL;
|
||||
|
||||
if ((core != NULL) && (core->resmgr != NULL)
|
||||
&& (key_find(core->resmgr, (void*)resname, (void**)&r) >= 0)
|
||||
&& (r != NULL)) {
|
||||
return (r->flags & XCE_RESF_LIMIT) != 0;
|
||||
}
|
||||
return strncmp(resname, VRES_PREFIX, VRES_PREFIX_LEN) == 0;
|
||||
}
|
||||
|
||||
/* The host limits matched to the job on the worker named `host`, or NULL
|
||||
* (no schedobj, unknown worker, none matched). */
|
||||
static const xlist_t* host_limits(xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj, const char* host)
|
||||
{
|
||||
xce_sched_worker_t* w = NULL;
|
||||
|
||||
if ((obj == NULL) || (core == NULL) || (core->workers == NULL)
|
||||
|| (host == NULL)
|
||||
|| (key_find(core->workers, (void*)host, (void**)&w) < 0)
|
||||
|| (w == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return worker_limits(obj, w->idx);
|
||||
}
|
||||
|
||||
/* Stores allocated amount `a` as a limit charge, resolved through `limits`;
|
||||
* a host charge (`host` non-NULL) gets the host appended to its scope. */
|
||||
static bool charge_row(xce_sched_trace_rec_t* rec, const xlist_t* limits,
|
||||
const xce_alloc_t* a, const char* host,
|
||||
sched_trace_alloc_row_t* out)
|
||||
{
|
||||
charge_ref_t c;
|
||||
|
||||
charge_resolve(list_vres(limits, NULL, a->resname), a->resname, &c);
|
||||
out->res = sched_trace_arena_strdup(rec, a->resname);
|
||||
out->value = a->value;
|
||||
out->limit = copy_prefix(rec, c.name, c.name_len);
|
||||
out->scope = copy_scope(rec, c.scope, host);
|
||||
out->limited_res = sched_trace_arena_strdup(rec, c.limited_res);
|
||||
return (out->res != NULL) && (out->limit != NULL) && (out->scope != NULL)
|
||||
&& ((c.limited_res == NULL) || (out->limited_res != NULL));
|
||||
}
|
||||
|
||||
/* One allocation list (a host's, or the shared one) as resource rows and
|
||||
* limit charges, each in allocation order. False on an arena failure. */
|
||||
static bool alloc_rows(xce_sched_trace_rec_t* rec, xce_sched_core_t* core,
|
||||
const xlist_t* limits, int n, xce_alloc_t** allocs,
|
||||
const char* host, int* nres,
|
||||
sched_trace_alloc_row_t** res, int* ncharges,
|
||||
sched_trace_alloc_row_t** charges)
|
||||
{
|
||||
size_t sz = sizeof(**res) * (size_t)n;
|
||||
int i;
|
||||
|
||||
if ((n <= 0) || (allocs == NULL)) {
|
||||
return true;
|
||||
}
|
||||
*res = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(rec, sz);
|
||||
*charges = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(rec, sz);
|
||||
if ((*res == NULL) || (*charges == NULL)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
const xce_alloc_t* a = allocs[i];
|
||||
sched_trace_alloc_row_t* row;
|
||||
|
||||
if ((a == NULL) || (a->resname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
if (is_limit_charge(core, a->resname)) {
|
||||
if (!charge_row(rec, limits, a, host, &(*charges)[(*ncharges)++])) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
row = &(*res)[(*nres)++];
|
||||
row->res = sched_trace_arena_strdup(rec, a->resname);
|
||||
row->value = a->value;
|
||||
if (row->res == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A total over the omitted hosts, keyed by resource name or, for a charge,
|
||||
* by limit name and scope. Scratch memory; the strings are the sources'. */
|
||||
typedef struct {
|
||||
const char* res; /* resource total; NULL: a limit total */
|
||||
charge_ref_t c; /* limit total */
|
||||
double value;
|
||||
} agg_t;
|
||||
|
||||
static void agg_add(agg_t* agg, int* n, const char* res,
|
||||
const charge_ref_t* c, double value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *n; i++) {
|
||||
if ((res != NULL)
|
||||
? (strcmp(agg[i].res, res) == 0)
|
||||
: ((agg[i].c.name_len == c->name_len)
|
||||
&& (strncmp(agg[i].c.name, c->name, c->name_len) == 0)
|
||||
&& (strcmp(agg[i].c.scope, c->scope) == 0))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == *n) {
|
||||
memset(&agg[i], 0, sizeof(agg[i]));
|
||||
agg[i].res = res;
|
||||
if (c != NULL) {
|
||||
agg[i].c = *c;
|
||||
}
|
||||
(*n)++;
|
||||
}
|
||||
agg[i].value += value;
|
||||
if ((c != NULL) && (agg[i].c.limited_res == NULL)) {
|
||||
agg[i].c.limited_res = c->limited_res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies `n` totals into the arena as rows (a charge's scope has no
|
||||
* host part: it totals hosts). */
|
||||
static bool agg_store(xce_sched_trace_rec_t* rec, const agg_t* agg, int n,
|
||||
int* nout, sched_trace_alloc_row_t** out)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n == 0) {
|
||||
return true;
|
||||
}
|
||||
*out = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(**out) * (size_t)n);
|
||||
if (*out == NULL) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
sched_trace_alloc_row_t* row = &(*out)[i];
|
||||
|
||||
row->value = agg[i].value;
|
||||
if (agg[i].res != NULL) {
|
||||
row->res = sched_trace_arena_strdup(rec, agg[i].res);
|
||||
if (row->res == NULL) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
row->limit = copy_prefix(rec, agg[i].c.name, agg[i].c.name_len);
|
||||
row->scope = copy_scope(rec, agg[i].c.scope, NULL);
|
||||
row->limited_res = sched_trace_arena_strdup(rec, agg[i].c.limited_res);
|
||||
if ((row->limit == NULL) || (row->scope == NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*nout = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Totals of the hosts from index `from` on (9.4): per resource without the
|
||||
* limit charges, and per limit. False on an allocation failure. */
|
||||
static bool aggregate_hosts(xce_sched_trace_rec_t* rec, xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* alloc, int from)
|
||||
{
|
||||
sched_trace_dispatch_out_t* d = &rec->dispatch;
|
||||
size_t total = 0;
|
||||
agg_t* res;
|
||||
agg_t* chg;
|
||||
int nres = 0;
|
||||
int nchg = 0;
|
||||
bool ok;
|
||||
int i;
|
||||
int k;
|
||||
|
||||
for (i = from; i < alloc->nhosts; i++) {
|
||||
if ((alloc->allocs[i] != NULL) && (alloc->allocs[i]->allocs != NULL)
|
||||
&& (alloc->allocs[i]->nallocs > 0)) {
|
||||
total += (size_t)alloc->allocs[i]->nallocs;
|
||||
}
|
||||
}
|
||||
res = (agg_t*)malloc(sizeof(*res) * (total + 1));
|
||||
chg = (agg_t*)malloc(sizeof(*chg) * (total + 1));
|
||||
ok = (res != NULL) && (chg != NULL);
|
||||
|
||||
for (i = from; ok && (i < alloc->nhosts); i++) {
|
||||
const xce_host_alloc_t* h = alloc->allocs[i];
|
||||
const xlist_t* limits;
|
||||
|
||||
if ((h == NULL) || (h->allocs == NULL)) {
|
||||
continue;
|
||||
}
|
||||
limits = host_limits(core, obj, h->host);
|
||||
for (k = 0; k < h->nallocs; k++) {
|
||||
const xce_alloc_t* a = h->allocs[k];
|
||||
charge_ref_t c;
|
||||
|
||||
if ((a == NULL) || (a->resname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_limit_charge(core, a->resname)) {
|
||||
agg_add(res, &nres, a->resname, NULL, a->value);
|
||||
continue;
|
||||
}
|
||||
charge_resolve(list_vres(limits, NULL, a->resname), a->resname,
|
||||
&c);
|
||||
agg_add(chg, &nchg, NULL, &c, a->value);
|
||||
}
|
||||
}
|
||||
ok = ok && agg_store(rec, res, nres, &d->nagg_res, &d->agg_res)
|
||||
&& agg_store(rec, chg, nchg, &d->nagg_charges, &d->agg_charges);
|
||||
free(res);
|
||||
free(chg);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void sched_trace_capture_dispatch(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* alloc)
|
||||
{
|
||||
sched_trace_dispatch_out_t* d = &rec->dispatch;
|
||||
int from = -1;
|
||||
int nh;
|
||||
int i;
|
||||
|
||||
memset(d, 0, sizeof(*d));
|
||||
rec->outcome = SCHED_TRACE_OUTCOME_DISPATCH;
|
||||
if (alloc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!alloc_rows(rec, core, (obj != NULL) ? &obj->glimits : NULL,
|
||||
alloc->nres_shared, alloc->allocs_shared, NULL,
|
||||
&d->nshared, &d->shared, &d->nshared_charges,
|
||||
&d->shared_charges)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nh = (alloc->allocs != NULL) ? alloc->nhosts : 0;
|
||||
if (nh > 0) {
|
||||
int keep = (nh < XCE_TRACE_OUTCOME_HOSTS) ? nh : XCE_TRACE_OUTCOME_HOSTS;
|
||||
|
||||
d->hosts = (sched_trace_host_out_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(*d->hosts) * (size_t)keep);
|
||||
if (d->hosts == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nh; i++) {
|
||||
const xce_host_alloc_t* h = alloc->allocs[i];
|
||||
sched_trace_host_out_t* ho;
|
||||
|
||||
if (h == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (d->nhosts == XCE_TRACE_OUTCOME_HOSTS) {
|
||||
if (from < 0) {
|
||||
from = i;
|
||||
}
|
||||
d->omitted_hosts++;
|
||||
continue;
|
||||
}
|
||||
ho = &d->hosts[d->nhosts++];
|
||||
ho->host = sched_trace_arena_strdup(rec, h->host);
|
||||
if (((h->host != NULL) && (ho->host == NULL))
|
||||
|| !alloc_rows(rec, core, host_limits(core, obj, h->host),
|
||||
h->nallocs, h->allocs,
|
||||
(h->host != NULL) ? h->host : "", &ho->nres,
|
||||
&ho->res, &ho->ncharges, &ho->charges)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((from >= 0) && !aggregate_hosts(rec, core, obj, alloc, from)) {
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
}
|
||||
|
||||
xce_sched_trace_rec_t*
|
||||
sched_trace_rec_reconstructed(const xce_sched_trace_ctl_t* ctl,
|
||||
xce_sched_core_t* core, uint64_t jobid,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* allocs)
|
||||
{
|
||||
xce_sched_trace_rec_t* rec;
|
||||
|
||||
rec = sched_trace_rec_new(ctl, jobid, XCE_TRACE_REC_RECONSTRUCTED);
|
||||
if (rec != NULL) {
|
||||
sched_trace_capture_dispatch(rec, core, obj, allocs);
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/* ---- pend outcome (6.4, 9.4) ------------------------------------------ */
|
||||
|
||||
/* A set of host names, open addressing over a power-of-two table. Scratch
|
||||
* memory; the names are the source's. */
|
||||
typedef struct {
|
||||
const char** slot;
|
||||
size_t mask;
|
||||
int n;
|
||||
} host_set_t;
|
||||
|
||||
static bool host_set_init(host_set_t* s, int nmax)
|
||||
{
|
||||
size_t cap = 16;
|
||||
|
||||
while (cap < (size_t)nmax * 2 + 2) {
|
||||
cap <<= 1;
|
||||
}
|
||||
s->slot = (const char**)calloc(cap, sizeof(*s->slot));
|
||||
s->mask = cap - 1;
|
||||
s->n = 0;
|
||||
return s->slot != NULL;
|
||||
}
|
||||
|
||||
/* True iff `h` is in the set; when absent and `insert`, adds it. */
|
||||
static bool host_set_has(host_set_t* s, const char* h, bool insert)
|
||||
{
|
||||
size_t i = 2166136261u; /* FNV-1a */
|
||||
const unsigned char* p;
|
||||
|
||||
for (p = (const unsigned char*)h; *p != '\0'; p++) {
|
||||
i = (i ^ *p) * 16777619u;
|
||||
}
|
||||
for (i &= s->mask; s->slot[i] != NULL; i = (i + 1) & s->mask) {
|
||||
if (strcmp(s->slot[i], h) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (insert) {
|
||||
s->slot[i] = h;
|
||||
s->n++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
host_set_t kept; /* the first 50 distinct hosts */
|
||||
host_set_t omitted; /* the hosts after them */
|
||||
sched_trace_reason_count_t* codes; /* omitted entries per code */
|
||||
int ncodes;
|
||||
} pend_build_t;
|
||||
|
||||
/* True iff an entry naming `host` is kept: it names no host, or one of the
|
||||
* first 50 distinct hosts. Otherwise it is counted under its code. */
|
||||
static bool pend_keep(pend_build_t* b, const char* host, int code)
|
||||
{
|
||||
if ((host == NULL) || (host[0] == '\0')
|
||||
|| host_set_has(&b->kept, host, false)) {
|
||||
return true;
|
||||
}
|
||||
if (b->kept.n < XCE_TRACE_OUTCOME_HOSTS) {
|
||||
(void)host_set_has(&b->kept, host, true);
|
||||
return true;
|
||||
}
|
||||
(void)host_set_has(&b->omitted, host, true);
|
||||
count_code(b->codes, &b->ncodes, code);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Arena copies of the kept host entries (NULL entries stay NULL). */
|
||||
static bool copy_host_reasons(xce_sched_trace_rec_t* rec,
|
||||
const xce_job_preason_t* r, const bool* keep,
|
||||
int nh, xce_job_preason_t* c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nh; i++) {
|
||||
c->nhost_reasons += keep[i] ? 1 : 0;
|
||||
}
|
||||
if (c->nhost_reasons == 0) {
|
||||
return true;
|
||||
}
|
||||
c->host_based_reasons = (xce_host_reason_t**)sched_trace_arena_alloc(
|
||||
rec, sizeof(*c->host_based_reasons) * (size_t)c->nhost_reasons);
|
||||
if (c->host_based_reasons == NULL) {
|
||||
return false;
|
||||
}
|
||||
c->nhost_reasons = 0;
|
||||
for (i = 0; i < nh; i++) {
|
||||
const xce_host_reason_t* src = r->host_based_reasons[i];
|
||||
xce_host_reason_t* dst = NULL;
|
||||
|
||||
if (!keep[i]) {
|
||||
continue;
|
||||
}
|
||||
if (src != NULL) {
|
||||
dst = (xce_host_reason_t*)sched_trace_arena_alloc(rec, sizeof(*dst));
|
||||
if (dst == NULL) {
|
||||
return false;
|
||||
}
|
||||
dst->reason = src->reason;
|
||||
dst->host = sched_trace_arena_strdup(rec, src->host);
|
||||
if ((src->host != NULL) && (dst->host == NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
c->host_based_reasons[c->nhost_reasons++] = dst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Arena copies of the kept resource entries (NULL entries stay NULL). */
|
||||
static bool copy_res_reasons(xce_sched_trace_rec_t* rec,
|
||||
const xce_job_preason_t* r, const bool* keep,
|
||||
int nr, xce_job_preason_t* c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
c->nres_reasons += keep[i] ? 1 : 0;
|
||||
}
|
||||
if (c->nres_reasons == 0) {
|
||||
return true;
|
||||
}
|
||||
c->res_based_reasons = (xce_res_reason_t**)sched_trace_arena_alloc(
|
||||
rec, sizeof(*c->res_based_reasons) * (size_t)c->nres_reasons);
|
||||
if (c->res_based_reasons == NULL) {
|
||||
return false;
|
||||
}
|
||||
c->nres_reasons = 0;
|
||||
for (i = 0; i < nr; i++) {
|
||||
const xce_res_reason_t* src = r->res_based_reasons[i];
|
||||
xce_res_reason_t* dst = NULL;
|
||||
|
||||
if (!keep[i]) {
|
||||
continue;
|
||||
}
|
||||
if (src != NULL) {
|
||||
dst = (xce_res_reason_t*)sched_trace_arena_alloc(rec, sizeof(*dst));
|
||||
if (dst == NULL) {
|
||||
return false;
|
||||
}
|
||||
dst->reason = src->reason;
|
||||
dst->host = sched_trace_arena_strdup(rec, src->host);
|
||||
dst->resource = sched_trace_arena_strdup(rec, src->resource);
|
||||
if (((src->host != NULL) && (dst->host == NULL))
|
||||
|| ((src->resource != NULL) && (dst->resource == NULL))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
c->res_based_reasons[c->nres_reasons++] = dst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sched_trace_capture_pend(xce_sched_trace_rec_t* rec,
|
||||
const xce_pendreason_t* reason)
|
||||
{
|
||||
sched_trace_pend_out_t* p = &rec->pend;
|
||||
const xce_job_preason_t* r = (reason != NULL) ? reason->reasons : NULL;
|
||||
pend_build_t b;
|
||||
bool* keep;
|
||||
int nh;
|
||||
int nr;
|
||||
int i;
|
||||
bool ok;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
rec->outcome = SCHED_TRACE_OUTCOME_PEND;
|
||||
if (r == NULL) {
|
||||
return;
|
||||
}
|
||||
nh = ((r->host_based_reasons != NULL) && (r->nhost_reasons > 0))
|
||||
? r->nhost_reasons : 0;
|
||||
nr = ((r->res_based_reasons != NULL) && (r->nres_reasons > 0))
|
||||
? r->nres_reasons : 0;
|
||||
|
||||
/* which entries are kept, in one pass over the source order */
|
||||
memset(&b, 0, sizeof(b));
|
||||
keep = (bool*)malloc(sizeof(*keep) * (size_t)(nh + nr + 1));
|
||||
b.codes = (sched_trace_reason_count_t*)malloc(sizeof(*b.codes)
|
||||
* (size_t)(nh + nr + 1));
|
||||
ok = (keep != NULL) && (b.codes != NULL)
|
||||
&& host_set_init(&b.kept, XCE_TRACE_OUTCOME_HOSTS)
|
||||
&& host_set_init(&b.omitted, nh + nr);
|
||||
for (i = 0; ok && (i < nh); i++) {
|
||||
const xce_host_reason_t* e = r->host_based_reasons[i];
|
||||
|
||||
keep[i] = (e == NULL) || pend_keep(&b, e->host, e->reason);
|
||||
}
|
||||
for (i = 0; ok && (i < nr); i++) {
|
||||
const xce_res_reason_t* e = r->res_based_reasons[i];
|
||||
|
||||
keep[nh + i] = (e == NULL) || pend_keep(&b, e->host, e->reason);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
p->reasons = (xce_job_preason_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(*p->reasons));
|
||||
ok = (p->reasons != NULL);
|
||||
}
|
||||
if (ok) {
|
||||
p->reasons->main_reason = r->main_reason;
|
||||
ok = copy_host_reasons(rec, r, keep, nh, p->reasons)
|
||||
&& copy_res_reasons(rec, r, keep + nh, nr, p->reasons);
|
||||
}
|
||||
if (ok && (b.ncodes > 0)) {
|
||||
p->omitted_codes = (sched_trace_reason_count_t*)sched_trace_arena_memdup(
|
||||
rec, b.codes, sizeof(*b.codes) * (size_t)b.ncodes);
|
||||
ok = (p->omitted_codes != NULL);
|
||||
p->ncodes = ok ? b.ncodes : 0;
|
||||
}
|
||||
p->omitted_hosts = b.omitted.n;
|
||||
if (!ok) {
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
}
|
||||
free(keep);
|
||||
free(b.codes);
|
||||
free((void*)b.kept.slot);
|
||||
free((void*)b.omitted.slot);
|
||||
}
|
||||
|
|
|
|||
83
src/scheduler/src/sched_trace_internal.h
Normal file
83
src/scheduler/src/sched_trace_internal.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#if !defined(_SCHED_TRACE_INTERNAL_H_)
|
||||
|
||||
#define _SCHED_TRACE_INTERNAL_H_ 1
|
||||
|
||||
/*
|
||||
* sched_trace_internal.h - declarations shared by the decision trace
|
||||
* sources (scheduler-sim-decision-trace spec, task 2.4). NOT installed and
|
||||
* NOT included outside src/scheduler/src/sched_trace*.c; the public
|
||||
* interface stays include/sched/trace.h.
|
||||
*
|
||||
* The sources are split to keep each under the scheduler's 800-line cap:
|
||||
*
|
||||
* - sched_trace.c: the record (arena, header, lifecycle), the entry
|
||||
* sequence and line budget, and the requirement, plan and check
|
||||
* entries (TraceModel).
|
||||
* - sched_trace_scan.c: positions, host verdicts and rows, the
|
||||
* summary-level tallies, rollback, commit and the search-end counters
|
||||
* (TraceModel).
|
||||
* - sched_trace_capture.c: limit names and scopes, selection,
|
||||
* cluster-wide limits, check rows and attempt reasons (TraceCapture).
|
||||
* - sched_trace_outcome.c: the dispatch and pend outcomes and the
|
||||
* reconstructed-dispatch record (TraceCapture).
|
||||
*
|
||||
* The helpers below were file statics before the split. They have
|
||||
* external linkage now, so they carry the sti_ prefix; nothing outside
|
||||
* the trace sources calls them. The same no-I/O rule applies to this
|
||||
* header (test_core_isolation.c).
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "sched/trace.h"
|
||||
|
||||
/* ---- model (sched_trace.c), used by sched_trace_scan.c ---------------- */
|
||||
|
||||
/* Summary level: one limit hit by hosts of the current position. Builder
|
||||
* memory, owned by the record until the position ends. */
|
||||
struct sched_trace_tally_build_s {
|
||||
char* limit;
|
||||
char* scope;
|
||||
int count; /* hosts with a limit-exceeded verdict */
|
||||
unsigned last_host; /* host_seq of the last host that hit it */
|
||||
};
|
||||
|
||||
extern void sti_pos_limits_clear(xce_sched_trace_rec_t* rec);
|
||||
extern void sti_host_rows_clear(xce_sched_trace_rec_t* rec);
|
||||
extern sched_trace_entry_t* sti_entry_add(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type);
|
||||
extern void sti_rollback_run_end(xce_sched_trace_rec_t* rec);
|
||||
extern sched_trace_entry_t* sti_scope_begin(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type);
|
||||
extern sched_trace_entry_t* sti_scope_end(xce_sched_trace_rec_t* rec,
|
||||
sched_trace_entry_type_t type);
|
||||
|
||||
/* ---- capture (sched_trace_capture.c), used by sched_trace_outcome.c --- */
|
||||
|
||||
#define VRES_PREFIX "limit="
|
||||
#define VRES_PREFIX_LEN (sizeof(VRES_PREFIX) - 1)
|
||||
|
||||
/* A limit resource named `resname`, resolved: the limit name (not
|
||||
* NUL-terminated), the vres-form scope part and the limited resource. */
|
||||
typedef struct {
|
||||
const char* name;
|
||||
size_t name_len;
|
||||
const char* scope;
|
||||
const char* limited_res; /* NULL: no matched limit names it */
|
||||
} charge_ref_t;
|
||||
|
||||
extern char* sti_copy_scope(xce_sched_trace_rec_t* rec, const char* scope,
|
||||
const char* host);
|
||||
extern char* sti_copy_prefix(xce_sched_trace_rec_t* rec, const char* s,
|
||||
size_t n);
|
||||
extern const xlist_t* sti_worker_limits(const xce_sched_obj_t* obj, int idx);
|
||||
extern const xce_vres_t* sti_list_vres(const xlist_t* list,
|
||||
const xce_resource_t* r,
|
||||
const char* name);
|
||||
extern void sti_charge_resolve(const xce_vres_t* v, const char* resname,
|
||||
charge_ref_t* c);
|
||||
extern void sti_count_code(sched_trace_reason_count_t* codes, int* ncodes,
|
||||
int code);
|
||||
|
||||
#endif /* _SCHED_TRACE_INTERNAL_H_ */
|
||||
548
src/scheduler/src/sched_trace_outcome.c
Normal file
548
src/scheduler/src/sched_trace_outcome.c
Normal file
|
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* sched_trace_outcome.c - allocator-side trace capture, part 2
|
||||
* (scheduler-sim-decision-trace spec, design.md "TraceCapture").
|
||||
*
|
||||
* The decision's outcome: the dispatch (6.1-6.3) with its per-host
|
||||
* resource rows and limit charges, totals over the hosts past the first
|
||||
* XCE_TRACE_OUTCOME_HOSTS (9.4), the pend reasons (6.4, 9.4), and the
|
||||
* reconstructed-dispatch record (2.5). Split from sched_trace_capture.c
|
||||
* (task 2.4), whose limit-resolution helpers it shares through
|
||||
* sched_trace_internal.h.
|
||||
*
|
||||
* Same rules as sched_trace_capture.c: inputs are read-only, struct fields
|
||||
* and librtti's key_find() only, no scheduler API that mutates state
|
||||
* (Requirement 10), and no I/O and no logging (test_core_isolation.c scans
|
||||
* this file).
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "sched/trace.h"
|
||||
#include "sched_trace_internal.h"
|
||||
|
||||
/* ---- dispatch outcome (6.1-6.3, 9.4) ---------------------------------- */
|
||||
|
||||
/* True iff the allocated resource `resname` is a limit charge: its
|
||||
* registered resource has XCE_RESF_LIMIT, or, when the name is not
|
||||
* registered in this replay, it has the "limit=" prefix. */
|
||||
static bool is_limit_charge(xce_sched_core_t* core, const char* resname)
|
||||
{
|
||||
xce_resource_t* r = NULL;
|
||||
|
||||
if ((core != NULL) && (core->resmgr != NULL)
|
||||
&& (key_find(core->resmgr, (void*)resname, (void**)&r) >= 0)
|
||||
&& (r != NULL)) {
|
||||
return (r->flags & XCE_RESF_LIMIT) != 0;
|
||||
}
|
||||
return strncmp(resname, VRES_PREFIX, VRES_PREFIX_LEN) == 0;
|
||||
}
|
||||
|
||||
/* The host limits matched to the job on the worker named `host`, or NULL
|
||||
* (no schedobj, unknown worker, none matched). */
|
||||
static const xlist_t* host_limits(xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj, const char* host)
|
||||
{
|
||||
xce_sched_worker_t* w = NULL;
|
||||
|
||||
if ((obj == NULL) || (core == NULL) || (core->workers == NULL)
|
||||
|| (host == NULL)
|
||||
|| (key_find(core->workers, (void*)host, (void**)&w) < 0)
|
||||
|| (w == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return sti_worker_limits(obj, w->idx);
|
||||
}
|
||||
|
||||
/* Stores allocated amount `a` as a limit charge, resolved through `limits`;
|
||||
* a host charge (`host` non-NULL) gets the host appended to its scope. */
|
||||
static bool charge_row(xce_sched_trace_rec_t* rec, const xlist_t* limits,
|
||||
const xce_alloc_t* a, const char* host,
|
||||
sched_trace_alloc_row_t* out)
|
||||
{
|
||||
charge_ref_t c;
|
||||
|
||||
sti_charge_resolve(sti_list_vres(limits, NULL, a->resname), a->resname, &c);
|
||||
out->res = sched_trace_arena_strdup(rec, a->resname);
|
||||
out->value = a->value;
|
||||
out->limit = sti_copy_prefix(rec, c.name, c.name_len);
|
||||
out->scope = sti_copy_scope(rec, c.scope, host);
|
||||
out->limited_res = sched_trace_arena_strdup(rec, c.limited_res);
|
||||
return (out->res != NULL) && (out->limit != NULL) && (out->scope != NULL)
|
||||
&& ((c.limited_res == NULL) || (out->limited_res != NULL));
|
||||
}
|
||||
|
||||
/* One allocation list (a host's, or the shared one) as resource rows and
|
||||
* limit charges, each in allocation order. False on an arena failure. */
|
||||
static bool alloc_rows(xce_sched_trace_rec_t* rec, xce_sched_core_t* core,
|
||||
const xlist_t* limits, int n, xce_alloc_t** allocs,
|
||||
const char* host, int* nres,
|
||||
sched_trace_alloc_row_t** res, int* ncharges,
|
||||
sched_trace_alloc_row_t** charges)
|
||||
{
|
||||
size_t sz = sizeof(**res) * (size_t)n;
|
||||
int i;
|
||||
|
||||
if ((n <= 0) || (allocs == NULL)) {
|
||||
return true;
|
||||
}
|
||||
*res = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(rec, sz);
|
||||
*charges = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(rec, sz);
|
||||
if ((*res == NULL) || (*charges == NULL)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
const xce_alloc_t* a = allocs[i];
|
||||
sched_trace_alloc_row_t* row;
|
||||
|
||||
if ((a == NULL) || (a->resname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
if (is_limit_charge(core, a->resname)) {
|
||||
if (!charge_row(rec, limits, a, host, &(*charges)[(*ncharges)++])) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
row = &(*res)[(*nres)++];
|
||||
row->res = sched_trace_arena_strdup(rec, a->resname);
|
||||
row->value = a->value;
|
||||
if (row->res == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A total over the omitted hosts, keyed by resource name or, for a charge,
|
||||
* by limit name and scope. Scratch memory; the strings are the sources'. */
|
||||
typedef struct {
|
||||
const char* res; /* resource total; NULL: a limit total */
|
||||
charge_ref_t c; /* limit total */
|
||||
double value;
|
||||
} agg_t;
|
||||
|
||||
static void agg_add(agg_t* agg, int* n, const char* res,
|
||||
const charge_ref_t* c, double value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *n; i++) {
|
||||
if ((res != NULL)
|
||||
? (strcmp(agg[i].res, res) == 0)
|
||||
: ((agg[i].c.name_len == c->name_len)
|
||||
&& (strncmp(agg[i].c.name, c->name, c->name_len) == 0)
|
||||
&& (strcmp(agg[i].c.scope, c->scope) == 0))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == *n) {
|
||||
memset(&agg[i], 0, sizeof(agg[i]));
|
||||
agg[i].res = res;
|
||||
if (c != NULL) {
|
||||
agg[i].c = *c;
|
||||
}
|
||||
(*n)++;
|
||||
}
|
||||
agg[i].value += value;
|
||||
if ((c != NULL) && (agg[i].c.limited_res == NULL)) {
|
||||
agg[i].c.limited_res = c->limited_res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies `n` totals into the arena as rows (a charge's scope has no
|
||||
* host part: it totals hosts). */
|
||||
static bool agg_store(xce_sched_trace_rec_t* rec, const agg_t* agg, int n,
|
||||
int* nout, sched_trace_alloc_row_t** out)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n == 0) {
|
||||
return true;
|
||||
}
|
||||
*out = (sched_trace_alloc_row_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(**out) * (size_t)n);
|
||||
if (*out == NULL) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
sched_trace_alloc_row_t* row = &(*out)[i];
|
||||
|
||||
row->value = agg[i].value;
|
||||
if (agg[i].res != NULL) {
|
||||
row->res = sched_trace_arena_strdup(rec, agg[i].res);
|
||||
if (row->res == NULL) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
row->limit
|
||||
= sti_copy_prefix(rec, agg[i].c.name, agg[i].c.name_len);
|
||||
row->scope = sti_copy_scope(rec, agg[i].c.scope, NULL);
|
||||
row->limited_res = sched_trace_arena_strdup(rec, agg[i].c.limited_res);
|
||||
if ((row->limit == NULL) || (row->scope == NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*nout = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Totals of the hosts from index `from` on (9.4): per resource without the
|
||||
* limit charges, and per limit. False on an allocation failure. */
|
||||
static bool aggregate_hosts(xce_sched_trace_rec_t* rec, xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* alloc, int from)
|
||||
{
|
||||
sched_trace_dispatch_out_t* d = &rec->dispatch;
|
||||
size_t total = 0;
|
||||
agg_t* res;
|
||||
agg_t* chg;
|
||||
int nres = 0;
|
||||
int nchg = 0;
|
||||
bool ok;
|
||||
int i;
|
||||
int k;
|
||||
|
||||
for (i = from; i < alloc->nhosts; i++) {
|
||||
if ((alloc->allocs[i] != NULL) && (alloc->allocs[i]->allocs != NULL)
|
||||
&& (alloc->allocs[i]->nallocs > 0)) {
|
||||
total += (size_t)alloc->allocs[i]->nallocs;
|
||||
}
|
||||
}
|
||||
res = (agg_t*)malloc(sizeof(*res) * (total + 1));
|
||||
chg = (agg_t*)malloc(sizeof(*chg) * (total + 1));
|
||||
ok = (res != NULL) && (chg != NULL);
|
||||
|
||||
for (i = from; ok && (i < alloc->nhosts); i++) {
|
||||
const xce_host_alloc_t* h = alloc->allocs[i];
|
||||
const xlist_t* limits;
|
||||
|
||||
if ((h == NULL) || (h->allocs == NULL)) {
|
||||
continue;
|
||||
}
|
||||
limits = host_limits(core, obj, h->host);
|
||||
for (k = 0; k < h->nallocs; k++) {
|
||||
const xce_alloc_t* a = h->allocs[k];
|
||||
charge_ref_t c;
|
||||
|
||||
if ((a == NULL) || (a->resname == NULL)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_limit_charge(core, a->resname)) {
|
||||
agg_add(res, &nres, a->resname, NULL, a->value);
|
||||
continue;
|
||||
}
|
||||
sti_charge_resolve(sti_list_vres(limits, NULL, a->resname),
|
||||
a->resname, &c);
|
||||
agg_add(chg, &nchg, NULL, &c, a->value);
|
||||
}
|
||||
}
|
||||
ok = ok && agg_store(rec, res, nres, &d->nagg_res, &d->agg_res)
|
||||
&& agg_store(rec, chg, nchg, &d->nagg_charges, &d->agg_charges);
|
||||
free(res);
|
||||
free(chg);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void sched_trace_capture_dispatch(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_core_t* core,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* alloc)
|
||||
{
|
||||
sched_trace_dispatch_out_t* d = &rec->dispatch;
|
||||
int from = -1;
|
||||
int nh;
|
||||
int i;
|
||||
|
||||
memset(d, 0, sizeof(*d));
|
||||
rec->outcome = SCHED_TRACE_OUTCOME_DISPATCH;
|
||||
if (alloc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!alloc_rows(rec, core, (obj != NULL) ? &obj->glimits : NULL,
|
||||
alloc->nres_shared, alloc->allocs_shared, NULL,
|
||||
&d->nshared, &d->shared, &d->nshared_charges,
|
||||
&d->shared_charges)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nh = (alloc->allocs != NULL) ? alloc->nhosts : 0;
|
||||
if (nh > 0) {
|
||||
int keep = (nh < XCE_TRACE_OUTCOME_HOSTS) ? nh : XCE_TRACE_OUTCOME_HOSTS;
|
||||
|
||||
d->hosts = (sched_trace_host_out_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(*d->hosts) * (size_t)keep);
|
||||
if (d->hosts == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nh; i++) {
|
||||
const xce_host_alloc_t* h = alloc->allocs[i];
|
||||
sched_trace_host_out_t* ho;
|
||||
|
||||
if (h == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (d->nhosts == XCE_TRACE_OUTCOME_HOSTS) {
|
||||
if (from < 0) {
|
||||
from = i;
|
||||
}
|
||||
d->omitted_hosts++;
|
||||
continue;
|
||||
}
|
||||
ho = &d->hosts[d->nhosts++];
|
||||
ho->host = sched_trace_arena_strdup(rec, h->host);
|
||||
if (((h->host != NULL) && (ho->host == NULL))
|
||||
|| !alloc_rows(rec, core, host_limits(core, obj, h->host),
|
||||
h->nallocs, h->allocs,
|
||||
(h->host != NULL) ? h->host : "", &ho->nres,
|
||||
&ho->res, &ho->ncharges, &ho->charges)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((from >= 0) && !aggregate_hosts(rec, core, obj, alloc, from)) {
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
}
|
||||
|
||||
xce_sched_trace_rec_t*
|
||||
sched_trace_rec_reconstructed(const xce_sched_trace_ctl_t* ctl,
|
||||
xce_sched_core_t* core, uint64_t jobid,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_job_alloc_t* allocs)
|
||||
{
|
||||
xce_sched_trace_rec_t* rec;
|
||||
|
||||
rec = sched_trace_rec_new(ctl, jobid, XCE_TRACE_REC_RECONSTRUCTED);
|
||||
if (rec != NULL) {
|
||||
sched_trace_capture_dispatch(rec, core, obj, allocs);
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/* ---- pend outcome (6.4, 9.4) ------------------------------------------ */
|
||||
|
||||
/* A set of host names, open addressing over a power-of-two table. Scratch
|
||||
* memory; the names are the source's. */
|
||||
typedef struct {
|
||||
const char** slot;
|
||||
size_t mask;
|
||||
int n;
|
||||
} host_set_t;
|
||||
|
||||
static bool host_set_init(host_set_t* s, int nmax)
|
||||
{
|
||||
size_t cap = 16;
|
||||
|
||||
while (cap < (size_t)nmax * 2 + 2) {
|
||||
cap <<= 1;
|
||||
}
|
||||
s->slot = (const char**)calloc(cap, sizeof(*s->slot));
|
||||
s->mask = cap - 1;
|
||||
s->n = 0;
|
||||
return s->slot != NULL;
|
||||
}
|
||||
|
||||
/* True iff `h` is in the set; when absent and `insert`, adds it. */
|
||||
static bool host_set_has(host_set_t* s, const char* h, bool insert)
|
||||
{
|
||||
size_t i = 2166136261u; /* FNV-1a */
|
||||
const unsigned char* p;
|
||||
|
||||
for (p = (const unsigned char*)h; *p != '\0'; p++) {
|
||||
i = (i ^ *p) * 16777619u;
|
||||
}
|
||||
for (i &= s->mask; s->slot[i] != NULL; i = (i + 1) & s->mask) {
|
||||
if (strcmp(s->slot[i], h) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (insert) {
|
||||
s->slot[i] = h;
|
||||
s->n++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
host_set_t kept; /* the first 50 distinct hosts */
|
||||
host_set_t omitted; /* the hosts after them */
|
||||
sched_trace_reason_count_t* codes; /* omitted entries per code */
|
||||
int ncodes;
|
||||
} pend_build_t;
|
||||
|
||||
/* True iff an entry naming `host` is kept: it names no host, or one of the
|
||||
* first 50 distinct hosts. Otherwise it is counted under its code. */
|
||||
static bool pend_keep(pend_build_t* b, const char* host, int code)
|
||||
{
|
||||
if ((host == NULL) || (host[0] == '\0')
|
||||
|| host_set_has(&b->kept, host, false)) {
|
||||
return true;
|
||||
}
|
||||
if (b->kept.n < XCE_TRACE_OUTCOME_HOSTS) {
|
||||
(void)host_set_has(&b->kept, host, true);
|
||||
return true;
|
||||
}
|
||||
(void)host_set_has(&b->omitted, host, true);
|
||||
sti_count_code(b->codes, &b->ncodes, code);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Arena copies of the kept host entries (NULL entries stay NULL). */
|
||||
static bool copy_host_reasons(xce_sched_trace_rec_t* rec,
|
||||
const xce_job_preason_t* r, const bool* keep,
|
||||
int nh, xce_job_preason_t* c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nh; i++) {
|
||||
c->nhost_reasons += keep[i] ? 1 : 0;
|
||||
}
|
||||
if (c->nhost_reasons == 0) {
|
||||
return true;
|
||||
}
|
||||
c->host_based_reasons = (xce_host_reason_t**)sched_trace_arena_alloc(
|
||||
rec, sizeof(*c->host_based_reasons) * (size_t)c->nhost_reasons);
|
||||
if (c->host_based_reasons == NULL) {
|
||||
return false;
|
||||
}
|
||||
c->nhost_reasons = 0;
|
||||
for (i = 0; i < nh; i++) {
|
||||
const xce_host_reason_t* src = r->host_based_reasons[i];
|
||||
xce_host_reason_t* dst = NULL;
|
||||
|
||||
if (!keep[i]) {
|
||||
continue;
|
||||
}
|
||||
if (src != NULL) {
|
||||
dst = (xce_host_reason_t*)sched_trace_arena_alloc(rec, sizeof(*dst));
|
||||
if (dst == NULL) {
|
||||
return false;
|
||||
}
|
||||
dst->reason = src->reason;
|
||||
dst->host = sched_trace_arena_strdup(rec, src->host);
|
||||
if ((src->host != NULL) && (dst->host == NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
c->host_based_reasons[c->nhost_reasons++] = dst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Arena copies of the kept resource entries (NULL entries stay NULL). */
|
||||
static bool copy_res_reasons(xce_sched_trace_rec_t* rec,
|
||||
const xce_job_preason_t* r, const bool* keep,
|
||||
int nr, xce_job_preason_t* c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
c->nres_reasons += keep[i] ? 1 : 0;
|
||||
}
|
||||
if (c->nres_reasons == 0) {
|
||||
return true;
|
||||
}
|
||||
c->res_based_reasons = (xce_res_reason_t**)sched_trace_arena_alloc(
|
||||
rec, sizeof(*c->res_based_reasons) * (size_t)c->nres_reasons);
|
||||
if (c->res_based_reasons == NULL) {
|
||||
return false;
|
||||
}
|
||||
c->nres_reasons = 0;
|
||||
for (i = 0; i < nr; i++) {
|
||||
const xce_res_reason_t* src = r->res_based_reasons[i];
|
||||
xce_res_reason_t* dst = NULL;
|
||||
|
||||
if (!keep[i]) {
|
||||
continue;
|
||||
}
|
||||
if (src != NULL) {
|
||||
dst = (xce_res_reason_t*)sched_trace_arena_alloc(rec, sizeof(*dst));
|
||||
if (dst == NULL) {
|
||||
return false;
|
||||
}
|
||||
dst->reason = src->reason;
|
||||
dst->host = sched_trace_arena_strdup(rec, src->host);
|
||||
dst->resource = sched_trace_arena_strdup(rec, src->resource);
|
||||
if (((src->host != NULL) && (dst->host == NULL))
|
||||
|| ((src->resource != NULL) && (dst->resource == NULL))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
c->res_based_reasons[c->nres_reasons++] = dst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sched_trace_capture_pend(xce_sched_trace_rec_t* rec,
|
||||
const xce_pendreason_t* reason)
|
||||
{
|
||||
sched_trace_pend_out_t* p = &rec->pend;
|
||||
const xce_job_preason_t* r = (reason != NULL) ? reason->reasons : NULL;
|
||||
pend_build_t b;
|
||||
bool* keep;
|
||||
int nh;
|
||||
int nr;
|
||||
int i;
|
||||
bool ok;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
rec->outcome = SCHED_TRACE_OUTCOME_PEND;
|
||||
if (r == NULL) {
|
||||
return;
|
||||
}
|
||||
nh = ((r->host_based_reasons != NULL) && (r->nhost_reasons > 0))
|
||||
? r->nhost_reasons : 0;
|
||||
nr = ((r->res_based_reasons != NULL) && (r->nres_reasons > 0))
|
||||
? r->nres_reasons : 0;
|
||||
|
||||
/* which entries are kept, in one pass over the source order */
|
||||
memset(&b, 0, sizeof(b));
|
||||
keep = (bool*)malloc(sizeof(*keep) * (size_t)(nh + nr + 1));
|
||||
b.codes = (sched_trace_reason_count_t*)malloc(sizeof(*b.codes)
|
||||
* (size_t)(nh + nr + 1));
|
||||
ok = (keep != NULL) && (b.codes != NULL)
|
||||
&& host_set_init(&b.kept, XCE_TRACE_OUTCOME_HOSTS)
|
||||
&& host_set_init(&b.omitted, nh + nr);
|
||||
for (i = 0; ok && (i < nh); i++) {
|
||||
const xce_host_reason_t* e = r->host_based_reasons[i];
|
||||
|
||||
keep[i] = (e == NULL) || pend_keep(&b, e->host, e->reason);
|
||||
}
|
||||
for (i = 0; ok && (i < nr); i++) {
|
||||
const xce_res_reason_t* e = r->res_based_reasons[i];
|
||||
|
||||
keep[nh + i] = (e == NULL) || pend_keep(&b, e->host, e->reason);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
p->reasons = (xce_job_preason_t*)sched_trace_arena_alloc(
|
||||
rec, sizeof(*p->reasons));
|
||||
ok = (p->reasons != NULL);
|
||||
}
|
||||
if (ok) {
|
||||
p->reasons->main_reason = r->main_reason;
|
||||
ok = copy_host_reasons(rec, r, keep, nh, p->reasons)
|
||||
&& copy_res_reasons(rec, r, keep + nh, nr, p->reasons);
|
||||
}
|
||||
if (ok && (b.ncodes > 0)) {
|
||||
p->omitted_codes = (sched_trace_reason_count_t*)sched_trace_arena_memdup(
|
||||
rec, b.codes, sizeof(*b.codes) * (size_t)b.ncodes);
|
||||
ok = (p->omitted_codes != NULL);
|
||||
p->ncodes = ok ? b.ncodes : 0;
|
||||
}
|
||||
p->omitted_hosts = b.omitted.n;
|
||||
if (!ok) {
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
}
|
||||
free(keep);
|
||||
free(b.codes);
|
||||
free((void*)b.kept.slot);
|
||||
free((void*)b.omitted.slot);
|
||||
}
|
||||
491
src/scheduler/src/sched_trace_scan.c
Normal file
491
src/scheduler/src/sched_trace_scan.c
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* sched_trace_scan.c - decision trace record model, part 2
|
||||
* (scheduler-sim-decision-trace spec, design.md "TraceModel").
|
||||
*
|
||||
* The host scan's entries and counters: positions, host verdicts and the
|
||||
* rows held under them, the summary-level per-position tallies (5.9),
|
||||
* rollback runs, commit and finalize failures, and the per-requirement
|
||||
* search-end statement with its decision-wide counters (9.3). Split from
|
||||
* sched_trace.c (task 2.4), which keeps the record, its arena and the entry
|
||||
* sequence these functions append to (sched_trace_internal.h).
|
||||
*
|
||||
* Same rules as sched_trace.c: one building thread, no locking, and no
|
||||
* I/O and no logging (test_core_isolation.c scans this file).
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sched/trace.h"
|
||||
#include "sched_trace_internal.h"
|
||||
|
||||
/* Starts the current position's tallies over. */
|
||||
static void pos_tally_reset(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
memset(rec->pos_counts, 0, sizeof(rec->pos_counts));
|
||||
sti_pos_limits_clear(rec);
|
||||
}
|
||||
|
||||
void sched_trace_pos_begin(xce_sched_trace_rec_t* rec, int pos, int slots)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
rec->cur_pos = pos;
|
||||
rec->scan_err = 0;
|
||||
rec->scan_err_set = false;
|
||||
pos_tally_reset(rec);
|
||||
|
||||
e = sti_scope_begin(rec, SCHED_TRACE_E_POS_BEGIN);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_begin.pos = pos;
|
||||
e->u.pos_begin.slots = slots;
|
||||
}
|
||||
|
||||
void sched_trace_pos_scan_error(xce_sched_trace_rec_t* rec, int err)
|
||||
{
|
||||
sti_rollback_run_end(rec);
|
||||
rec->scan_err = err;
|
||||
rec->scan_err_set = true;
|
||||
}
|
||||
|
||||
/* Fills a POS_SUMMARY entry from the current position's tallies; only
|
||||
* limits some host was rejected for are kept. */
|
||||
static void pos_summary_fill(xce_sched_trace_rec_t* rec, sched_trace_entry_t* e,
|
||||
xce_sched_trace_pos_end_t how, const char* host,
|
||||
int err)
|
||||
{
|
||||
int n = 0;
|
||||
int k;
|
||||
|
||||
e->u.pos_summary.pos = rec->cur_pos;
|
||||
e->u.pos_summary.how = how;
|
||||
e->u.pos_summary.err = err;
|
||||
e->u.pos_summary.host = sched_trace_arena_strdup(rec, host);
|
||||
memcpy(e->u.pos_summary.counts, rec->pos_counts,
|
||||
sizeof(e->u.pos_summary.counts));
|
||||
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
if (rec->pos_limits[k].count > 0) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_summary.limits = (sched_trace_limit_tally_t*)
|
||||
sched_trace_arena_alloc(rec, sizeof(sched_trace_limit_tally_t)
|
||||
* (size_t)n);
|
||||
if (e->u.pos_summary.limits == NULL) {
|
||||
return;
|
||||
}
|
||||
n = 0;
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
const struct sched_trace_tally_build_s* b = &rec->pos_limits[k];
|
||||
sched_trace_limit_tally_t* t;
|
||||
|
||||
if (b->count <= 0) {
|
||||
continue;
|
||||
}
|
||||
t = &e->u.pos_summary.limits[n++];
|
||||
t->limit = sched_trace_arena_strdup(rec, b->limit);
|
||||
t->scope = sched_trace_arena_strdup(rec, b->scope);
|
||||
t->count = b->count;
|
||||
}
|
||||
e->u.pos_summary.nlimits = n;
|
||||
}
|
||||
|
||||
void sched_trace_pos_end(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_trace_pos_end_t how, const char* host)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
int err = 0;
|
||||
|
||||
if (rec->scan_err_set) {
|
||||
/* the host scan stopped: the runner's UNFILLED is a scan error */
|
||||
err = rec->scan_err;
|
||||
if (how == XCE_TRACE_POS_UNFILLED) {
|
||||
how = XCE_TRACE_POS_SCAN_ERROR;
|
||||
}
|
||||
}
|
||||
rec->scan_err = 0;
|
||||
rec->scan_err_set = false;
|
||||
|
||||
if (rec->level == XCE_TRACE_LEVEL_SUMMARY) {
|
||||
/* the position's end carries its host tallies (5.9) */
|
||||
e = sti_scope_end(rec, SCHED_TRACE_E_POS_SUMMARY);
|
||||
if (e != NULL) {
|
||||
pos_summary_fill(rec, e, how, host, err);
|
||||
}
|
||||
pos_tally_reset(rec);
|
||||
return;
|
||||
}
|
||||
|
||||
e = sti_scope_end(rec, SCHED_TRACE_E_POS_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.pos_end.pos = rec->cur_pos;
|
||||
e->u.pos_end.how = how;
|
||||
e->u.pos_end.err = err;
|
||||
e->u.pos_end.host = sched_trace_arena_strdup(rec, host);
|
||||
}
|
||||
|
||||
void sched_trace_host_begin(xce_sched_trace_rec_t* rec,
|
||||
const xce_sched_obj_t* obj,
|
||||
const xce_sched_worker_t* w)
|
||||
{
|
||||
sti_rollback_run_end(rec);
|
||||
rec->host_seq++;
|
||||
rec->scope_obj = obj;
|
||||
rec->scope_w = w;
|
||||
rec->host_row_verdicts = 0;
|
||||
sti_host_rows_clear(rec); /* a scope that ended without its verdict */
|
||||
}
|
||||
|
||||
/* Counts one host evaluation: decision-wide per verdict kind, and at
|
||||
* summary level per position, with each limit the host hit counted once
|
||||
* when its verdict is limit exceeded (5.9, 9.3). */
|
||||
static void host_count(xce_sched_trace_rec_t* rec, unsigned verdicts)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < XCE_TRACE_V_NKINDS; k++) {
|
||||
if ((verdicts & (1u << k)) != 0) {
|
||||
rec->counters.host_evals[k]++;
|
||||
rec->pos_counts[k]++;
|
||||
}
|
||||
}
|
||||
if ((verdicts & XCE_TRACE_V_LIMIT) == 0) {
|
||||
return;
|
||||
}
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
if (rec->pos_limits[k].last_host == rec->host_seq) {
|
||||
rec->pos_limits[k].count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies a row's strings into the arena (NULL stays NULL). */
|
||||
static void row_to_arena(xce_sched_trace_rec_t* rec, sched_trace_row_t* dst,
|
||||
const sched_trace_row_t* src)
|
||||
{
|
||||
*dst = *src;
|
||||
dst->res = sched_trace_arena_strdup(rec, src->res);
|
||||
dst->limit = sched_trace_arena_strdup(rec, src->limit);
|
||||
dst->scope = sched_trace_arena_strdup(rec, src->scope);
|
||||
dst->limited_res = sched_trace_arena_strdup(rec, src->limited_res);
|
||||
}
|
||||
|
||||
/* Stores the current host's held rows after its verdict line, one level
|
||||
* below it, then drops them. Each counts against the budget. */
|
||||
static void host_rows_flush(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < rec->host_nrows; k++) {
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_HOST_ROW);
|
||||
|
||||
if (e == NULL) {
|
||||
continue;
|
||||
}
|
||||
e->depth++;
|
||||
row_to_arena(rec, &e->u.row, &rec->host_rows[k]);
|
||||
}
|
||||
sti_host_rows_clear(rec);
|
||||
}
|
||||
|
||||
/* NULL-preserving heap copy for held rows; sets *failed on failure. */
|
||||
static const char* row_strdup(const char* s, bool* failed)
|
||||
{
|
||||
char* d;
|
||||
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
d = strdup(s);
|
||||
if (d == NULL) {
|
||||
*failed = true;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void sched_trace_row(xce_sched_trace_rec_t* rec, const sched_trace_row_t* row)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_row_t* h;
|
||||
bool failed = false;
|
||||
|
||||
sti_rollback_run_end(rec);
|
||||
if (row == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rec->scope_w == NULL) {
|
||||
/* the cluster-wide check: stored now, before its check-end line */
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_CHECK_ROW);
|
||||
if (e != NULL) {
|
||||
row_to_arena(rec, &e->u.row, row);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* a host scope: no host lines at summary level; at full level the row
|
||||
* waits for the host's verdict line, which it belongs under */
|
||||
if ((rec->level == XCE_TRACE_LEVEL_SUMMARY) || rec->incomplete) {
|
||||
return;
|
||||
}
|
||||
if (rec->host_nrows == rec->host_rows_cap) {
|
||||
int cap = (rec->host_rows_cap > 0) ? rec->host_rows_cap * 2 : 4;
|
||||
void* p = realloc(rec->host_rows, sizeof(*h) * (size_t)cap);
|
||||
|
||||
if (p == NULL) {
|
||||
rec->incomplete = true; /* design "Error Strategy" */
|
||||
return;
|
||||
}
|
||||
rec->host_rows = (sched_trace_row_t*)p;
|
||||
rec->host_rows_cap = cap;
|
||||
}
|
||||
h = &rec->host_rows[rec->host_nrows];
|
||||
*h = *row;
|
||||
h->res = row_strdup(row->res, &failed);
|
||||
h->limit = row_strdup(row->limit, &failed);
|
||||
h->scope = row_strdup(row->scope, &failed);
|
||||
h->limited_res = row_strdup(row->limited_res, &failed);
|
||||
if (failed) {
|
||||
free((void*)h->res);
|
||||
free((void*)h->limit);
|
||||
free((void*)h->scope);
|
||||
free((void*)h->limited_res);
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->host_nrows++;
|
||||
}
|
||||
|
||||
void sched_trace_host_verdict(xce_sched_trace_rec_t* rec, unsigned verdicts,
|
||||
int err)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_phase_t phase = SCHED_TRACE_PHASE_NONE;
|
||||
const char* name
|
||||
= (rec->scope_w != NULL) ? rec->scope_w->name : NULL;
|
||||
|
||||
if (verdicts == 0) {
|
||||
/* from the rows the per-host check recorded; none means the
|
||||
* check's own selection branch rejected the host */
|
||||
verdicts = rec->host_row_verdicts & (XCE_TRACE_V_SHORTFALL
|
||||
| XCE_TRACE_V_LIMIT);
|
||||
if (verdicts == 0) {
|
||||
verdicts = XCE_TRACE_V_SELECT;
|
||||
}
|
||||
phase = SCHED_TRACE_PHASE_PERHOST;
|
||||
} else if ((verdicts & XCE_TRACE_V_SELECT) != 0) {
|
||||
phase = SCHED_TRACE_PHASE_PREFILTER;
|
||||
} else if ((verdicts & (XCE_TRACE_V_SHORTFALL | XCE_TRACE_V_LIMIT)) != 0) {
|
||||
phase = SCHED_TRACE_PHASE_PERHOST;
|
||||
}
|
||||
|
||||
/* one host evaluation, counted once under each kind it matched (9.3) */
|
||||
host_count(rec, verdicts);
|
||||
|
||||
/* the host scope ends here */
|
||||
rec->scope_obj = NULL;
|
||||
rec->scope_w = NULL;
|
||||
rec->host_row_verdicts = 0;
|
||||
rec->host_seq++; /* later hits belong to no host until the next one */
|
||||
sti_rollback_run_end(rec);
|
||||
|
||||
if (rec->level == XCE_TRACE_LEVEL_SUMMARY) {
|
||||
sti_host_rows_clear(rec);
|
||||
return; /* tallied for the position summary, no host line */
|
||||
}
|
||||
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_HOST);
|
||||
if (e != NULL) {
|
||||
e->u.host.host = sched_trace_arena_strdup(rec, name);
|
||||
e->u.host.verdicts = verdicts;
|
||||
e->u.host.phase = phase;
|
||||
e->u.host.err = err;
|
||||
}
|
||||
/* the host's unmet rows, under its verdict line (5.4, 5.5) */
|
||||
host_rows_flush(rec);
|
||||
}
|
||||
|
||||
void sched_trace_commit(xce_sched_trace_rec_t* rec)
|
||||
{
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_COMMIT);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.commit.plan_no = rec->cur_plan_no;
|
||||
}
|
||||
|
||||
void sched_trace_finalize_failed(xce_sched_trace_rec_t* rec, int err)
|
||||
{
|
||||
sched_trace_entry_t* e = sti_entry_add(rec, SCHED_TRACE_E_FINALIZE_FAILED);
|
||||
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.commit.plan_no = rec->cur_plan_no;
|
||||
e->u.commit.err = err;
|
||||
}
|
||||
|
||||
|
||||
static bool tally_str_eq(const char* a, const char* b)
|
||||
{
|
||||
if ((a == NULL) || (b == NULL)) {
|
||||
return a == b;
|
||||
}
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
/* NULL-preserving heap copy for the builder's tallies; sets *failed when a
|
||||
* non-NULL string could not be copied. */
|
||||
static char* tally_strdup(const char* s, bool* failed)
|
||||
{
|
||||
char* d;
|
||||
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
d = strdup(s);
|
||||
if (d == NULL) {
|
||||
*failed = true;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void sched_trace_host_limit_hit(xce_sched_trace_rec_t* rec, const char* limit,
|
||||
const char* scope)
|
||||
{
|
||||
struct sched_trace_tally_build_s* b;
|
||||
bool failed = false;
|
||||
int k;
|
||||
|
||||
sti_rollback_run_end(rec);
|
||||
if (rec->scope_w == NULL) {
|
||||
return; /* not a host scope: the cluster-wide check's rows */
|
||||
}
|
||||
rec->host_row_verdicts |= XCE_TRACE_V_LIMIT;
|
||||
if ((rec->level != XCE_TRACE_LEVEL_SUMMARY) || rec->incomplete) {
|
||||
return; /* full level shows the rows themselves (2.2) */
|
||||
}
|
||||
|
||||
for (k = 0; k < rec->pos_nlimits; k++) {
|
||||
b = &rec->pos_limits[k];
|
||||
if (tally_str_eq(b->limit, limit) && tally_str_eq(b->scope, scope)) {
|
||||
b->last_host = rec->host_seq; /* once per host, at its verdict */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rec->pos_nlimits == rec->pos_limits_cap) {
|
||||
int cap = (rec->pos_limits_cap > 0) ? rec->pos_limits_cap * 2 : 4;
|
||||
void* p = realloc(rec->pos_limits, sizeof(*b) * (size_t)cap);
|
||||
|
||||
if (p == NULL) {
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->pos_limits = (struct sched_trace_tally_build_s*)p;
|
||||
rec->pos_limits_cap = cap;
|
||||
}
|
||||
b = &rec->pos_limits[rec->pos_nlimits];
|
||||
b->limit = tally_strdup(limit, &failed);
|
||||
b->scope = tally_strdup(scope, &failed);
|
||||
b->count = 0;
|
||||
b->last_host = rec->host_seq;
|
||||
if (failed) {
|
||||
free(b->limit);
|
||||
free(b->scope);
|
||||
rec->incomplete = true;
|
||||
return;
|
||||
}
|
||||
rec->pos_nlimits++;
|
||||
}
|
||||
|
||||
/* Appends one host to a ROLLBACK entry, growing its array in the arena. */
|
||||
static void rollback_append(xce_sched_trace_rec_t* rec, sched_trace_entry_t* e,
|
||||
const char* host)
|
||||
{
|
||||
if (e->u.rollback.nhosts == e->u.rollback.cap) {
|
||||
int cap = (e->u.rollback.cap > 0) ? e->u.rollback.cap * 2 : 4;
|
||||
const char** h = (const char**)sched_trace_arena_alloc(
|
||||
rec, sizeof(char*) * (size_t)cap);
|
||||
|
||||
if (h == NULL) {
|
||||
return;
|
||||
}
|
||||
if (e->u.rollback.nhosts > 0) {
|
||||
memcpy(h, e->u.rollback.hosts,
|
||||
sizeof(char*) * (size_t)e->u.rollback.nhosts);
|
||||
}
|
||||
e->u.rollback.hosts = h;
|
||||
e->u.rollback.cap = cap;
|
||||
}
|
||||
e->u.rollback.hosts[e->u.rollback.nhosts++]
|
||||
= sched_trace_arena_strdup(rec, host);
|
||||
}
|
||||
|
||||
void sched_trace_rollback_host(xce_sched_trace_rec_t* rec, const char* host)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
if (rec->rollback_open) {
|
||||
/* the same run of released hosts: the same line */
|
||||
if ((rec->rollback_e != NULL) && !rec->incomplete) {
|
||||
rollback_append(rec, rec->rollback_e, host);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_ROLLBACK);
|
||||
rec->rollback_open = true; /* also when omitted: one line either way */
|
||||
rec->rollback_e = e;
|
||||
if (e != NULL) {
|
||||
rollback_append(rec, e, host);
|
||||
}
|
||||
}
|
||||
|
||||
void sched_trace_search_end(xce_sched_trace_rec_t* rec, int rc)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
sched_trace_search_stmt_t stmt = SCHED_TRACE_SEARCH_NO_STMT;
|
||||
int t = rec->scope_tried;
|
||||
int d = rec->scope_discarded;
|
||||
int x = rec->scope_not_tried;
|
||||
int g = t + d + x;
|
||||
|
||||
if (rc == 0) {
|
||||
if (g == 0) {
|
||||
stmt = SCHED_TRACE_SEARCH_NONE_GENERATED;
|
||||
rec->counters.reqs_none_generated++;
|
||||
} else if (d == g) {
|
||||
stmt = SCHED_TRACE_SEARCH_ALL_DISCARDED;
|
||||
rec->counters.reqs_all_discarded++;
|
||||
} else if ((t > 0) && !rec->scope_plan_err) {
|
||||
stmt = SCHED_TRACE_SEARCH_NONE_PLACED;
|
||||
rec->counters.reqs_none_placed++;
|
||||
}
|
||||
}
|
||||
|
||||
/* the search is over: a later search in this requirement counts anew */
|
||||
rec->scope_tried = 0;
|
||||
rec->scope_discarded = 0;
|
||||
rec->scope_not_tried = 0;
|
||||
rec->scope_plan_err = false;
|
||||
|
||||
e = sti_entry_add(rec, SCHED_TRACE_E_SEARCH_END);
|
||||
if (e == NULL) {
|
||||
return;
|
||||
}
|
||||
e->u.search_end.generated = g;
|
||||
e->u.search_end.tried = t;
|
||||
e->u.search_end.discarded = d;
|
||||
e->u.search_end.not_tried = x;
|
||||
e->u.search_end.rc = rc;
|
||||
e->u.search_end.stmt = stmt;
|
||||
}
|
||||
|
|
@ -241,6 +241,18 @@ sched_core_isolation_test = executable(
|
|||
+ (meson.current_source_dir() / '..' / 'src'
|
||||
/ 'sched_trace_capture.c')
|
||||
+ '"',
|
||||
'-DXCE_SCHED_TRACE_SCAN_C_PATH="'
|
||||
+ (meson.current_source_dir() / '..' / 'src'
|
||||
/ 'sched_trace_scan.c')
|
||||
+ '"',
|
||||
'-DXCE_SCHED_TRACE_OUTCOME_C_PATH="'
|
||||
+ (meson.current_source_dir() / '..' / 'src'
|
||||
/ 'sched_trace_outcome.c')
|
||||
+ '"',
|
||||
'-DXCE_SCHED_TRACE_INTERNAL_H_PATH="'
|
||||
+ (meson.current_source_dir() / '..' / 'src'
|
||||
/ 'sched_trace_internal.h')
|
||||
+ '"',
|
||||
'-DXCE_CORE_LIB_PATH="'
|
||||
+ sched_core_isolation_lib.full_path()
|
||||
+ '"'],
|
||||
|
|
@ -2520,10 +2532,12 @@ test('scheduler repl trace baseline test',
|
|||
# scheduler-sim-decision-trace task 1.3: decision trace record model
|
||||
# (src/sched_trace.c) unit tests -- selection matching, record header and
|
||||
# per-record arena copying and size, record finishing. Links only the model
|
||||
# source; libxce supplies the jobid helpers.
|
||||
# sources (sched_trace.c and, since task 2.4, sched_trace_scan.c); libxce
|
||||
# supplies the jobid helpers.
|
||||
sched_trace_model_test = executable(
|
||||
'sched_trace_model_test',
|
||||
['test_trace_model.c', '../src/sched_trace.c'],
|
||||
['test_trace_model.c', '../src/sched_trace.c',
|
||||
'../src/sched_trace_scan.c'],
|
||||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
|
@ -2537,7 +2551,8 @@ test('scheduler trace model test',
|
|||
# src/decision.c; links the record model for its constructor and free.
|
||||
sched_decision_trace_test = executable(
|
||||
'sched_decision_trace_test',
|
||||
['test_decision_trace.c', '../src/sched_trace.c'],
|
||||
['test_decision_trace.c', '../src/sched_trace.c',
|
||||
'../src/sched_trace_scan.c'],
|
||||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
|
@ -2549,11 +2564,13 @@ test('scheduler decision trace test',
|
|||
# scheduler-sim-decision-trace task 2.1: capture helpers (design.md
|
||||
# "TraceCapture") over a fixture core and job -- selection facts,
|
||||
# cluster-wide limits and attempt failure reasons. Links the record model
|
||||
# and the capture source; libxce supplies the pend-reason helpers.
|
||||
# and the capture sources (split in task 2.4); libxce supplies the
|
||||
# pend-reason helpers.
|
||||
sched_trace_capture_test = executable(
|
||||
'sched_trace_capture_test',
|
||||
['test_trace_capture.c', '../src/sched_trace.c',
|
||||
'../src/sched_trace_capture.c'],
|
||||
'../src/sched_trace_scan.c', '../src/sched_trace_capture.c',
|
||||
'../src/sched_trace_outcome.c'],
|
||||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
/* scheduler-sim-live-allocation spec, task 1.3: sim.h only depends on
|
||||
* core.h (+ stdbool/stdint/stdio, per its own header comment), so it is
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/recording.c"
|
||||
#include "../src/recording_events.c"
|
||||
#include "../src/snapshot.c"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@
|
|||
#include "../src/alloc_runner.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/parjob_allocator.c"
|
||||
|
||||
/* Link-satisfying stubs (same convention as test_seqjob_allocator.c). */
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@
|
|||
#include "../src/alloc_runner.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/seqjob_allocator.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@
|
|||
#include "../src/alloc_runner.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/seqjob_allocator.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ static void* test_calloc(size_t n, size_t sz);
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ static void* test_realloc(void* p, size_t n);
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
void kill_sched_job(void* arg) { (void)arg; }
|
||||
void kill_sched_queue(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -64,11 +64,13 @@
|
|||
* sidesteps that entirely by checking *usage*, not *visibility*).
|
||||
*
|
||||
* Decision trace sources (scheduler-sim-decision-trace spec, task 1.3,
|
||||
* design.md "Invariant Cross-Check" dimension 2): sched_trace.c (record
|
||||
* model) and sched_trace_capture.c (capture helpers) run on the allocator
|
||||
* design.md "Invariant Cross-Check" dimension 2): sched_trace.c and
|
||||
* sched_trace_scan.c (record model), sched_trace_capture.c and
|
||||
* sched_trace_outcome.c (capture helpers) and their shared
|
||||
* sched_trace_internal.h (split in task 2.4) run on the allocator
|
||||
* thread inside the core's allocation chain, so they join layer 1's
|
||||
* forbidden-headers and forbidden-symbols scans, plus a check that applies
|
||||
* to these two files only: no stdio, no file I/O and no logging (a trace
|
||||
* to these trace files only: no stdio, no file I/O and no logging (a trace
|
||||
* record is pure data; only the simulator REPL renders or writes it). They
|
||||
* are NOT part of layer 2: sched_core_isolation_lib covers the four core
|
||||
* state-machine files, and the capture file references scheduler symbols
|
||||
|
|
@ -117,6 +119,18 @@
|
|||
#error \
|
||||
"XCE_SCHED_TRACE_CAPTURE_C_PATH must be defined by meson.build to sched_trace_capture.c's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_SCHED_TRACE_SCAN_C_PATH
|
||||
#error \
|
||||
"XCE_SCHED_TRACE_SCAN_C_PATH must be defined by meson.build to sched_trace_scan.c's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_SCHED_TRACE_OUTCOME_C_PATH
|
||||
#error \
|
||||
"XCE_SCHED_TRACE_OUTCOME_C_PATH must be defined by meson.build to sched_trace_outcome.c's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_SCHED_TRACE_INTERNAL_H_PATH
|
||||
#error \
|
||||
"XCE_SCHED_TRACE_INTERNAL_H_PATH must be defined by meson.build to sched_trace_internal.h's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_CORE_LIB_PATH
|
||||
#error \
|
||||
"XCE_CORE_LIB_PATH must be defined by meson.build to the absolute path of core.c's isolated static_library() archive"
|
||||
|
|
@ -217,6 +231,9 @@ static void test_core_source_paths_are_readable(void)
|
|||
char* cc = read_whole_file(XCE_CORE_CLOCK_C_PATH);
|
||||
char* st = read_whole_file(XCE_SCHED_TRACE_C_PATH);
|
||||
char* sc = read_whole_file(XCE_SCHED_TRACE_CAPTURE_C_PATH);
|
||||
char* ss = read_whole_file(XCE_SCHED_TRACE_SCAN_C_PATH);
|
||||
char* so = read_whole_file(XCE_SCHED_TRACE_OUTCOME_C_PATH);
|
||||
char* si = read_whole_file(XCE_SCHED_TRACE_INTERNAL_H_PATH);
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(h, XCE_CORE_H_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(c, XCE_CORE_C_PATH);
|
||||
|
|
@ -225,6 +242,9 @@ static void test_core_source_paths_are_readable(void)
|
|||
TEST_ASSERT_NOT_NULL_MESSAGE(cc, XCE_CORE_CLOCK_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(st, XCE_SCHED_TRACE_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(sc, XCE_SCHED_TRACE_CAPTURE_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(ss, XCE_SCHED_TRACE_SCAN_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(so, XCE_SCHED_TRACE_OUTCOME_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(si, XCE_SCHED_TRACE_INTERNAL_H_PATH);
|
||||
|
||||
free(h);
|
||||
free(c);
|
||||
|
|
@ -233,6 +253,9 @@ static void test_core_source_paths_are_readable(void)
|
|||
free(cc);
|
||||
free(st);
|
||||
free(sc);
|
||||
free(ss);
|
||||
free(so);
|
||||
free(si);
|
||||
}
|
||||
|
||||
/* Scans one file's text for every FORBIDDEN_INCLUDES entry, tagging each
|
||||
|
|
@ -261,6 +284,9 @@ static void test_core_never_includes_forbidden_headers(void)
|
|||
char* cc = read_whole_file(XCE_CORE_CLOCK_C_PATH);
|
||||
char* st = read_whole_file(XCE_SCHED_TRACE_C_PATH);
|
||||
char* sc = read_whole_file(XCE_SCHED_TRACE_CAPTURE_C_PATH);
|
||||
char* ss = read_whole_file(XCE_SCHED_TRACE_SCAN_C_PATH);
|
||||
char* so = read_whole_file(XCE_SCHED_TRACE_OUTCOME_C_PATH);
|
||||
char* si = read_whole_file(XCE_SCHED_TRACE_INTERNAL_H_PATH);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
|
|
@ -269,6 +295,9 @@ static void test_core_never_includes_forbidden_headers(void)
|
|||
TEST_ASSERT_NOT_NULL(cc);
|
||||
TEST_ASSERT_NOT_NULL(st);
|
||||
TEST_ASSERT_NOT_NULL(sc);
|
||||
TEST_ASSERT_NOT_NULL(ss);
|
||||
TEST_ASSERT_NOT_NULL(so);
|
||||
TEST_ASSERT_NOT_NULL(si);
|
||||
|
||||
assert_no_forbidden_includes(h, "core.h");
|
||||
assert_no_forbidden_includes(c, "core.c");
|
||||
|
|
@ -277,6 +306,9 @@ static void test_core_never_includes_forbidden_headers(void)
|
|||
assert_no_forbidden_includes(cc, "core_clock.c");
|
||||
assert_no_forbidden_includes(st, "sched_trace.c");
|
||||
assert_no_forbidden_includes(sc, "sched_trace_capture.c");
|
||||
assert_no_forbidden_includes(ss, "sched_trace_scan.c");
|
||||
assert_no_forbidden_includes(so, "sched_trace_outcome.c");
|
||||
assert_no_forbidden_includes(si, "sched_trace_internal.h");
|
||||
|
||||
free(h);
|
||||
free(c);
|
||||
|
|
@ -285,6 +317,9 @@ static void test_core_never_includes_forbidden_headers(void)
|
|||
free(cc);
|
||||
free(st);
|
||||
free(sc);
|
||||
free(ss);
|
||||
free(so);
|
||||
free(si);
|
||||
}
|
||||
|
||||
/* Shared by test_core_never_calls_forbidden_symbols() -- see
|
||||
|
|
@ -313,6 +348,9 @@ static void test_core_never_calls_forbidden_symbols(void)
|
|||
char* cc = read_whole_file(XCE_CORE_CLOCK_C_PATH);
|
||||
char* st = read_whole_file(XCE_SCHED_TRACE_C_PATH);
|
||||
char* sc = read_whole_file(XCE_SCHED_TRACE_CAPTURE_C_PATH);
|
||||
char* ss = read_whole_file(XCE_SCHED_TRACE_SCAN_C_PATH);
|
||||
char* so = read_whole_file(XCE_SCHED_TRACE_OUTCOME_C_PATH);
|
||||
char* si = read_whole_file(XCE_SCHED_TRACE_INTERNAL_H_PATH);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
|
|
@ -321,6 +359,9 @@ static void test_core_never_calls_forbidden_symbols(void)
|
|||
TEST_ASSERT_NOT_NULL(cc);
|
||||
TEST_ASSERT_NOT_NULL(st);
|
||||
TEST_ASSERT_NOT_NULL(sc);
|
||||
TEST_ASSERT_NOT_NULL(ss);
|
||||
TEST_ASSERT_NOT_NULL(so);
|
||||
TEST_ASSERT_NOT_NULL(si);
|
||||
|
||||
assert_no_forbidden_symbols(h, "core.h");
|
||||
assert_no_forbidden_symbols(c, "core.c");
|
||||
|
|
@ -329,6 +370,9 @@ static void test_core_never_calls_forbidden_symbols(void)
|
|||
assert_no_forbidden_symbols(cc, "core_clock.c");
|
||||
assert_no_forbidden_symbols(st, "sched_trace.c");
|
||||
assert_no_forbidden_symbols(sc, "sched_trace_capture.c");
|
||||
assert_no_forbidden_symbols(ss, "sched_trace_scan.c");
|
||||
assert_no_forbidden_symbols(so, "sched_trace_outcome.c");
|
||||
assert_no_forbidden_symbols(si, "sched_trace_internal.h");
|
||||
|
||||
free(h);
|
||||
free(c);
|
||||
|
|
@ -337,6 +381,9 @@ static void test_core_never_calls_forbidden_symbols(void)
|
|||
free(cc);
|
||||
free(st);
|
||||
free(sc);
|
||||
free(ss);
|
||||
free(so);
|
||||
free(si);
|
||||
}
|
||||
|
||||
/* core_clock.c is deliberately NOT scanned here -- it is the one
|
||||
|
|
@ -386,8 +433,9 @@ static void test_core_never_calls_wallclock_functions(void)
|
|||
/*
|
||||
* No stdio, no file I/O, no logging in the decision trace sources
|
||||
* (scheduler-sim-decision-trace design.md, TraceModel / TraceCapture: "No
|
||||
* I/O and no tlog"). Applies to sched_trace.c and sched_trace_capture.c
|
||||
* only.
|
||||
* I/O and no tlog"). Applies to the trace sources only: sched_trace.c,
|
||||
* sched_trace_scan.c, sched_trace_capture.c, sched_trace_outcome.c and
|
||||
* sched_trace_internal.h.
|
||||
*
|
||||
* Comments, string literals and character literals are blanked before the
|
||||
* scan, so prose such as "never calls fopen" in a comment is not a hit.
|
||||
|
|
@ -650,15 +698,27 @@ static void test_trace_sources_do_no_stdio_or_file_io(void)
|
|||
{
|
||||
char* st = read_whole_file(XCE_SCHED_TRACE_C_PATH);
|
||||
char* sc = read_whole_file(XCE_SCHED_TRACE_CAPTURE_C_PATH);
|
||||
char* ss = read_whole_file(XCE_SCHED_TRACE_SCAN_C_PATH);
|
||||
char* so = read_whole_file(XCE_SCHED_TRACE_OUTCOME_C_PATH);
|
||||
char* si = read_whole_file(XCE_SCHED_TRACE_INTERNAL_H_PATH);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(st);
|
||||
TEST_ASSERT_NOT_NULL(sc);
|
||||
TEST_ASSERT_NOT_NULL(ss);
|
||||
TEST_ASSERT_NOT_NULL(so);
|
||||
TEST_ASSERT_NOT_NULL(si);
|
||||
|
||||
assert_trace_source_has_no_io(st, "sched_trace.c");
|
||||
assert_trace_source_has_no_io(sc, "sched_trace_capture.c");
|
||||
assert_trace_source_has_no_io(ss, "sched_trace_scan.c");
|
||||
assert_trace_source_has_no_io(so, "sched_trace_outcome.c");
|
||||
assert_trace_source_has_no_io(si, "sched_trace_internal.h");
|
||||
|
||||
free(st);
|
||||
free(sc);
|
||||
free(ss);
|
||||
free(so);
|
||||
free(si);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
void kill_sched_job(void* arg) { (void)arg; }
|
||||
void kill_sched_queue(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugin.c"
|
||||
|
||||
void kill_sched_job(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
void kill_sched_job(void* arg) { (void)arg; }
|
||||
void kill_sched_queue(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@
|
|||
#include "../src/host_iter.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
/* Link-satisfying stubs -- identical set to test_resource_accounting.c's
|
||||
* own (see that file's header comment for the full rationale). None of
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugin.c"
|
||||
#include "../src/recording.c"
|
||||
#include "../src/recording_events.c"
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
void plugin_unregister(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -87,7 +87,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
|
||||
void kill_sched_job(void* arg) { (void)arg; }
|
||||
void kill_sched_queue(void* arg) { (void)arg; }
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@
|
|||
#include "../src/alloc_runner.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/seqjob_allocator.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -95,7 +95,9 @@
|
|||
#include "../src/alloc_runner.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/seqjob_allocator.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -208,7 +208,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
|
|
|
|||
|
|
@ -202,7 +202,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
|
|
|
|||
|
|
@ -191,7 +191,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugin.c"
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
#include "../src/recording.c"
|
||||
|
|
|
|||
|
|
@ -139,7 +139,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sim_loop.c calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
|
|
|
|||
|
|
@ -114,7 +114,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sim.c"
|
||||
#include "../src/sim_loop.c"
|
||||
#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */
|
||||
|
|
|
|||
|
|
@ -136,7 +136,9 @@
|
|||
#include "../src/driver.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
#include "../src/recording.c"
|
||||
#include "../src/recording_events.c"
|
||||
|
|
|
|||
|
|
@ -101,7 +101,9 @@
|
|||
#include "../src/resource.c"
|
||||
/* Hooked sources compile SCHED_TRACE bodies (scheduler-sim-decision-trace). */
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/snapshot.c"
|
||||
#include "../src/snapshot_load.c"
|
||||
#include "../src/snapshot_wire.c"
|
||||
|
|
|
|||
|
|
@ -40,8 +40,9 @@
|
|||
* - 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.
|
||||
* Links the trace model (src/sched_trace.c, sched_trace_scan.c) and the
|
||||
* capture sources (src/sched_trace_capture.c, sched_trace_outcome.c), plus
|
||||
* libxce for the pend-reason, resmgr and container helpers.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@
|
|||
* tallying hits per limit (5.9, 5.10);
|
||||
* - consecutive released-host calls merge into one rollback entry (5.8).
|
||||
*
|
||||
* Links only src/sched_trace.c (plus libxce for the jobid helpers).
|
||||
* Links only the model, src/sched_trace.c and src/sched_trace_scan.c (plus
|
||||
* libxce for the jobid helpers).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue