/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: GPL-3.0-or-later */ #include "model.h" #include #include "common.h" #include "version.h" #include "emu.h" #include "emu_args.h" #include "model_evspec.h" #include "ev_spec.h" #include "thread.h" #include "proc.h" void model_init(struct model *model) { memset(model, 0, sizeof(struct model)); } int model_register(struct model *model, struct model_spec *spec) { int i = spec->model; if (spec->name == NULL) { err("model %c missing name", i); return -1; } if (model->registered[i]) { err("model %c already registered", i); return -1; } if (spec->version == NULL) { err("model %s missing version", spec->name); return -1; } if (spec->evlist == NULL) { err("model %s missing evlist", spec->name); return -1; } if (spec->evspec != NULL) { err("model %s evspec must be NULL", spec->name); return -1; } spec->evspec = calloc(1, sizeof(struct model_evspec)); if (spec->evspec == NULL) { err("calloc failed:"); return -1; } if (model_evspec_init(spec->evspec, spec) != 0) { err("model_evspec_init failed for model %s", spec->name); return -1; } model->spec[i] = spec; model->registered[i] = 1; dbg("registered model %c", (char) i); return 0; } int model_probe(struct model *model, struct emu *emu) { int nenabled = 0; for (int i = 0; i < MAX_MODELS; i++) { if (!model->registered[i]) continue; struct model_spec *spec = model->spec[i]; if (spec->probe == NULL) continue; int ret = spec->probe(emu); if (ret < 0) { err("probe failed for model '%c'", (char) i); return -1; } /* Zero is disabled */ if (ret > 0 || emu->args.enable_all_models) { model->enabled[i] = 1; nenabled++; } } if (nenabled == 0) { warn("no models enabled"); } else { if (emu->args.enable_all_models) info("all %d models are enabled (-a): ", nenabled); else info("the following %d models are enabled: ", nenabled); for (int i = 0; i < MAX_MODELS; i++) { if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; long nevents = spec->evspec->nevents; info(" %8s %s '%c' (%ld events)", spec->name, spec->version, (char) i, nevents); } } return 0; } int model_create(struct model *model, struct emu *emu) { for (int i = 0; i < MAX_MODELS; i++) { if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; if (spec->create == NULL) continue; if (spec->create(emu) != 0) { err("create failed for model '%c'", (char) i); return -1; } } return 0; } int model_connect(struct model *model, struct emu *emu) { for (int i = 0; i < MAX_MODELS; i++) { if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; if (spec->connect == NULL) continue; if (spec->connect(emu) != 0) { err("connect failed for model '%c'", (char) i); return -1; } dbg("connect for model %c ok", (char) i); } return 0; } int model_event(struct model *model, struct emu *emu, int index) { struct model_spec *spec = model->spec[index]; if (!model->registered[index]) { err("model not registered for event %s", emu->ev->mcv); return -1; } if (!model->enabled[index]) { err("model %s not enabled for event %s", spec->name, emu->ev->mcv); info("missing call to ovni_thread_require(\"%s\", \"%s\")?", spec->name, spec->version); return -1; } if (spec->event == NULL) return 0; if (spec->event(emu) != 0) { err("event() failed for '%s' model", spec->name); return -1; } return 0; } int model_event_print(struct model *model, struct emu_ev *ev, char *buf, int buflen) { int index = ev->m; if (!model->registered[index]) { err("no model registered for %c", ev->m); return -1; } struct model_spec *spec = model->spec[index]; struct ev_spec *es = model_evspec_find(spec->evspec, ev->mcv); if (es == NULL) { err("cannot find event definition for %s", ev->mcv); return -1; } if (ev_spec_print(es, ev, buf, buflen) < 0) { err("cannot print event signature for %s", ev->mcv); return -1; } return 0; } int model_finish(struct model *model, struct emu *emu) { int ret = 0; for (int i = 0; i < MAX_MODELS; i++) { if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; if (spec->finish == NULL) continue; if (spec->finish(emu) != 0) { err("finish failed for model '%c'", (char) i); ret = -1; } } return ret; } static int should_enable(int have[3], struct model_spec *spec, struct thread *t) { static int compat = 0; /* Enable all models if we are in compatibility model. Don't check other * threads metadata */ if (compat) return 1; if (t->meta == NULL) { err("missing metadata for thread %s", t->id); return -1; } JSON_Object *require = json_object_dotget_object(t->meta, "ovni.require"); if (require == NULL) { warn("missing 'ovni.require' key in thread %s", t->id); warn("loading trace in compatibility mode"); warn("all models will be enabled (expect slowdown)"); warn("use ovni_thread_require() to enable only required models"); compat = 1; return 1; } /* May not have the current model */ const char *req_version = json_object_get_string(require, spec->name); if (req_version == NULL) return 0; int want[3]; if (version_parse(req_version, want) != 0) { err("cannot parse version %s from thread %s", req_version, t->id); return -1; } if (!version_is_compatible(want, have)) { err("unsupported %s model version (want %s, have %s) in %s", spec->name, req_version, spec->version, t->id); return -1; } /* Compatible */ return 1; } int model_version_probe(struct model_spec *spec, struct emu *emu) { int enable = 0; int have[3]; if (version_parse(spec->version, have) != 0) { err("cannot parse version %s", spec->version); return -1; } char path[128]; if (snprintf(path, 128, "%s.version", spec->name) >= 128) die("model %s name too long", spec->name); /* Find a stream with an model name key */ struct system *sys = &emu->system; for (struct thread *t = sys->threads; t; t = t->gnext) { int ret = should_enable(have, spec, t); if (ret < 0) { err("cannot determine if model %s must be enabled", spec->name); return -1; } else if (ret != 0) { enable = 1; /* Check the rest of threads */ } } if (enable) { dbg("model %s enabled", spec->name); } else { dbg("model %s disabled", spec->name); } return enable; }