Add PCF information for nanos6

This commit is contained in:
Rodrigo Arias 2023-02-01 18:18:58 +01:00 committed by Rodrigo Arias Mallo
parent c94a786c1e
commit 4e2164646c
15 changed files with 570 additions and 411 deletions

View File

@ -27,6 +27,7 @@ add_library(emu STATIC
mux.c mux.c
path.c path.c
proc.c proc.c
pcf.c
prv.c prv.c
pvt.c pvt.c
recorder.c recorder.c
@ -41,6 +42,7 @@ add_library(emu STATIC
nanos6/connect.c nanos6/connect.c
nanos6/create.c nanos6/create.c
nanos6/event.c nanos6/event.c
nanos6/pvt.c
) )
add_executable(ovniemu ovniemu.c) add_executable(ovniemu ovniemu.c)

View File

@ -156,3 +156,13 @@ emu_step(struct emu *emu)
return 0; return 0;
} }
int
emu_finish(struct emu *emu)
{
if (recorder_finish(&emu->recorder) != 0) {
err("recorder_finish failed");
return -1;
}
return 0;
}

View File

@ -39,6 +39,7 @@ struct emu {
int emu_init(struct emu *emu, int argc, char *argv[]); int emu_init(struct emu *emu, int argc, char *argv[]);
int emu_connect(struct emu *emu); int emu_connect(struct emu *emu);
int emu_step(struct emu *emu); int emu_step(struct emu *emu);
int emu_finish(struct emu *emu);
static inline struct emu * static inline struct emu *
emu_get(void *ptr) emu_get(void *ptr)

View File

@ -1,32 +1,14 @@
#include "nanos6_priv.h" #include "nanos6_priv.h"
const enum nanos6_track th_track[] = { const enum nanos6_track chan_track[CH_MAX][CT_MAX] = {
[CH_TASKID] = RUN_TH, /* Thread CPU */
[CH_TYPE] = RUN_TH, [CH_TASKID] = { RUN_TH, RUN_TH },
[CH_SUBSYSTEM] = ACT_TH, [CH_TYPE] = { RUN_TH, RUN_TH },
[CH_RANK] = RUN_TH, [CH_SUBSYSTEM] = { ACT_TH, RUN_TH },
[CH_THREAD] = NONE, [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 static int
connect_thread_mux(struct emu *emu, struct thread *thread) 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 /* The tracking only sets the ch_out, but we keep both tracking
* updated as the CPU tracking channels may use them. */ * 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]; 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]; th->ch_out[i] = &th->ch_act[i];
else else
th->ch_out[i] = &th->ch[i]; th->ch_out[i] = &th->ch[i];
@ -79,23 +62,6 @@ connect_thread_mux(struct emu *emu, struct thread *thread)
return 0; 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 static int
add_inputs_cpu_mux(struct emu *emu, struct mux *mux, int i) 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 */ /* Choose input thread channel based on tracking mode */
struct chan *inp = NULL; 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]; inp = &th->ch_run[i];
else if (cpu_track[i] == ACT_TH) else if (track == ACT_TH)
inp = &th->ch_act[i]; inp = &th->ch_act[i];
else 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) { if (mux_add_input(mux, value_int64(t->gindex), inp) != 0) {
err("mux_add_input failed"); 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 */ /* Choose select CPU channel based on tracking mode */
struct chan *sel = NULL; 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]; sel = &scpu->chan[CPU_CHAN_THRUN];
else if (cpu_track[i] == ACT_TH) else if (track == ACT_TH)
sel = &scpu->chan[CPU_CHAN_THACT]; sel = &scpu->chan[CPU_CHAN_THACT];
else 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) { if (mux_init(mux, &emu->bay, sel, out, NULL) != 0) {
err("mux_init failed"); err("mux_init failed");
@ -151,8 +119,8 @@ connect_cpu_mux(struct emu *emu, struct cpu *scpu)
return 0; return 0;
} }
static int int
connect_threads(struct emu *emu) nanos6_connect(struct emu *emu)
{ {
struct system *sys = &emu->system; 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 */ /* cpus */
for (struct cpu *c = sys->cpus; c; c = c->next) { for (struct cpu *c = sys->cpus; c; c = c->next) {
if (connect_cpu_mux(emu, c) != 0) { if (connect_cpu_mux(emu, c) != 0) {
@ -217,37 +140,8 @@ connect_cpus(struct emu *emu)
} }
} }
/* Get cpu PRV */ if (init_pvt(emu) != 0) {
struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); err("init_pvt failed");
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");
return -1; return -1;
} }

View File

@ -10,7 +10,14 @@
#include "task.h" #include "task.h"
/* Private enums */ /* Private enums */
enum nanos6_chan_type { enum nanos6_chan_type {
CT_TH = 0,
CT_CPU,
CT_MAX
};
enum nanos6_chan {
CH_TASKID = 0, CH_TASKID = 0,
CH_TYPE, CH_TYPE,
CH_SUBSYSTEM, CH_SUBSYSTEM,
@ -26,8 +33,7 @@ enum nanos6_track {
TRACK_MAX, TRACK_MAX,
}; };
extern const enum nanos6_track th_track[CH_MAX]; extern const enum nanos6_track chan_track[CH_MAX][CT_MAX];
extern const enum nanos6_track cpu_track[CH_MAX];
enum nanos6_ss_state { enum nanos6_ss_state {
ST_TASK_BODY = 1, ST_TASK_BODY = 1,
@ -93,4 +99,6 @@ int nanos6_create(struct emu *emu);
int nanos6_connect(struct emu *emu); int nanos6_connect(struct emu *emu);
int nanos6_event(struct emu *emu); int nanos6_event(struct emu *emu);
int init_pvt(struct emu *emu);
#endif /* NANOS6_PRIV_H */ #endif /* NANOS6_PRIV_H */

242
src/emu/nanos6/pvt.c Normal file
View File

@ -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;
}

View File

@ -21,16 +21,25 @@ main(int argc, char *argv[])
if (emu_connect(emu) != 0) if (emu_connect(emu) != 0)
die("emu_connect failed\n"); die("emu_connect failed\n");
err("emulation starts\n"); err("emulation starts");
int ret = 0; int ret = 0;
while ((ret = emu_step(emu)) == 0); while ((ret = emu_step(emu)) == 0);
if (ret < 0) if (ret < 0) {
die("emu_step failed\n"); 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); free(emu);
return 0; return ret;
} }

View File

@ -2,9 +2,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later */ * SPDX-License-Identifier: GPL-3.0-or-later */
#include "pcf.h" #include "pcf.h"
#include "emu.h"
#include "prv.h"
#include "common.h"
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -69,210 +68,210 @@ const uint32_t pcf_def_palette[] = {
const uint32_t *pcf_palette = pcf_def_palette; const uint32_t *pcf_palette = pcf_def_palette;
const int pcf_palette_len = ARRAY_LEN(pcf_def_palette); const int pcf_palette_len = ARRAY_LEN(pcf_def_palette);
/* ------------------ Value labels --------------------- */ ///* ------------------ Value labels --------------------- */
//
struct pcf_value_label default_values[] = { //struct pcf_value_label default_values[] = {
{ ST_TOO_MANY_TH, "Unknown: Multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown: Multiple threads running" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label ovni_state_values[] = { //struct pcf_value_label ovni_state_values[] = {
{ TH_ST_UNKNOWN, "Unknown" }, // { TH_ST_UNKNOWN, "Unknown" },
{ TH_ST_RUNNING, "Running" }, // { TH_ST_RUNNING, "Running" },
{ TH_ST_PAUSED, "Paused" }, // { TH_ST_PAUSED, "Paused" },
{ TH_ST_DEAD, "Dead" }, // { TH_ST_DEAD, "Dead" },
{ TH_ST_COOLING, "Cooling" }, // { TH_ST_COOLING, "Cooling" },
{ TH_ST_WARMING, "Warming" }, // { TH_ST_WARMING, "Warming" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label ovni_flush_values[] = { //struct pcf_value_label ovni_flush_values[] = {
{ 0, "None" }, // { 0, "None" },
{ ST_OVNI_FLUSHING, "Flushing" }, // { ST_OVNI_FLUSHING, "Flushing" },
{ ST_TOO_MANY_TH, "Unknown flushing state: Multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown flushing state: Multiple threads running" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label nosv_ss_values[] = { //struct pcf_value_label nosv_ss_values[] = {
/* Errors */ // /* Errors */
{ ST_BAD, "Unknown: bad happened (report bug)" }, // { ST_BAD, "Unknown: bad happened (report bug)" },
{ ST_TOO_MANY_TH, "Unknown: multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown: multiple threads running" },
/* Good values */ // /* Good values */
{ ST_NULL, "No subsystem" }, // { ST_NULL, "No subsystem" },
{ ST_NOSV_SCHED_HUNGRY, "Scheduler: Hungry" }, // { ST_NOSV_SCHED_HUNGRY, "Scheduler: Hungry" },
{ ST_NOSV_SCHED_SERVING, "Scheduler: Serving" }, // { ST_NOSV_SCHED_SERVING, "Scheduler: Serving" },
{ ST_NOSV_SCHED_SUBMITTING, "Scheduler: Submitting" }, // { ST_NOSV_SCHED_SUBMITTING, "Scheduler: Submitting" },
{ ST_NOSV_MEM_ALLOCATING, "Memory: Allocating" }, // { ST_NOSV_MEM_ALLOCATING, "Memory: Allocating" },
{ ST_NOSV_MEM_FREEING, "Memory: Freeing" }, // { ST_NOSV_MEM_FREEING, "Memory: Freeing" },
{ ST_NOSV_TASK_RUNNING, "Task: Running" }, // { ST_NOSV_TASK_RUNNING, "Task: Running" },
{ ST_NOSV_API_SUBMIT, "API: Submit" }, // { ST_NOSV_API_SUBMIT, "API: Submit" },
{ ST_NOSV_API_PAUSE, "API: Pause" }, // { ST_NOSV_API_PAUSE, "API: Pause" },
{ ST_NOSV_API_YIELD, "API: Yield" }, // { ST_NOSV_API_YIELD, "API: Yield" },
{ ST_NOSV_API_WAITFOR, "API: Waitfor" }, // { ST_NOSV_API_WAITFOR, "API: Waitfor" },
{ ST_NOSV_API_SCHEDPOINT, "API: Scheduling point" }, // { ST_NOSV_API_SCHEDPOINT, "API: Scheduling point" },
{ ST_NOSV_ATTACH, "Thread: Attached" }, // { ST_NOSV_ATTACH, "Thread: Attached" },
{ ST_NOSV_WORKER, "Thread: Worker" }, // { ST_NOSV_WORKER, "Thread: Worker" },
{ ST_NOSV_DELEGATE, "Thread: Delegate" }, // { ST_NOSV_DELEGATE, "Thread: Delegate" },
{ EV_NOSV_SCHED_SEND, "EV Scheduler: Send task" }, // { EV_NOSV_SCHED_SEND, "EV Scheduler: Send task" },
{ EV_NOSV_SCHED_RECV, "EV Scheduler: Recv task" }, // { EV_NOSV_SCHED_RECV, "EV Scheduler: Recv task" },
{ EV_NOSV_SCHED_SELF, "EV Scheduler: Self-assign task" }, // { EV_NOSV_SCHED_SELF, "EV Scheduler: Self-assign task" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label nodes_mode_values[] = { //struct pcf_value_label nodes_mode_values[] = {
{ ST_NULL, "NULL" }, // { ST_NULL, "NULL" },
{ ST_TOO_MANY_TH, "NODES: Multiple threads running" }, // { ST_TOO_MANY_TH, "NODES: Multiple threads running" },
{ ST_NODES_REGISTER, "Dependencies: Registering task accesses" }, // { ST_NODES_REGISTER, "Dependencies: Registering task accesses" },
{ ST_NODES_UNREGISTER, "Dependencies: Unregistering task accesses" }, // { ST_NODES_UNREGISTER, "Dependencies: Unregistering task accesses" },
{ ST_NODES_IF0_WAIT, "If0: Waiting for an If0 task" }, // { ST_NODES_IF0_WAIT, "If0: Waiting for an If0 task" },
{ ST_NODES_IF0_INLINE, "If0: Executing an If0 task inline" }, // { ST_NODES_IF0_INLINE, "If0: Executing an If0 task inline" },
{ ST_NODES_TASKWAIT, "Taskwait: Taskwait" }, // { ST_NODES_TASKWAIT, "Taskwait: Taskwait" },
{ ST_NODES_CREATE, "Add Task: Creating a task" }, // { ST_NODES_CREATE, "Add Task: Creating a task" },
{ ST_NODES_SUBMIT, "Add Task: Submitting a task" }, // { ST_NODES_SUBMIT, "Add Task: Submitting a task" },
{ ST_NODES_SPAWN, "Spawn Function: Spawning a function" }, // { ST_NODES_SPAWN, "Spawn Function: Spawning a function" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label kernel_cs_values[] = { //struct pcf_value_label kernel_cs_values[] = {
{ ST_NULL, "NULL" }, // { ST_NULL, "NULL" },
{ ST_TOO_MANY_TH, "Unknown: multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown: multiple threads running" },
{ ST_KERNEL_CSOUT, "Context switch: Out of the CPU" }, // { ST_KERNEL_CSOUT, "Context switch: Out of the CPU" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label nanos6_ss_values[] = { //struct pcf_value_label nanos6_ss_values[] = {
{ ST_NULL, "No subsystem" }, // { ST_NULL, "No subsystem" },
{ ST_TOO_MANY_TH, "Unknown: multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown: multiple threads running" },
{ ST_NANOS6_TASK_BODY, "Task: Running body" }, // { ST_NANOS6_TASK_BODY, "Task: Running body" },
{ ST_NANOS6_TASK_CREATING, "Task: Creating" }, // { ST_NANOS6_TASK_CREATING, "Task: Creating" },
{ ST_NANOS6_TASK_SUBMIT, "Task: Submitting" }, // { ST_NANOS6_TASK_SUBMIT, "Task: Submitting" },
{ ST_NANOS6_TASK_SPAWNING, "Task: Spawning function" }, // { ST_NANOS6_TASK_SPAWNING, "Task: Spawning function" },
{ ST_NANOS6_TASK_FOR, "Task: Running task for" }, // { ST_NANOS6_TASK_FOR, "Task: Running task for" },
{ ST_NANOS6_SCHED_SERVING, "Scheduler: Serving tasks" }, // { ST_NANOS6_SCHED_SERVING, "Scheduler: Serving tasks" },
{ ST_NANOS6_SCHED_ADDING, "Scheduler: Adding ready tasks" }, // { ST_NANOS6_SCHED_ADDING, "Scheduler: Adding ready tasks" },
{ ST_NANOS6_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, // { ST_NANOS6_SCHED_PROCESSING, "Scheduler: Processing ready tasks" },
{ ST_NANOS6_DEP_REG, "Dependency: Registering" }, // { ST_NANOS6_DEP_REG, "Dependency: Registering" },
{ ST_NANOS6_DEP_UNREG, "Dependency: Unregistering" }, // { ST_NANOS6_DEP_UNREG, "Dependency: Unregistering" },
{ ST_NANOS6_BLK_TASKWAIT, "Blocking: Taskwait" }, // { ST_NANOS6_BLK_TASKWAIT, "Blocking: Taskwait" },
{ ST_NANOS6_BLK_BLOCKING, "Blocking: Blocking current task" }, // { ST_NANOS6_BLK_BLOCKING, "Blocking: Blocking current task" },
{ ST_NANOS6_BLK_UNBLOCKING, "Blocking: Unblocking remote task" }, // { ST_NANOS6_BLK_UNBLOCKING, "Blocking: Unblocking remote task" },
{ ST_NANOS6_BLK_WAITFOR, "Blocking: Wait for deadline" }, // { ST_NANOS6_BLK_WAITFOR, "Blocking: Wait for deadline" },
{ ST_NANOS6_HANDLING_TASK, "Worker: Handling task" }, // { ST_NANOS6_HANDLING_TASK, "Worker: Handling task" },
{ ST_NANOS6_WORKER_LOOP, "Worker: Looking for work" }, // { ST_NANOS6_WORKER_LOOP, "Worker: Looking for work" },
{ ST_NANOS6_SWITCH_TO, "Worker: Switching to another thread" }, // { ST_NANOS6_SWITCH_TO, "Worker: Switching to another thread" },
{ ST_NANOS6_MIGRATE, "Worker: Migrating CPU" }, // { ST_NANOS6_MIGRATE, "Worker: Migrating CPU" },
{ ST_NANOS6_SUSPEND, "Worker: Suspending thread" }, // { ST_NANOS6_SUSPEND, "Worker: Suspending thread" },
{ ST_NANOS6_RESUME, "Worker: Resuming another thread" }, // { ST_NANOS6_RESUME, "Worker: Resuming another thread" },
{ ST_NANOS6_ALLOCATING, "Memory: Allocating" }, // { ST_NANOS6_ALLOCATING, "Memory: Allocating" },
{ ST_NANOS6_FREEING, "Memory: Freeing" }, // { ST_NANOS6_FREEING, "Memory: Freeing" },
{ EV_NANOS6_SCHED_SEND, "EV Scheduler: Send task" }, // { EV_NANOS6_SCHED_SEND, "EV Scheduler: Send task" },
{ EV_NANOS6_SCHED_RECV, "EV Scheduler: Recv task" }, // { EV_NANOS6_SCHED_RECV, "EV Scheduler: Recv task" },
{ EV_NANOS6_SCHED_SELF, "EV Scheduler: Self-assign task" }, // { EV_NANOS6_SCHED_SELF, "EV Scheduler: Self-assign task" },
{ EV_NANOS6_CPU_IDLE, "EV CPU: Becomes idle" }, // { EV_NANOS6_CPU_IDLE, "EV CPU: Becomes idle" },
{ EV_NANOS6_CPU_ACTIVE, "EV CPU: Becomes active" }, // { EV_NANOS6_CPU_ACTIVE, "EV CPU: Becomes active" },
{ EV_NANOS6_SIGNAL, "EV Worker: Wakening another thread" }, // { EV_NANOS6_SIGNAL, "EV Worker: Wakening another thread" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label nanos6_thread_type[] = { //struct pcf_value_label nanos6_thread_type[] = {
{ ST_NULL, "No type" }, // { ST_NULL, "No type" },
{ ST_TOO_MANY_TH, "Unknown: multiple threads running" }, // { ST_TOO_MANY_TH, "Unknown: multiple threads running" },
{ ST_NANOS6_TH_EXTERNAL, "External" }, // { ST_NANOS6_TH_EXTERNAL, "External" },
{ ST_NANOS6_TH_WORKER, "Worker" }, // { ST_NANOS6_TH_WORKER, "Worker" },
{ ST_NANOS6_TH_LEADER, "Leader" }, // { ST_NANOS6_TH_LEADER, "Leader" },
{ ST_NANOS6_TH_MAIN, "Main" }, // { ST_NANOS6_TH_MAIN, "Main" },
{ -1, NULL }, // { -1, NULL },
}; //};
//
struct pcf_value_label (*pcf_chan_value_labels[CHAN_MAX])[] = { //struct pcf_value_label (*pcf_chan_value_labels[CHAN_MAX])[] = {
[CHAN_OVNI_PID] = &default_values, // [CHAN_OVNI_PID] = &default_values,
[CHAN_OVNI_TID] = &default_values, // [CHAN_OVNI_TID] = &default_values,
[CHAN_OVNI_NRTHREADS] = &default_values, // [CHAN_OVNI_NRTHREADS] = &default_values,
[CHAN_OVNI_STATE] = &ovni_state_values, // [CHAN_OVNI_STATE] = &ovni_state_values,
[CHAN_OVNI_APPID] = &default_values, // [CHAN_OVNI_APPID] = &default_values,
[CHAN_OVNI_CPU] = &default_values, // [CHAN_OVNI_CPU] = &default_values,
[CHAN_OVNI_FLUSH] = &ovni_flush_values, // [CHAN_OVNI_FLUSH] = &ovni_flush_values,
//
[CHAN_NOSV_TASKID] = &default_values, // [CHAN_NOSV_TASKID] = &default_values,
[CHAN_NOSV_TYPE] = &default_values, // [CHAN_NOSV_TYPE] = &default_values,
[CHAN_NOSV_APPID] = &default_values, // [CHAN_NOSV_APPID] = &default_values,
[CHAN_NOSV_SUBSYSTEM] = &nosv_ss_values, // [CHAN_NOSV_SUBSYSTEM] = &nosv_ss_values,
[CHAN_NOSV_RANK] = &default_values, // [CHAN_NOSV_RANK] = &default_values,
//
[CHAN_NODES_SUBSYSTEM] = &nodes_mode_values, // [CHAN_NODES_SUBSYSTEM] = &nodes_mode_values,
//
[CHAN_NANOS6_TASKID] = &default_values, // [CHAN_NANOS6_TASKID] = &default_values,
[CHAN_NANOS6_TYPE] = &default_values, // [CHAN_NANOS6_TYPE] = &default_values,
[CHAN_NANOS6_SUBSYSTEM] = &nanos6_ss_values, // [CHAN_NANOS6_SUBSYSTEM] = &nanos6_ss_values,
[CHAN_NANOS6_RANK] = &default_values, // [CHAN_NANOS6_RANK] = &default_values,
[CHAN_NANOS6_THREAD] = &nanos6_thread_type, // [CHAN_NANOS6_THREAD] = &nanos6_thread_type,
//
[CHAN_KERNEL_CS] = &kernel_cs_values, // [CHAN_KERNEL_CS] = &kernel_cs_values,
}; //};
//
/* ------------------ Type labels --------------------- */ ///* ------------------ Type labels --------------------- */
//
char *pcf_chan_name[CHAN_MAX] = { //char *pcf_chan_name[CHAN_MAX] = {
[CHAN_OVNI_PID] = "PID", // [CHAN_OVNI_PID] = "PID",
[CHAN_OVNI_TID] = "TID", // [CHAN_OVNI_TID] = "TID",
[CHAN_OVNI_NRTHREADS] = "Number of RUNNING threads", // [CHAN_OVNI_NRTHREADS] = "Number of RUNNING threads",
[CHAN_OVNI_STATE] = "Execution state", // [CHAN_OVNI_STATE] = "Execution state",
[CHAN_OVNI_APPID] = "AppID", // [CHAN_OVNI_APPID] = "AppID",
[CHAN_OVNI_CPU] = "CPU affinity", // [CHAN_OVNI_CPU] = "CPU affinity",
[CHAN_OVNI_FLUSH] = "Flushing state", // [CHAN_OVNI_FLUSH] = "Flushing state",
//
[CHAN_NOSV_TASKID] = "nOS-V TaskID", // [CHAN_NOSV_TASKID] = "nOS-V TaskID",
[CHAN_NOSV_TYPE] = "nOS-V task type", // [CHAN_NOSV_TYPE] = "nOS-V task type",
[CHAN_NOSV_APPID] = "nOS-V task AppID", // [CHAN_NOSV_APPID] = "nOS-V task AppID",
[CHAN_NOSV_SUBSYSTEM] = "nOS-V subsystem", // [CHAN_NOSV_SUBSYSTEM] = "nOS-V subsystem",
[CHAN_NOSV_RANK] = "nOS-V task MPI rank", // [CHAN_NOSV_RANK] = "nOS-V task MPI rank",
//
[CHAN_NODES_SUBSYSTEM] = "NODES subsystem", // [CHAN_NODES_SUBSYSTEM] = "NODES subsystem",
//
[CHAN_NANOS6_TASKID] = "Nanos6 task ID", // [CHAN_NANOS6_TASKID] = "Nanos6 task ID",
[CHAN_NANOS6_TYPE] = "Nanos6 task type", // [CHAN_NANOS6_TYPE] = "Nanos6 task type",
[CHAN_NANOS6_SUBSYSTEM] = "Nanos6 subsystem", // [CHAN_NANOS6_SUBSYSTEM] = "Nanos6 subsystem",
[CHAN_NANOS6_RANK] = "Nanos6 task MPI rank", // [CHAN_NANOS6_RANK] = "Nanos6 task MPI rank",
[CHAN_NANOS6_THREAD] = "Nanos6 thread type", // [CHAN_NANOS6_THREAD] = "Nanos6 thread type",
//
[CHAN_KERNEL_CS] = "Context switches", // [CHAN_KERNEL_CS] = "Context switches",
}; //};
//
enum pcf_suffix { NONE = 0, CUR_TH, RUN_TH, ACT_TH, SUFFIX_MAX }; //enum pcf_suffix { NONE = 0, CUR_TH, RUN_TH, ACT_TH, SUFFIX_MAX };
//
char *pcf_suffix_name[SUFFIX_MAX] = { //char *pcf_suffix_name[SUFFIX_MAX] = {
[NONE] = "", // [NONE] = "",
[CUR_TH] = "of the CURRENT thread", // [CUR_TH] = "of the CURRENT thread",
[RUN_TH] = "of the RUNNING thread", // [RUN_TH] = "of the RUNNING thread",
[ACT_TH] = "of the ACTIVE thread", // [ACT_TH] = "of the ACTIVE thread",
}; //};
//
int pcf_chan_suffix[CHAN_MAX][CHAN_MAXTYPE] = { //int pcf_chan_suffix[CHAN_MAX][CHAN_MAXTYPE] = {
/* Thread CPU */ // /* Thread CPU */
[CHAN_OVNI_PID] = { RUN_TH, RUN_TH }, // [CHAN_OVNI_PID] = { RUN_TH, RUN_TH },
[CHAN_OVNI_TID] = { RUN_TH, RUN_TH }, // [CHAN_OVNI_TID] = { RUN_TH, RUN_TH },
[CHAN_OVNI_NRTHREADS] = { NONE, NONE }, // [CHAN_OVNI_NRTHREADS] = { NONE, NONE },
[CHAN_OVNI_STATE] = { CUR_TH, NONE }, // [CHAN_OVNI_STATE] = { CUR_TH, NONE },
[CHAN_OVNI_APPID] = { NONE, RUN_TH }, // [CHAN_OVNI_APPID] = { NONE, RUN_TH },
[CHAN_OVNI_CPU] = { CUR_TH, NONE }, // [CHAN_OVNI_CPU] = { CUR_TH, NONE },
[CHAN_OVNI_FLUSH] = { CUR_TH, RUN_TH }, // [CHAN_OVNI_FLUSH] = { CUR_TH, RUN_TH },
//
[CHAN_NOSV_TASKID] = { RUN_TH, RUN_TH }, // [CHAN_NOSV_TASKID] = { RUN_TH, RUN_TH },
[CHAN_NOSV_TYPE] = { RUN_TH, RUN_TH }, // [CHAN_NOSV_TYPE] = { RUN_TH, RUN_TH },
[CHAN_NOSV_APPID] = { RUN_TH, RUN_TH }, // [CHAN_NOSV_APPID] = { RUN_TH, RUN_TH },
[CHAN_NOSV_SUBSYSTEM] = { ACT_TH, RUN_TH }, // [CHAN_NOSV_SUBSYSTEM] = { ACT_TH, RUN_TH },
[CHAN_NOSV_RANK] = { RUN_TH, RUN_TH }, // [CHAN_NOSV_RANK] = { RUN_TH, RUN_TH },
//
[CHAN_NODES_SUBSYSTEM] = { RUN_TH, RUN_TH }, // [CHAN_NODES_SUBSYSTEM] = { RUN_TH, RUN_TH },
//
[CHAN_NANOS6_TASKID] = { RUN_TH, RUN_TH }, // [CHAN_NANOS6_TASKID] = { RUN_TH, RUN_TH },
[CHAN_NANOS6_TYPE] = { RUN_TH, RUN_TH }, // [CHAN_NANOS6_TYPE] = { RUN_TH, RUN_TH },
[CHAN_NANOS6_SUBSYSTEM] = { ACT_TH, RUN_TH }, // [CHAN_NANOS6_SUBSYSTEM] = { ACT_TH, RUN_TH },
[CHAN_NANOS6_RANK] = { RUN_TH, RUN_TH }, // [CHAN_NANOS6_RANK] = { RUN_TH, RUN_TH },
[CHAN_NANOS6_THREAD] = { RUN_TH, NONE }, // [CHAN_NANOS6_THREAD] = { RUN_TH, NONE },
//
[CHAN_KERNEL_CS] = { RUN_TH, ACT_TH }, // [CHAN_KERNEL_CS] = { RUN_TH, ACT_TH },
}; //};
/* clang-format on */ /* clang-format on */
@ -324,61 +323,20 @@ write_types(struct pcf *pcf)
write_type(pcf->f, t); 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. */ /** Open the given PCF file and create the default events. */
void int
pcf_open(struct pcf *pcf, char *path, int chantype) pcf_open(struct pcf *pcf, char *path)
{ {
memset(pcf, 0, sizeof(*pcf)); memset(pcf, 0, sizeof(*pcf));
pcf->f = fopen(path, "w"); pcf->f = fopen(path, "w");
pcf->chantype = chantype;
if (pcf->f == NULL) { if (pcf->f == NULL) {
die("cannot open PCF file '%s': %s\n", err("cannot open PCF file '%s':", path);
path, strerror(errno)); return -1;
} }
/* Create default types and values */ return 0;
for (enum chan c = 0; c < CHAN_MAX; c++)
create_type(pcf, c);
} }
struct pcf_type * struct pcf_type *
@ -461,17 +419,14 @@ pcf_add_value(struct pcf_type *type, int value, const char *label)
return pcfvalue; return pcfvalue;
} }
/** Writes the defined event and values to the PCF file. */ /** Writes the defined event and values to the PCF file and closes the file. */
void int
pcf_write(struct pcf *pcf) pcf_close(struct pcf *pcf)
{ {
write_header(pcf->f); write_header(pcf->f);
write_colors(pcf->f, pcf_palette, pcf_palette_len); write_colors(pcf->f, pcf_palette, pcf_palette_len);
write_types(pcf); write_types(pcf);
}
void
pcf_close(struct pcf *pcf)
{
fclose(pcf->f); fclose(pcf->f);
return 0;
} }

View File

@ -31,7 +31,6 @@ struct pcf_type {
struct pcf { struct pcf {
FILE *f; FILE *f;
int chantype;
int pcf_ntypes; int pcf_ntypes;
struct pcf_type *types; struct pcf_type *types;
}; };
@ -42,22 +41,12 @@ struct pcf_value_label {
char *label; char *label;
}; };
extern struct pcf_value_label nanos6_ss_values[]; int pcf_open(struct pcf *pcf, char *path);
int pcf_close(struct pcf *pcf);
void pcf_open(struct pcf *pcf, char *path, int chantype);
void pcf_write(struct pcf *pcf);
void pcf_close(struct pcf *pcf);
struct pcf_type *pcf_find_type(struct pcf *pcf, int type_id); 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_type *pcf_add_type(struct pcf *pcf, int type_id, struct pcf_value *pcf_add_value(struct pcf_type *type, int value, const char *label);
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); struct pcf_value *pcf_find_value(struct pcf_type *type, int value);
#endif /* OVNI_PCF_H */ #endif /* OVNI_PCF_H */

View File

@ -42,13 +42,14 @@ prv_open(struct prv *prv, long nrows, const char *path)
return prv_open_file(prv, nrows, f); return prv_open_file(prv, nrows, f);
} }
void int
prv_close(struct prv *prv) prv_close(struct prv *prv)
{ {
/* Fix the header with the current duration */ /* Fix the header with the current duration */
fseek(prv->file, 0, SEEK_SET); fseek(prv->file, 0, SEEK_SET);
write_header(prv->file, prv->time, prv->nrows); write_header(prv->file, prv->time, prv->nrows);
fclose(prv->file); fclose(prv->file);
return 0;
} }
static struct prv_chan * static struct prv_chan *

View File

@ -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_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_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); int prv_advance(struct prv *prv, int64_t time);
void prv_close(struct prv *prv); int prv_close(struct prv *prv);
#endif /* PRV_H */ #endif /* PRV_H */

View File

@ -26,6 +26,17 @@ pvt_open(struct pvt *pvt, long nrows, const char *dir, const char *name)
return -1; 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; return 0;
} }
@ -35,8 +46,30 @@ pvt_get_prv(struct pvt *pvt)
return &pvt->prv; return &pvt->prv;
} }
struct pcf *
pvt_get_pcf(struct pvt *pvt)
{
return &pvt->pcf;
}
int int
pvt_advance(struct pvt *pvt, int64_t time) pvt_advance(struct pvt *pvt, int64_t time)
{ {
return prv_advance(&pvt->prv, 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;
}

View File

@ -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 prv *pvt_get_prv(struct pvt *pvt);
struct pcf *pvt_get_pcf(struct pvt *pvt); struct pcf *pvt_get_pcf(struct pvt *pvt);
int pvt_advance(struct pvt *pvt, int64_t time); int pvt_advance(struct pvt *pvt, int64_t time);
int pvt_close(struct pvt *pvt);
#endif /* PVT_H */ #endif /* PVT_H */

View File

@ -64,3 +64,16 @@ recorder_advance(struct recorder *rec, int64_t time)
return 0; 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;
}

View File

@ -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_find_pvt(struct recorder *rec, const char *name);
struct pvt *recorder_add_pvt(struct recorder *rec, const char *name, long nrows); struct pvt *recorder_add_pvt(struct recorder *rec, const char *name, long nrows);
int recorder_advance(struct recorder *rec, int64_t time); int recorder_advance(struct recorder *rec, int64_t time);
int recorder_finish(struct recorder *rec);
#endif /* RECORDER_H */ #endif /* RECORDER_H */