From 4e2164646c4ea5b97908b8ed7d80e601f9ea010f Mon Sep 17 00:00:00 2001 From: Rodrigo Arias Date: Wed, 1 Feb 2023 18:18:58 +0100 Subject: [PATCH] Add PCF information for nanos6 --- src/emu/CMakeLists.txt | 2 + src/emu/emu.c | 10 + src/emu/emu.h | 1 + src/emu/nanos6/connect.c | 150 ++--------- src/emu/nanos6/nanos6_priv.h | 12 +- src/emu/nanos6/pvt.c | 242 ++++++++++++++++++ src/emu/ovniemu.c | 19 +- src/emu/pcf.c | 473 ++++++++++++++++------------------- src/emu/pcf.h | 19 +- src/emu/prv.c | 3 +- src/emu/prv.h | 2 +- src/emu/pvt.c | 33 +++ src/emu/pvt.h | 1 + src/emu/recorder.c | 13 + src/emu/recorder.h | 1 + 15 files changed, 570 insertions(+), 411 deletions(-) create mode 100644 src/emu/nanos6/pvt.c diff --git a/src/emu/CMakeLists.txt b/src/emu/CMakeLists.txt index 39e5694..b18de1a 100644 --- a/src/emu/CMakeLists.txt +++ b/src/emu/CMakeLists.txt @@ -27,6 +27,7 @@ add_library(emu STATIC mux.c path.c proc.c + pcf.c prv.c pvt.c recorder.c @@ -41,6 +42,7 @@ add_library(emu STATIC nanos6/connect.c nanos6/create.c nanos6/event.c + nanos6/pvt.c ) add_executable(ovniemu ovniemu.c) diff --git a/src/emu/emu.c b/src/emu/emu.c index 5fbc6f5..4498b27 100644 --- a/src/emu/emu.c +++ b/src/emu/emu.c @@ -156,3 +156,13 @@ emu_step(struct emu *emu) return 0; } + +int +emu_finish(struct emu *emu) +{ + if (recorder_finish(&emu->recorder) != 0) { + err("recorder_finish failed"); + return -1; + } + return 0; +} diff --git a/src/emu/emu.h b/src/emu/emu.h index 5eade0b..27eb18a 100644 --- a/src/emu/emu.h +++ b/src/emu/emu.h @@ -39,6 +39,7 @@ struct emu { int emu_init(struct emu *emu, int argc, char *argv[]); int emu_connect(struct emu *emu); int emu_step(struct emu *emu); +int emu_finish(struct emu *emu); static inline struct emu * emu_get(void *ptr) diff --git a/src/emu/nanos6/connect.c b/src/emu/nanos6/connect.c index 37867b2..4c81495 100644 --- a/src/emu/nanos6/connect.c +++ b/src/emu/nanos6/connect.c @@ -1,32 +1,14 @@ #include "nanos6_priv.h" -const enum nanos6_track th_track[] = { - [CH_TASKID] = RUN_TH, - [CH_TYPE] = RUN_TH, - [CH_SUBSYSTEM] = ACT_TH, - [CH_RANK] = RUN_TH, - [CH_THREAD] = NONE, +const enum nanos6_track chan_track[CH_MAX][CT_MAX] = { + /* Thread CPU */ + [CH_TASKID] = { RUN_TH, RUN_TH }, + [CH_TYPE] = { RUN_TH, RUN_TH }, + [CH_SUBSYSTEM] = { ACT_TH, RUN_TH }, + [CH_RANK] = { RUN_TH, RUN_TH }, + [CH_THREAD] = { NONE, RUN_TH }, }; -const enum nanos6_track cpu_track[] = { - [CH_TASKID] = RUN_TH, - [CH_TYPE] = RUN_TH, - [CH_SUBSYSTEM] = RUN_TH, - [CH_RANK] = RUN_TH, - [CH_THREAD] = RUN_TH, -}; - -static const int th_type[] = { - [CH_TASKID] = 35, - [CH_TYPE] = 36, - [CH_SUBSYSTEM] = 37, - [CH_RANK] = 38, - [CH_THREAD] = 39, -}; - -static const int *cpu_type = th_type; - - static int connect_thread_mux(struct emu *emu, struct thread *thread) { @@ -67,9 +49,10 @@ connect_thread_mux(struct emu *emu, struct thread *thread) /* The tracking only sets the ch_out, but we keep both tracking * updated as the CPU tracking channels may use them. */ - if (th_track[i] == RUN_TH) + enum nanos6_track track = chan_track[i][CT_TH]; + if (track == RUN_TH) th->ch_out[i] = &th->ch_run[i]; - else if (th_track[i] == ACT_TH) + else if (track == ACT_TH) th->ch_out[i] = &th->ch_act[i]; else th->ch_out[i] = &th->ch[i]; @@ -79,23 +62,6 @@ connect_thread_mux(struct emu *emu, struct thread *thread) return 0; } -static int -connect_thread_prv(struct emu *emu, struct thread *thread, struct prv *prv) -{ - struct nanos6_thread *th = EXT(thread, '6'); - for (int i = 0; i < CH_MAX; i++) { - struct chan *out = th->ch_out[i]; - long type = th_type[i]; - long row = thread->gindex; - if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { - err("prv_register failed"); - return -1; - } - } - - return 0; -} - static int add_inputs_cpu_mux(struct emu *emu, struct mux *mux, int i) { @@ -104,12 +70,13 @@ add_inputs_cpu_mux(struct emu *emu, struct mux *mux, int i) /* Choose input thread channel based on tracking mode */ struct chan *inp = NULL; - if (cpu_track[i] == RUN_TH) + enum nanos6_track track = chan_track[i][CT_CPU]; + if (track == RUN_TH) inp = &th->ch_run[i]; - else if (cpu_track[i] == ACT_TH) + else if (track == ACT_TH) inp = &th->ch_act[i]; else - die("cpu tracking must be 'running' or 'active'"); + die("cpu tracking must be running or active"); if (mux_add_input(mux, value_int64(t->gindex), inp) != 0) { err("mux_add_input failed"); @@ -130,12 +97,13 @@ connect_cpu_mux(struct emu *emu, struct cpu *scpu) /* Choose select CPU channel based on tracking mode */ struct chan *sel = NULL; - if (cpu_track[i] == RUN_TH) + enum nanos6_track track = chan_track[i][CT_CPU]; + if (track == RUN_TH) sel = &scpu->chan[CPU_CHAN_THRUN]; - else if (cpu_track[i] == ACT_TH) + else if (track == ACT_TH) sel = &scpu->chan[CPU_CHAN_THACT]; else - die("cpu tracking must be 'running' or 'active'"); + die("cpu tracking must be running or active"); if (mux_init(mux, &emu->bay, sel, out, NULL) != 0) { err("mux_init failed"); @@ -151,8 +119,8 @@ connect_cpu_mux(struct emu *emu, struct cpu *scpu) return 0; } -static int -connect_threads(struct emu *emu) +int +nanos6_connect(struct emu *emu) { struct system *sys = &emu->system; @@ -164,51 +132,6 @@ connect_threads(struct emu *emu) } } - /* Get thread PRV */ - struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread"); - if (pvt == NULL) { - err("cannot find thread pvt"); - return -1; - } - struct prv *prv = pvt_get_prv(pvt); - - for (struct thread *t = sys->threads; t; t = t->gnext) { - if (connect_thread_prv(emu, t, prv) != 0) { - err("connect_thread_prv failed"); - return -1; - } - } - - return 0; -} - -static int -connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv) -{ - struct nanos6_cpu *cpu = EXT(scpu, '6'); - for (int i = 0; i < CH_MAX; i++) { - struct chan *out = &cpu->ch[i]; - long type = cpu_type[i]; - long row = scpu->gindex; - if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { - err("prv_register failed"); - return -1; - } - } - - return 0; -} - -//static int -//populate_cpu_pcf(struct emu *emu, struct pcf *pcf) -//{ -//} - -static int -connect_cpus(struct emu *emu) -{ - struct system *sys = &emu->system; - /* cpus */ for (struct cpu *c = sys->cpus; c; c = c->next) { if (connect_cpu_mux(emu, c) != 0) { @@ -217,37 +140,8 @@ connect_cpus(struct emu *emu) } } - /* Get cpu PRV */ - struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); - if (pvt == NULL) { - err("cannot find cpu pvt"); - return -1; - } - struct prv *prv = pvt_get_prv(pvt); - - for (struct cpu *c = sys->cpus; c; c = c->next) { - if (connect_cpu_prv(emu, c, prv) != 0) { - err("connect_cpu_prv failed"); - return -1; - } - } - -// struct pcf *pcf = pvt_get_pcf(pvt); -// populate_cpu_pcf(pcf, emu); - - return 0; -} - -int -nanos6_connect(struct emu *emu) -{ - if (connect_threads(emu) != 0) { - err("connect_threads failed"); - return -1; - } - - if (connect_cpus(emu) != 0) { - err("connect_cpus failed"); + if (init_pvt(emu) != 0) { + err("init_pvt failed"); return -1; } diff --git a/src/emu/nanos6/nanos6_priv.h b/src/emu/nanos6/nanos6_priv.h index 3a83bba..ddb39c2 100644 --- a/src/emu/nanos6/nanos6_priv.h +++ b/src/emu/nanos6/nanos6_priv.h @@ -10,7 +10,14 @@ #include "task.h" /* Private enums */ + enum nanos6_chan_type { + CT_TH = 0, + CT_CPU, + CT_MAX +}; + +enum nanos6_chan { CH_TASKID = 0, CH_TYPE, CH_SUBSYSTEM, @@ -26,8 +33,7 @@ enum nanos6_track { TRACK_MAX, }; -extern const enum nanos6_track th_track[CH_MAX]; -extern const enum nanos6_track cpu_track[CH_MAX]; +extern const enum nanos6_track chan_track[CH_MAX][CT_MAX]; enum nanos6_ss_state { ST_TASK_BODY = 1, @@ -93,4 +99,6 @@ int nanos6_create(struct emu *emu); int nanos6_connect(struct emu *emu); int nanos6_event(struct emu *emu); +int init_pvt(struct emu *emu); + #endif /* NANOS6_PRIV_H */ diff --git a/src/emu/nanos6/pvt.c b/src/emu/nanos6/pvt.c new file mode 100644 index 0000000..94efe83 --- /dev/null +++ b/src/emu/nanos6/pvt.c @@ -0,0 +1,242 @@ +#include "nanos6_priv.h" + +/* TODO: Assign types on runtime and generate configs */ +static const int pvt_type[] = { + [CH_TASKID] = 35, + [CH_TYPE] = 36, + [CH_SUBSYSTEM] = 37, + [CH_RANK] = 38, + [CH_THREAD] = 39, +}; + +static const char *pcf_prefix[CH_MAX] = { + [CH_TASKID] = "Nanos6 task ID", + [CH_TYPE] = "Nanos6 task type", + [CH_SUBSYSTEM] = "Nanos6 subsystem", + [CH_RANK] = "Nanos6 task MPI rank", + [CH_THREAD] = "Nanos6 thread type", +}; + +static const char *pcf_suffix[TRACK_MAX] = { + [NONE] = "", + [RUN_TH] = "of the RUNNING thread", + [ACT_TH] = "of the ACTIVE thread", +}; + +static const struct pcf_value_label nanos6_ss_values[] = { + { ST_TASK_BODY, "Task: Running body" }, + { ST_TASK_CREATING, "Task: Creating" }, + { ST_TASK_SUBMIT, "Task: Submitting" }, + { ST_TASK_SPAWNING, "Task: Spawning function" }, + { ST_TASK_FOR, "Task: Running task for" }, + { ST_SCHED_SERVING, "Scheduler: Serving tasks" }, + { ST_SCHED_ADDING, "Scheduler: Adding ready tasks" }, + { ST_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, + { ST_DEP_REG, "Dependency: Registering" }, + { ST_DEP_UNREG, "Dependency: Unregistering" }, + { ST_BLK_TASKWAIT, "Blocking: Taskwait" }, + { ST_BLK_BLOCKING, "Blocking: Blocking current task" }, + { ST_BLK_UNBLOCKING, "Blocking: Unblocking remote task" }, + { ST_BLK_WAITFOR, "Blocking: Wait for deadline" }, + { ST_HANDLING_TASK, "Worker: Handling task" }, + { ST_WORKER_LOOP, "Worker: Looking for work" }, + { ST_SWITCH_TO, "Worker: Switching to another thread" }, + { ST_MIGRATE, "Worker: Migrating CPU" }, + { ST_SUSPEND, "Worker: Suspending thread" }, + { ST_RESUME, "Worker: Resuming another thread" }, + { ST_ALLOCATING, "Memory: Allocating" }, + { ST_FREEING, "Memory: Freeing" }, + + { EV_SCHED_SEND, "EV Scheduler: Send task" }, + { EV_SCHED_RECV, "EV Scheduler: Recv task" }, + { EV_SCHED_SELF, "EV Scheduler: Self-assign task" }, + { EV_CPU_IDLE, "EV CPU: Becomes idle" }, + { EV_CPU_ACTIVE, "EV CPU: Becomes active" }, + { EV_SIGNAL, "EV Worker: Wakening another thread" }, + { -1, NULL }, +}; + +static const struct pcf_value_label nanos6_thread_type[] = { + { ST_TH_EXTERNAL, "External" }, + { ST_TH_WORKER, "Worker" }, + { ST_TH_LEADER, "Leader" }, + { ST_TH_MAIN, "Main" }, + { -1, NULL }, +}; + +static const struct pcf_value_label (*pcf_chan_value_labels[CH_MAX])[] = { + [CH_SUBSYSTEM] = &nanos6_ss_values, + [CH_THREAD] = &nanos6_thread_type, +}; + +/* ------------------------------ pcf ------------------------------ */ + +static int +create_values(struct pcf_type *t, int c) +{ + const struct pcf_value_label(*q)[] = pcf_chan_value_labels[c]; + + if (q == NULL) + return 0; + + for (const struct pcf_value_label *p = *q; p->label != NULL; p++) + pcf_add_value(t, p->value, p->label); + + return 0; +} + +static int +create_type(struct pcf *pcf, enum nanos6_chan c, enum nanos6_chan_type ct) +{ + long type = pvt_type[c]; + + if (type == -1) + return 0; + + /* Compute the label by joining the two parts */ + const char *prefix = pcf_prefix[c]; + int track_mode = chan_track[c][ct]; + const char *suffix = pcf_suffix[track_mode]; + + char label[MAX_PCF_LABEL]; + int ret = snprintf(label, MAX_PCF_LABEL, "%s %s", + prefix, suffix); + + if (ret >= MAX_PCF_LABEL) { + err("computed type label too long"); + return -1; + } + + struct pcf_type *pcftype = pcf_add_type(pcf, type, label); + + return create_values(pcftype, c); +} + +static int +init_pcf(struct pcf *pcf, enum nanos6_chan_type ct) +{ + /* Create default types and values */ + for (enum nanos6_chan c = 0; c < CH_MAX; c++) { + if (create_type(pcf, c, ct) != 0) { + err("create_type failed"); + return -1; + } + } + + return 0; +} + +/* ------------------------------ prv ------------------------------ */ + +static int +connect_thread_prv(struct emu *emu, struct thread *thread, struct prv *prv) +{ + struct nanos6_thread *th = EXT(thread, '6'); + for (int i = 0; i < CH_MAX; i++) { + struct chan *out = th->ch_out[i]; + long type = pvt_type[i]; + long row = thread->gindex; + if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { + err("prv_register failed"); + return -1; + } + } + + return 0; +} + +static int +connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv) +{ + struct nanos6_cpu *cpu = EXT(scpu, '6'); + for (int i = 0; i < CH_MAX; i++) { + struct chan *out = &cpu->ch[i]; + long type = pvt_type[i]; + long row = scpu->gindex; + if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { + err("prv_register failed"); + return -1; + } + } + + return 0; +} + +static int +connect_threads(struct emu *emu) +{ + struct system *sys = &emu->system; + + /* Get thread PRV */ + struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread"); + if (pvt == NULL) { + err("cannot find thread pvt"); + return -1; + } + + /* Connect thread channels to PRV */ + struct prv *prv = pvt_get_prv(pvt); + for (struct thread *t = sys->threads; t; t = t->gnext) { + if (connect_thread_prv(emu, t, prv) != 0) { + err("connect_thread_prv failed"); + return -1; + } + } + + /* Init thread PCF */ + struct pcf *pcf = pvt_get_pcf(pvt); + if (init_pcf(pcf, CT_TH) != 0) { + err("init_pcf failed"); + return -1; + } + + return 0; +} + +static int +connect_cpus(struct emu *emu) +{ + struct system *sys = &emu->system; + + /* Get cpu PRV */ + struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); + if (pvt == NULL) { + err("cannot find cpu pvt"); + return -1; + } + + /* Connect CPU channels to PRV */ + struct prv *prv = pvt_get_prv(pvt); + for (struct cpu *c = sys->cpus; c; c = c->next) { + if (connect_cpu_prv(emu, c, prv) != 0) { + err("connect_cpu_prv failed"); + return -1; + } + } + + /* Init CPU PCF */ + struct pcf *pcf = pvt_get_pcf(pvt); + if (init_pcf(pcf, CT_CPU) != 0) { + err("init_pcf failed"); + return -1; + } + + return 0; +} + +/* Connect all outputs to the paraver trace and setup PCF types */ +int +init_pvt(struct emu *emu) +{ + if (connect_threads(emu) != 0) { + err("connect_threads failed"); + return -1; + } + + if (connect_cpus(emu) != 0) { + err("connect_cpus failed"); + return -1; + } + + return 0; +} diff --git a/src/emu/ovniemu.c b/src/emu/ovniemu.c index 541ade8..cc33aaa 100644 --- a/src/emu/ovniemu.c +++ b/src/emu/ovniemu.c @@ -21,16 +21,25 @@ main(int argc, char *argv[]) if (emu_connect(emu) != 0) die("emu_connect failed\n"); - err("emulation starts\n"); + err("emulation starts"); int ret = 0; while ((ret = emu_step(emu)) == 0); - if (ret < 0) - die("emu_step failed\n"); + if (ret < 0) { + err("emu_step failed"); + ret = 1; + /* continue to close the trace files */ + err("emulation aborts!"); + } else { + err("emulation ends"); + } - err("emulation ends\n"); + if (emu_finish(emu) != 0) { + err("emu_finish failed"); + ret = 1; + } free(emu); - return 0; + return ret; } diff --git a/src/emu/pcf.c b/src/emu/pcf.c index 0d05d02..8b95c3e 100644 --- a/src/emu/pcf.c +++ b/src/emu/pcf.c @@ -2,9 +2,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ #include "pcf.h" -#include "emu.h" -#include "prv.h" +#include "common.h" #include #include #include @@ -69,210 +68,210 @@ const uint32_t pcf_def_palette[] = { const uint32_t *pcf_palette = pcf_def_palette; const int pcf_palette_len = ARRAY_LEN(pcf_def_palette); -/* ------------------ Value labels --------------------- */ - -struct pcf_value_label default_values[] = { - { ST_TOO_MANY_TH, "Unknown: Multiple threads running" }, - { -1, NULL }, -}; - -struct pcf_value_label ovni_state_values[] = { - { TH_ST_UNKNOWN, "Unknown" }, - { TH_ST_RUNNING, "Running" }, - { TH_ST_PAUSED, "Paused" }, - { TH_ST_DEAD, "Dead" }, - { TH_ST_COOLING, "Cooling" }, - { TH_ST_WARMING, "Warming" }, - { -1, NULL }, -}; - -struct pcf_value_label ovni_flush_values[] = { - { 0, "None" }, - { ST_OVNI_FLUSHING, "Flushing" }, - { ST_TOO_MANY_TH, "Unknown flushing state: Multiple threads running" }, - { -1, NULL }, -}; - -struct pcf_value_label nosv_ss_values[] = { - /* Errors */ - { ST_BAD, "Unknown: bad happened (report bug)" }, - { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, - /* Good values */ - { ST_NULL, "No subsystem" }, - { ST_NOSV_SCHED_HUNGRY, "Scheduler: Hungry" }, - { ST_NOSV_SCHED_SERVING, "Scheduler: Serving" }, - { ST_NOSV_SCHED_SUBMITTING, "Scheduler: Submitting" }, - { ST_NOSV_MEM_ALLOCATING, "Memory: Allocating" }, - { ST_NOSV_MEM_FREEING, "Memory: Freeing" }, - { ST_NOSV_TASK_RUNNING, "Task: Running" }, - { ST_NOSV_API_SUBMIT, "API: Submit" }, - { ST_NOSV_API_PAUSE, "API: Pause" }, - { ST_NOSV_API_YIELD, "API: Yield" }, - { ST_NOSV_API_WAITFOR, "API: Waitfor" }, - { ST_NOSV_API_SCHEDPOINT, "API: Scheduling point" }, - { ST_NOSV_ATTACH, "Thread: Attached" }, - { ST_NOSV_WORKER, "Thread: Worker" }, - { ST_NOSV_DELEGATE, "Thread: Delegate" }, - { EV_NOSV_SCHED_SEND, "EV Scheduler: Send task" }, - { EV_NOSV_SCHED_RECV, "EV Scheduler: Recv task" }, - { EV_NOSV_SCHED_SELF, "EV Scheduler: Self-assign task" }, - { -1, NULL }, -}; - -struct pcf_value_label nodes_mode_values[] = { - { ST_NULL, "NULL" }, - { ST_TOO_MANY_TH, "NODES: Multiple threads running" }, - { ST_NODES_REGISTER, "Dependencies: Registering task accesses" }, - { ST_NODES_UNREGISTER, "Dependencies: Unregistering task accesses" }, - { ST_NODES_IF0_WAIT, "If0: Waiting for an If0 task" }, - { ST_NODES_IF0_INLINE, "If0: Executing an If0 task inline" }, - { ST_NODES_TASKWAIT, "Taskwait: Taskwait" }, - { ST_NODES_CREATE, "Add Task: Creating a task" }, - { ST_NODES_SUBMIT, "Add Task: Submitting a task" }, - { ST_NODES_SPAWN, "Spawn Function: Spawning a function" }, - { -1, NULL }, -}; - -struct pcf_value_label kernel_cs_values[] = { - { ST_NULL, "NULL" }, - { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, - { ST_KERNEL_CSOUT, "Context switch: Out of the CPU" }, - { -1, NULL }, -}; - -struct pcf_value_label nanos6_ss_values[] = { - { ST_NULL, "No subsystem" }, - { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, - { ST_NANOS6_TASK_BODY, "Task: Running body" }, - { ST_NANOS6_TASK_CREATING, "Task: Creating" }, - { ST_NANOS6_TASK_SUBMIT, "Task: Submitting" }, - { ST_NANOS6_TASK_SPAWNING, "Task: Spawning function" }, - { ST_NANOS6_TASK_FOR, "Task: Running task for" }, - { ST_NANOS6_SCHED_SERVING, "Scheduler: Serving tasks" }, - { ST_NANOS6_SCHED_ADDING, "Scheduler: Adding ready tasks" }, - { ST_NANOS6_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, - { ST_NANOS6_DEP_REG, "Dependency: Registering" }, - { ST_NANOS6_DEP_UNREG, "Dependency: Unregistering" }, - { ST_NANOS6_BLK_TASKWAIT, "Blocking: Taskwait" }, - { ST_NANOS6_BLK_BLOCKING, "Blocking: Blocking current task" }, - { ST_NANOS6_BLK_UNBLOCKING, "Blocking: Unblocking remote task" }, - { ST_NANOS6_BLK_WAITFOR, "Blocking: Wait for deadline" }, - { ST_NANOS6_HANDLING_TASK, "Worker: Handling task" }, - { ST_NANOS6_WORKER_LOOP, "Worker: Looking for work" }, - { ST_NANOS6_SWITCH_TO, "Worker: Switching to another thread" }, - { ST_NANOS6_MIGRATE, "Worker: Migrating CPU" }, - { ST_NANOS6_SUSPEND, "Worker: Suspending thread" }, - { ST_NANOS6_RESUME, "Worker: Resuming another thread" }, - { ST_NANOS6_ALLOCATING, "Memory: Allocating" }, - { ST_NANOS6_FREEING, "Memory: Freeing" }, - { EV_NANOS6_SCHED_SEND, "EV Scheduler: Send task" }, - { EV_NANOS6_SCHED_RECV, "EV Scheduler: Recv task" }, - { EV_NANOS6_SCHED_SELF, "EV Scheduler: Self-assign task" }, - { EV_NANOS6_CPU_IDLE, "EV CPU: Becomes idle" }, - { EV_NANOS6_CPU_ACTIVE, "EV CPU: Becomes active" }, - { EV_NANOS6_SIGNAL, "EV Worker: Wakening another thread" }, - { -1, NULL }, -}; - -struct pcf_value_label nanos6_thread_type[] = { - { ST_NULL, "No type" }, - { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, - { ST_NANOS6_TH_EXTERNAL, "External" }, - { ST_NANOS6_TH_WORKER, "Worker" }, - { ST_NANOS6_TH_LEADER, "Leader" }, - { ST_NANOS6_TH_MAIN, "Main" }, - { -1, NULL }, -}; - -struct pcf_value_label (*pcf_chan_value_labels[CHAN_MAX])[] = { - [CHAN_OVNI_PID] = &default_values, - [CHAN_OVNI_TID] = &default_values, - [CHAN_OVNI_NRTHREADS] = &default_values, - [CHAN_OVNI_STATE] = &ovni_state_values, - [CHAN_OVNI_APPID] = &default_values, - [CHAN_OVNI_CPU] = &default_values, - [CHAN_OVNI_FLUSH] = &ovni_flush_values, - - [CHAN_NOSV_TASKID] = &default_values, - [CHAN_NOSV_TYPE] = &default_values, - [CHAN_NOSV_APPID] = &default_values, - [CHAN_NOSV_SUBSYSTEM] = &nosv_ss_values, - [CHAN_NOSV_RANK] = &default_values, - - [CHAN_NODES_SUBSYSTEM] = &nodes_mode_values, - - [CHAN_NANOS6_TASKID] = &default_values, - [CHAN_NANOS6_TYPE] = &default_values, - [CHAN_NANOS6_SUBSYSTEM] = &nanos6_ss_values, - [CHAN_NANOS6_RANK] = &default_values, - [CHAN_NANOS6_THREAD] = &nanos6_thread_type, - - [CHAN_KERNEL_CS] = &kernel_cs_values, -}; - -/* ------------------ Type labels --------------------- */ - -char *pcf_chan_name[CHAN_MAX] = { - [CHAN_OVNI_PID] = "PID", - [CHAN_OVNI_TID] = "TID", - [CHAN_OVNI_NRTHREADS] = "Number of RUNNING threads", - [CHAN_OVNI_STATE] = "Execution state", - [CHAN_OVNI_APPID] = "AppID", - [CHAN_OVNI_CPU] = "CPU affinity", - [CHAN_OVNI_FLUSH] = "Flushing state", - - [CHAN_NOSV_TASKID] = "nOS-V TaskID", - [CHAN_NOSV_TYPE] = "nOS-V task type", - [CHAN_NOSV_APPID] = "nOS-V task AppID", - [CHAN_NOSV_SUBSYSTEM] = "nOS-V subsystem", - [CHAN_NOSV_RANK] = "nOS-V task MPI rank", - - [CHAN_NODES_SUBSYSTEM] = "NODES subsystem", - - [CHAN_NANOS6_TASKID] = "Nanos6 task ID", - [CHAN_NANOS6_TYPE] = "Nanos6 task type", - [CHAN_NANOS6_SUBSYSTEM] = "Nanos6 subsystem", - [CHAN_NANOS6_RANK] = "Nanos6 task MPI rank", - [CHAN_NANOS6_THREAD] = "Nanos6 thread type", - - [CHAN_KERNEL_CS] = "Context switches", -}; - -enum pcf_suffix { NONE = 0, CUR_TH, RUN_TH, ACT_TH, SUFFIX_MAX }; - -char *pcf_suffix_name[SUFFIX_MAX] = { - [NONE] = "", - [CUR_TH] = "of the CURRENT thread", - [RUN_TH] = "of the RUNNING thread", - [ACT_TH] = "of the ACTIVE thread", -}; - -int pcf_chan_suffix[CHAN_MAX][CHAN_MAXTYPE] = { - /* Thread CPU */ - [CHAN_OVNI_PID] = { RUN_TH, RUN_TH }, - [CHAN_OVNI_TID] = { RUN_TH, RUN_TH }, - [CHAN_OVNI_NRTHREADS] = { NONE, NONE }, - [CHAN_OVNI_STATE] = { CUR_TH, NONE }, - [CHAN_OVNI_APPID] = { NONE, RUN_TH }, - [CHAN_OVNI_CPU] = { CUR_TH, NONE }, - [CHAN_OVNI_FLUSH] = { CUR_TH, RUN_TH }, - - [CHAN_NOSV_TASKID] = { RUN_TH, RUN_TH }, - [CHAN_NOSV_TYPE] = { RUN_TH, RUN_TH }, - [CHAN_NOSV_APPID] = { RUN_TH, RUN_TH }, - [CHAN_NOSV_SUBSYSTEM] = { ACT_TH, RUN_TH }, - [CHAN_NOSV_RANK] = { RUN_TH, RUN_TH }, - - [CHAN_NODES_SUBSYSTEM] = { RUN_TH, RUN_TH }, - - [CHAN_NANOS6_TASKID] = { RUN_TH, RUN_TH }, - [CHAN_NANOS6_TYPE] = { RUN_TH, RUN_TH }, - [CHAN_NANOS6_SUBSYSTEM] = { ACT_TH, RUN_TH }, - [CHAN_NANOS6_RANK] = { RUN_TH, RUN_TH }, - [CHAN_NANOS6_THREAD] = { RUN_TH, NONE }, - - [CHAN_KERNEL_CS] = { RUN_TH, ACT_TH }, -}; +///* ------------------ Value labels --------------------- */ +// +//struct pcf_value_label default_values[] = { +// { ST_TOO_MANY_TH, "Unknown: Multiple threads running" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label ovni_state_values[] = { +// { TH_ST_UNKNOWN, "Unknown" }, +// { TH_ST_RUNNING, "Running" }, +// { TH_ST_PAUSED, "Paused" }, +// { TH_ST_DEAD, "Dead" }, +// { TH_ST_COOLING, "Cooling" }, +// { TH_ST_WARMING, "Warming" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label ovni_flush_values[] = { +// { 0, "None" }, +// { ST_OVNI_FLUSHING, "Flushing" }, +// { ST_TOO_MANY_TH, "Unknown flushing state: Multiple threads running" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label nosv_ss_values[] = { +// /* Errors */ +// { ST_BAD, "Unknown: bad happened (report bug)" }, +// { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, +// /* Good values */ +// { ST_NULL, "No subsystem" }, +// { ST_NOSV_SCHED_HUNGRY, "Scheduler: Hungry" }, +// { ST_NOSV_SCHED_SERVING, "Scheduler: Serving" }, +// { ST_NOSV_SCHED_SUBMITTING, "Scheduler: Submitting" }, +// { ST_NOSV_MEM_ALLOCATING, "Memory: Allocating" }, +// { ST_NOSV_MEM_FREEING, "Memory: Freeing" }, +// { ST_NOSV_TASK_RUNNING, "Task: Running" }, +// { ST_NOSV_API_SUBMIT, "API: Submit" }, +// { ST_NOSV_API_PAUSE, "API: Pause" }, +// { ST_NOSV_API_YIELD, "API: Yield" }, +// { ST_NOSV_API_WAITFOR, "API: Waitfor" }, +// { ST_NOSV_API_SCHEDPOINT, "API: Scheduling point" }, +// { ST_NOSV_ATTACH, "Thread: Attached" }, +// { ST_NOSV_WORKER, "Thread: Worker" }, +// { ST_NOSV_DELEGATE, "Thread: Delegate" }, +// { EV_NOSV_SCHED_SEND, "EV Scheduler: Send task" }, +// { EV_NOSV_SCHED_RECV, "EV Scheduler: Recv task" }, +// { EV_NOSV_SCHED_SELF, "EV Scheduler: Self-assign task" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label nodes_mode_values[] = { +// { ST_NULL, "NULL" }, +// { ST_TOO_MANY_TH, "NODES: Multiple threads running" }, +// { ST_NODES_REGISTER, "Dependencies: Registering task accesses" }, +// { ST_NODES_UNREGISTER, "Dependencies: Unregistering task accesses" }, +// { ST_NODES_IF0_WAIT, "If0: Waiting for an If0 task" }, +// { ST_NODES_IF0_INLINE, "If0: Executing an If0 task inline" }, +// { ST_NODES_TASKWAIT, "Taskwait: Taskwait" }, +// { ST_NODES_CREATE, "Add Task: Creating a task" }, +// { ST_NODES_SUBMIT, "Add Task: Submitting a task" }, +// { ST_NODES_SPAWN, "Spawn Function: Spawning a function" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label kernel_cs_values[] = { +// { ST_NULL, "NULL" }, +// { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, +// { ST_KERNEL_CSOUT, "Context switch: Out of the CPU" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label nanos6_ss_values[] = { +// { ST_NULL, "No subsystem" }, +// { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, +// { ST_NANOS6_TASK_BODY, "Task: Running body" }, +// { ST_NANOS6_TASK_CREATING, "Task: Creating" }, +// { ST_NANOS6_TASK_SUBMIT, "Task: Submitting" }, +// { ST_NANOS6_TASK_SPAWNING, "Task: Spawning function" }, +// { ST_NANOS6_TASK_FOR, "Task: Running task for" }, +// { ST_NANOS6_SCHED_SERVING, "Scheduler: Serving tasks" }, +// { ST_NANOS6_SCHED_ADDING, "Scheduler: Adding ready tasks" }, +// { ST_NANOS6_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, +// { ST_NANOS6_DEP_REG, "Dependency: Registering" }, +// { ST_NANOS6_DEP_UNREG, "Dependency: Unregistering" }, +// { ST_NANOS6_BLK_TASKWAIT, "Blocking: Taskwait" }, +// { ST_NANOS6_BLK_BLOCKING, "Blocking: Blocking current task" }, +// { ST_NANOS6_BLK_UNBLOCKING, "Blocking: Unblocking remote task" }, +// { ST_NANOS6_BLK_WAITFOR, "Blocking: Wait for deadline" }, +// { ST_NANOS6_HANDLING_TASK, "Worker: Handling task" }, +// { ST_NANOS6_WORKER_LOOP, "Worker: Looking for work" }, +// { ST_NANOS6_SWITCH_TO, "Worker: Switching to another thread" }, +// { ST_NANOS6_MIGRATE, "Worker: Migrating CPU" }, +// { ST_NANOS6_SUSPEND, "Worker: Suspending thread" }, +// { ST_NANOS6_RESUME, "Worker: Resuming another thread" }, +// { ST_NANOS6_ALLOCATING, "Memory: Allocating" }, +// { ST_NANOS6_FREEING, "Memory: Freeing" }, +// { EV_NANOS6_SCHED_SEND, "EV Scheduler: Send task" }, +// { EV_NANOS6_SCHED_RECV, "EV Scheduler: Recv task" }, +// { EV_NANOS6_SCHED_SELF, "EV Scheduler: Self-assign task" }, +// { EV_NANOS6_CPU_IDLE, "EV CPU: Becomes idle" }, +// { EV_NANOS6_CPU_ACTIVE, "EV CPU: Becomes active" }, +// { EV_NANOS6_SIGNAL, "EV Worker: Wakening another thread" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label nanos6_thread_type[] = { +// { ST_NULL, "No type" }, +// { ST_TOO_MANY_TH, "Unknown: multiple threads running" }, +// { ST_NANOS6_TH_EXTERNAL, "External" }, +// { ST_NANOS6_TH_WORKER, "Worker" }, +// { ST_NANOS6_TH_LEADER, "Leader" }, +// { ST_NANOS6_TH_MAIN, "Main" }, +// { -1, NULL }, +//}; +// +//struct pcf_value_label (*pcf_chan_value_labels[CHAN_MAX])[] = { +// [CHAN_OVNI_PID] = &default_values, +// [CHAN_OVNI_TID] = &default_values, +// [CHAN_OVNI_NRTHREADS] = &default_values, +// [CHAN_OVNI_STATE] = &ovni_state_values, +// [CHAN_OVNI_APPID] = &default_values, +// [CHAN_OVNI_CPU] = &default_values, +// [CHAN_OVNI_FLUSH] = &ovni_flush_values, +// +// [CHAN_NOSV_TASKID] = &default_values, +// [CHAN_NOSV_TYPE] = &default_values, +// [CHAN_NOSV_APPID] = &default_values, +// [CHAN_NOSV_SUBSYSTEM] = &nosv_ss_values, +// [CHAN_NOSV_RANK] = &default_values, +// +// [CHAN_NODES_SUBSYSTEM] = &nodes_mode_values, +// +// [CHAN_NANOS6_TASKID] = &default_values, +// [CHAN_NANOS6_TYPE] = &default_values, +// [CHAN_NANOS6_SUBSYSTEM] = &nanos6_ss_values, +// [CHAN_NANOS6_RANK] = &default_values, +// [CHAN_NANOS6_THREAD] = &nanos6_thread_type, +// +// [CHAN_KERNEL_CS] = &kernel_cs_values, +//}; +// +///* ------------------ Type labels --------------------- */ +// +//char *pcf_chan_name[CHAN_MAX] = { +// [CHAN_OVNI_PID] = "PID", +// [CHAN_OVNI_TID] = "TID", +// [CHAN_OVNI_NRTHREADS] = "Number of RUNNING threads", +// [CHAN_OVNI_STATE] = "Execution state", +// [CHAN_OVNI_APPID] = "AppID", +// [CHAN_OVNI_CPU] = "CPU affinity", +// [CHAN_OVNI_FLUSH] = "Flushing state", +// +// [CHAN_NOSV_TASKID] = "nOS-V TaskID", +// [CHAN_NOSV_TYPE] = "nOS-V task type", +// [CHAN_NOSV_APPID] = "nOS-V task AppID", +// [CHAN_NOSV_SUBSYSTEM] = "nOS-V subsystem", +// [CHAN_NOSV_RANK] = "nOS-V task MPI rank", +// +// [CHAN_NODES_SUBSYSTEM] = "NODES subsystem", +// +// [CHAN_NANOS6_TASKID] = "Nanos6 task ID", +// [CHAN_NANOS6_TYPE] = "Nanos6 task type", +// [CHAN_NANOS6_SUBSYSTEM] = "Nanos6 subsystem", +// [CHAN_NANOS6_RANK] = "Nanos6 task MPI rank", +// [CHAN_NANOS6_THREAD] = "Nanos6 thread type", +// +// [CHAN_KERNEL_CS] = "Context switches", +//}; +// +//enum pcf_suffix { NONE = 0, CUR_TH, RUN_TH, ACT_TH, SUFFIX_MAX }; +// +//char *pcf_suffix_name[SUFFIX_MAX] = { +// [NONE] = "", +// [CUR_TH] = "of the CURRENT thread", +// [RUN_TH] = "of the RUNNING thread", +// [ACT_TH] = "of the ACTIVE thread", +//}; +// +//int pcf_chan_suffix[CHAN_MAX][CHAN_MAXTYPE] = { +// /* Thread CPU */ +// [CHAN_OVNI_PID] = { RUN_TH, RUN_TH }, +// [CHAN_OVNI_TID] = { RUN_TH, RUN_TH }, +// [CHAN_OVNI_NRTHREADS] = { NONE, NONE }, +// [CHAN_OVNI_STATE] = { CUR_TH, NONE }, +// [CHAN_OVNI_APPID] = { NONE, RUN_TH }, +// [CHAN_OVNI_CPU] = { CUR_TH, NONE }, +// [CHAN_OVNI_FLUSH] = { CUR_TH, RUN_TH }, +// +// [CHAN_NOSV_TASKID] = { RUN_TH, RUN_TH }, +// [CHAN_NOSV_TYPE] = { RUN_TH, RUN_TH }, +// [CHAN_NOSV_APPID] = { RUN_TH, RUN_TH }, +// [CHAN_NOSV_SUBSYSTEM] = { ACT_TH, RUN_TH }, +// [CHAN_NOSV_RANK] = { RUN_TH, RUN_TH }, +// +// [CHAN_NODES_SUBSYSTEM] = { RUN_TH, RUN_TH }, +// +// [CHAN_NANOS6_TASKID] = { RUN_TH, RUN_TH }, +// [CHAN_NANOS6_TYPE] = { RUN_TH, RUN_TH }, +// [CHAN_NANOS6_SUBSYSTEM] = { ACT_TH, RUN_TH }, +// [CHAN_NANOS6_RANK] = { RUN_TH, RUN_TH }, +// [CHAN_NANOS6_THREAD] = { RUN_TH, NONE }, +// +// [CHAN_KERNEL_CS] = { RUN_TH, ACT_TH }, +//}; /* clang-format on */ @@ -324,61 +323,20 @@ write_types(struct pcf *pcf) write_type(pcf->f, t); } -static void -create_values(struct pcf_type *t, enum chan c) -{ - struct pcf_value_label(*q)[] = pcf_chan_value_labels[c]; - - if (q == NULL) - return; - - for (struct pcf_value_label *p = *q; p->label != NULL; p++) - pcf_add_value(t, p->value, p->label); -} - -static void -create_type(struct pcf *pcf, enum chan c) -{ - enum chan_type ct = pcf->chantype; - int prv_type = chan_to_prvtype[c]; - - if (prv_type == -1) - return; - - /* Compute the label by joining the two parts */ - char *prefix = pcf_chan_name[c]; - int isuffix = pcf_chan_suffix[c][ct]; - char *suffix = pcf_suffix_name[isuffix]; - - char label[MAX_PCF_LABEL]; - int ret = snprintf(label, MAX_PCF_LABEL, "%s %s", - prefix, suffix); - - if (ret >= MAX_PCF_LABEL) - die("computed type label too long\n"); - - struct pcf_type *t = pcf_add_type(pcf, prv_type, label); - - create_values(t, c); -} - /** Open the given PCF file and create the default events. */ -void -pcf_open(struct pcf *pcf, char *path, int chantype) +int +pcf_open(struct pcf *pcf, char *path) { memset(pcf, 0, sizeof(*pcf)); pcf->f = fopen(path, "w"); - pcf->chantype = chantype; if (pcf->f == NULL) { - die("cannot open PCF file '%s': %s\n", - path, strerror(errno)); + err("cannot open PCF file '%s':", path); + return -1; } - /* Create default types and values */ - for (enum chan c = 0; c < CHAN_MAX; c++) - create_type(pcf, c); + return 0; } struct pcf_type * @@ -461,17 +419,14 @@ pcf_add_value(struct pcf_type *type, int value, const char *label) return pcfvalue; } -/** Writes the defined event and values to the PCF file. */ -void -pcf_write(struct pcf *pcf) +/** Writes the defined event and values to the PCF file and closes the file. */ +int +pcf_close(struct pcf *pcf) { write_header(pcf->f); write_colors(pcf->f, pcf_palette, pcf_palette_len); write_types(pcf); -} -void -pcf_close(struct pcf *pcf) -{ fclose(pcf->f); + return 0; } diff --git a/src/emu/pcf.h b/src/emu/pcf.h index ef7f7cf..c35c171 100644 --- a/src/emu/pcf.h +++ b/src/emu/pcf.h @@ -31,7 +31,6 @@ struct pcf_type { struct pcf { FILE *f; - int chantype; int pcf_ntypes; struct pcf_type *types; }; @@ -42,22 +41,12 @@ struct pcf_value_label { char *label; }; -extern struct pcf_value_label nanos6_ss_values[]; - -void pcf_open(struct pcf *pcf, char *path, int chantype); - -void pcf_write(struct pcf *pcf); - -void pcf_close(struct pcf *pcf); +int pcf_open(struct pcf *pcf, char *path); +int pcf_close(struct pcf *pcf); struct pcf_type *pcf_find_type(struct pcf *pcf, int type_id); - -struct pcf_type *pcf_add_type(struct pcf *pcf, int type_id, - const char *label); - -struct pcf_value *pcf_add_value(struct pcf_type *type, int value, - const char *label); - +struct pcf_type *pcf_add_type(struct pcf *pcf, int type_id, const char *label); +struct pcf_value *pcf_add_value(struct pcf_type *type, int value, const char *label); struct pcf_value *pcf_find_value(struct pcf_type *type, int value); #endif /* OVNI_PCF_H */ diff --git a/src/emu/prv.c b/src/emu/prv.c index 2da1d99..dfdbdf4 100644 --- a/src/emu/prv.c +++ b/src/emu/prv.c @@ -42,13 +42,14 @@ prv_open(struct prv *prv, long nrows, const char *path) return prv_open_file(prv, nrows, f); } -void +int prv_close(struct prv *prv) { /* Fix the header with the current duration */ fseek(prv->file, 0, SEEK_SET); write_header(prv->file, prv->time, prv->nrows); fclose(prv->file); + return 0; } static struct prv_chan * diff --git a/src/emu/prv.h b/src/emu/prv.h index 186e917..5154a11 100644 --- a/src/emu/prv.h +++ b/src/emu/prv.h @@ -37,6 +37,6 @@ int prv_open(struct prv *prv, long nrows, const char *path); int prv_open_file(struct prv *prv, long nrows, FILE *file); int prv_register(struct prv *prv, long row, long type, struct bay *bay, struct chan *chan, long flags); int prv_advance(struct prv *prv, int64_t time); -void prv_close(struct prv *prv); +int prv_close(struct prv *prv); #endif /* PRV_H */ diff --git a/src/emu/pvt.c b/src/emu/pvt.c index 2a9c622..4bdf08e 100644 --- a/src/emu/pvt.c +++ b/src/emu/pvt.c @@ -26,6 +26,17 @@ pvt_open(struct pvt *pvt, long nrows, const char *dir, const char *name) return -1; } + char pcfpath[PATH_MAX]; + if (snprintf(pcfpath, PATH_MAX, "%s/%s.pcf", dir, name) >= PATH_MAX) { + err("snprintf failed: path too long"); + return -1; + } + + if (pcf_open(&pvt->pcf, pcfpath) != 0) { + err("pcf_open failed"); + return -1; + } + return 0; } @@ -35,8 +46,30 @@ pvt_get_prv(struct pvt *pvt) return &pvt->prv; } +struct pcf * +pvt_get_pcf(struct pvt *pvt) +{ + return &pvt->pcf; +} + int pvt_advance(struct pvt *pvt, int64_t time) { return prv_advance(&pvt->prv, time); } + +int +pvt_close(struct pvt *pvt) +{ + if (prv_close(&pvt->prv) != 0) { + err("prv_close failed"); + return -1; + } + + if (pcf_close(&pvt->pcf) != 0) { + err("pcf_close failed"); + return -1; + } + + return 0; +} diff --git a/src/emu/pvt.h b/src/emu/pvt.h index 0277a53..2984f11 100644 --- a/src/emu/pvt.h +++ b/src/emu/pvt.h @@ -22,5 +22,6 @@ int pvt_open(struct pvt *pvt, long nrows, const char *dir, const char *name); struct prv *pvt_get_prv(struct pvt *pvt); struct pcf *pvt_get_pcf(struct pvt *pvt); int pvt_advance(struct pvt *pvt, int64_t time); +int pvt_close(struct pvt *pvt); #endif /* PVT_H */ diff --git a/src/emu/recorder.c b/src/emu/recorder.c index 6ada336..c1e9022 100644 --- a/src/emu/recorder.c +++ b/src/emu/recorder.c @@ -64,3 +64,16 @@ recorder_advance(struct recorder *rec, int64_t time) return 0; } + +int +recorder_finish(struct recorder *rec) +{ + for (struct pvt *pvt = rec->pvt; pvt; pvt = pvt->hh.next) { + if (pvt_close(pvt) != 0) { + err("pvt_close failed"); + return -1; + } + } + + return 0; +} diff --git a/src/emu/recorder.h b/src/emu/recorder.h index 6c130d6..dd3a9ff 100644 --- a/src/emu/recorder.h +++ b/src/emu/recorder.h @@ -19,5 +19,6 @@ int recorder_init(struct recorder *rec, const char *dir); struct pvt *recorder_find_pvt(struct recorder *rec, const char *name); struct pvt *recorder_add_pvt(struct recorder *rec, const char *name, long nrows); int recorder_advance(struct recorder *rec, int64_t time); +int recorder_finish(struct recorder *rec); #endif /* RECORDER_H */