diff --git a/src/emu/emu_system.c b/src/emu/emu_system.c new file mode 100644 index 0000000..8d78610 --- /dev/null +++ b/src/emu/emu_system.c @@ -0,0 +1,315 @@ +/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "emu_system.h" +#include "utlist.h" + +static int +has_prefix(const char *path, const char *prefix) +{ + if (strncmp(path, prefix, strlen(prefix)) != 0) + return 0; + + return 1; +} + +static struct emu_thread * +new_thread(struct emu_proc *proc, const char *name, const char *relpath) +{ + struct emu_thread *thread = calloc(1, sizeof(struct emu_thread)); + + if (thread == NULL) + die("calloc failed\n"); + + if (snprintf(thread->name, PATH_MAX, "%s", name) >= PATH_MAX) + die("new_thread: name too long: %s\n", name); + + if (snprintf(thread->relpath, PATH_MAX, "%s", relpath) >= PATH_MAX) + die("new_proc: relative path too long: %s\n", relpath); + + thread->proc = proc; + + err("new thread '%s'\n", thread->name); + + return thread; +} + +static struct emu_thread * +find_thread(struct emu_proc *proc, const char *name) +{ + for (struct emu_thread *t = proc->threads; t; t = t->lnext) { + if (strcmp(t->name, name) == 0) + return t; + } + return NULL; +} + +static int +create_thread(struct emu_proc *proc, const char *relpath) +{ + char name[PATH_MAX]; + if (snprintf(name, PATH_MAX, "%s", relpath) >= PATH_MAX) { + err("create_thread: path too long: %s\n", relpath); + return -1; + } + + if (strtok(name, "/") == NULL) { + err("missing first slash\n"); + return -1; + } + + if (strtok(NULL, "/") == NULL) { + err("missing second slash\n"); + return -1; + } + + char *threadname = strtok(NULL, "/"); + if (threadname == NULL) { + err("missing thread name\n"); + return -1; + } + + if (!has_prefix(threadname, "thread")) { + err("warning: ignoring unknown thread stream %s\n", + relpath); + return 0; + } + + struct emu_thread *thread = find_thread(proc, threadname); + + if (thread == NULL) { + thread = new_thread(proc, threadname, relpath); + DL_APPEND2(proc->threads, thread, lprev, lnext); + proc->nthreads++; + } + + return 0; +} + +static struct emu_proc * +new_proc(struct emu_loom *loom, const char *name, const char *relpath) +{ + struct emu_proc *proc = calloc(1, sizeof(struct emu_proc)); + + if (proc == NULL) + die("calloc failed\n"); + + if (snprintf(proc->name, PATH_MAX, "%s", name) >= PATH_MAX) + die("new_proc: name too long: %s\n", name); + + if (snprintf(proc->relpath, PATH_MAX, "%s", relpath) >= PATH_MAX) + die("new_proc: relative path too long: %s\n", relpath); + + proc->loom = loom; + + err("new proc '%s'\n", proc->name); + + return proc; +} + +static struct emu_proc * +find_proc(struct emu_loom *loom, const char *name) +{ + for (struct emu_proc *proc = loom->procs; proc; proc = proc->lnext) { + if (strcmp(proc->name, name) == 0) + return proc; + } + return NULL; +} + +static int +create_proc(struct emu_loom *loom, const char *relpath) +{ + char name[PATH_MAX]; + if (snprintf(name, PATH_MAX, "%s", relpath) >= PATH_MAX) { + err("create_proc: path too long: %s\n", relpath); + return -1; + } + + if (strtok(name, "/") == NULL) { + err("missing first slash\n"); + return -1; + } + + char *procname = strtok(NULL, "/"); + if (procname == NULL) { + err("missing proc name\n"); + return -1; + } + + if (!has_prefix(procname, "proc")) { + err("warning: ignoring unknown proc stream %s\n", + relpath); + return 0; + } + + struct emu_proc *proc = find_proc(loom, procname); + + if (proc == NULL) { + proc = new_proc(loom, procname, relpath); + DL_APPEND2(loom->procs, proc, lprev, lnext); + loom->nprocs++; + } + + return create_thread(proc, relpath); +} + +static struct emu_loom * +find_loom(struct emu_system *sys, const char *name) +{ + for (struct emu_loom *loom = sys->looms; loom; loom = loom->next) { + if (strcmp(loom->name, name) == 0) + return loom; + } + return NULL; +} + +static struct emu_loom * +new_loom(const char *name, const char *relpath) +{ + struct emu_loom *loom = calloc(1, sizeof(struct emu_loom)); + + if (loom == NULL) + die("calloc failed\n"); + + if (snprintf(loom->name, PATH_MAX, "%s", name) >= PATH_MAX) + die("new_loom: name too long: %s\n", name); + + if (snprintf(loom->relpath, PATH_MAX, "%s", relpath) >= PATH_MAX) + die("new_loom: relative path too long: %s\n", relpath); + + err("new loom '%s'\n", loom->name); + + return loom; +} + +static int +create_loom(struct emu_system *sys, const char *relpath) +{ + char name[PATH_MAX]; + if (snprintf(name, PATH_MAX, "%s", relpath) >= PATH_MAX) { + err("create_loom: path too long: %s\n", relpath); + return -1; + } + + if (strtok(name, "/") == NULL) { + err("create_looms: cannot find first '/': %s\n", + relpath); + return -1; + } + + struct emu_loom *loom = find_loom(sys, name); + + if (loom == NULL) { + loom = new_loom(name, relpath); + DL_APPEND(sys->looms, loom); + sys->nlooms++; + } + + return create_proc(loom, relpath); +} + +static int +create_system(struct emu_system *sys, struct emu_trace *trace) +{ + for (struct emu_stream *s = trace->streams; s ; s = s->next) { + if (!has_prefix(s->relpath, "loom")) { + err("warning: ignoring unknown steam %s\n", + s->relpath); + continue; + } + + if (create_loom(sys, s->relpath) != 0) { + err("create loom failed\n"); + return -1; + } + } + + return 0; +} + +static int +cmp_thread(struct emu_thread *a, struct emu_thread *b) +{ + return strcmp(a->name, b->name); +} + +static void +sort_proc(struct emu_proc *proc) +{ + DL_SORT2(proc->threads, cmp_thread, lprev, lnext); +} + +static int +cmp_proc(struct emu_proc *a, struct emu_proc *b) +{ + return strcmp(a->name, b->name); +} + +static void +sort_loom(struct emu_loom *loom) +{ + DL_SORT2(loom->procs, cmp_proc, lprev, lnext); + + for (struct emu_proc *p = loom->procs; p; p = p->gnext) + sort_proc(p); +} + +static int +cmp_loom(struct emu_loom *a, struct emu_loom *b) +{ + return strcmp(a->name, b->name); +} + +static void +sort_system(struct emu_system *sys) +{ + DL_SORT(sys->looms, cmp_loom); + + for (struct emu_loom *l = sys->looms; l; l = l->next) + sort_loom(l); +} + +static void +populate_global_lists(struct emu_system *sys) +{ + for (struct emu_loom *l = sys->looms; l; l = l->next) { + for (struct emu_proc *p = l->procs; p; p = p->lnext) { + for (struct emu_thread *t = p->threads; t; t = t->lnext) { + DL_APPEND2(sys->threads, t, gprev, gnext); + } + DL_APPEND2(sys->procs, p, gprev, gnext); + } + /* Looms are already in emu_system */ + } +} + +static void +print_system(struct emu_system *sys) +{ + err("content of system:\n"); + for (struct emu_loom *l = sys->looms; l; l = l->next) { + err("%s\n", l->name); + for (struct emu_proc *p = l->procs; p; p = p->lnext) { + err(" %s\n", p->name); + for (struct emu_thread *t = p->threads; t; t = t->lnext) { + err(" %s\n", t->name); + } + } + } +} + +int +emu_system_load(struct emu_system *sys, struct emu_trace *trace) +{ + if (create_system(sys, trace) != 0) { + err("emu_system_load: create system failed\n"); + return -1; + } + + sort_system(sys); + populate_global_lists(sys); + print_system(sys); + + return 0; +} diff --git a/src/emu/emu_system.h b/src/emu/emu_system.h new file mode 100644 index 0000000..11dad2a --- /dev/null +++ b/src/emu/emu_system.h @@ -0,0 +1,189 @@ +/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: GPL-3.0-or-later */ + +#ifndef EMU_SYSTEM_H +#define EMU_SYSTEM_H + +#include "emu_trace.h" +#include "parson.h" +#include "ovni.h" +#include + +#define MAX_CPU_NAME 32 + +struct emu_cpu; +struct emu_thread; +struct emu_proc; +struct emu_loom; +struct emu_system; + +enum emu_cpu_state { + CPU_ST_UNKNOWN, + CPU_ST_READY, +}; + +#define MAX_MODELS 256 + +struct model_ctx { + void *data[MAX_MODELS]; +}; + +struct emu_cpu { + /* Logical index: 0 to ncpus - 1 */ + int i; + + /* Physical id: as reported by lscpu(1) */ + int phyid; + + /* Global index for all CPUs */ + int gindex; + + enum emu_cpu_state state; + + /* The loom of the CPU */ + struct emu_loom *loom; + + size_t nthreads; + struct emu_thread *thread; /* List of threads assigned to this CPU */ + + size_t nrunning_threads; + struct emu_thread *th_running; /* One */ + + size_t nactive_threads; + struct emu_thread *th_active; + + /* Is this a virtual CPU? */ + int virtual; + + /* Global list */ + struct emu_cpu *next; + struct emu_cpu *prev; + + struct model_ctx ctx; +}; + +/* Emulated thread runtime status */ +enum emu_thread_state { + TH_ST_UNKNOWN, + TH_ST_RUNNING, + TH_ST_PAUSED, + TH_ST_DEAD, + TH_ST_COOLING, + TH_ST_WARMING, +}; + +struct emu_thread { + char name[PATH_MAX]; + char relpath[PATH_MAX]; + + int tid; + int index; /* In loom */ + int gindex; /* In emu */ + + /* The process associated with this thread */ + struct emu_proc *proc; + + enum emu_thread_state state; + int is_running; + int is_active; + + /* Current cpu, NULL if not unique affinity */ + struct emu_cpu *cpu; + + /* Linked list of threads in each CPU */ + struct emu_thread *cpu_prev; + struct emu_thread *cpu_next; + + /* Local list */ + struct emu_thread *lprev; + struct emu_thread *lnext; + + /* Global list */ + struct emu_thread *gnext; + struct emu_thread *gprev; + + struct model_ctx ctx; +}; + +/* State of each emulated process */ +struct emu_proc { + char name[PATH_MAX]; /* Proc directory name */ + char relpath[PATH_MAX]; + + int pid; + int index; + int gindex; + int appid; + int rank; + + struct emu_loom *loom; + + JSON_Value *meta; + + int nthreads; + struct emu_thread *threads; + + /* Local list */ + struct emu_proc *lnext; + struct emu_proc *lprev; + + /* Global list */ + struct emu_proc *gnext; + struct emu_proc *gprev; + + struct model_ctx ctx; +}; + +struct emu_loom { + char hostname[OVNI_MAX_HOSTNAME]; + char name[PATH_MAX]; /* Loom directory name */ + char relpath[PATH_MAX]; /* Relative to tracedir */ + + size_t max_ncpus; + size_t max_phyid; + size_t ncpus; + size_t offset_ncpus; + struct emu_cpu *cpu; + int rank_enabled; + + int64_t clock_offset; + + /* Virtual CPU */ + struct emu_cpu vcpu; + + /* Local list */ + size_t nprocs; + struct emu_proc *procs; + + /* Global list */ + struct emu_loom *next; + struct emu_loom *prev; + + struct model_ctx ctx; +}; + +struct emu_system { + size_t nlooms; + size_t nthreads; + size_t nprocs; + size_t ncpus; /* Physical */ + + struct emu_loom *looms; + struct emu_proc *procs; + struct emu_thread *threads; + struct emu_cpu *cpus; + + /* From current event */ + struct emu_loom *cur_loom; + struct emu_proc *cur_proc; + struct emu_thread *cur_thread; + + struct model_ctx ctx; +}; + +int emu_system_load(struct emu_system *system, struct emu_trace *trace); + +int model_ctx_set(struct model_ctx *ctx, int model, void *data); +int model_ctx_get(struct model_ctx *ctx, int model, void *data); + +#endif /* EMU_SYSTEM_H */