diff --git a/src/emu/CMakeLists.txt b/src/emu/CMakeLists.txt index 3deee46..4c2da7f 100644 --- a/src/emu/CMakeLists.txt +++ b/src/emu/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(chan STATIC chan.c bay.c mux.c + prv.c ) #add_library(emu STATIC diff --git a/src/emu/prv.c b/src/emu/prv.c index dad8062..4925096 100644 --- a/src/emu/prv.c +++ b/src/emu/prv.c @@ -1,80 +1,168 @@ -/* Copyright (c) 2021 Barcelona Supercomputing Center (BSC) +/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: GPL-3.0-or-later */ -#include - -#include "emu.h" -#include "ovni.h" #include "prv.h" - -void -prv_ev(FILE *f, int row, int64_t time, int type, int val) -{ - dbg("<<< 2:0:1:1:%d:%ld:%d:%d\n", row, time, type, val); - fprintf(f, "2:0:1:1:%d:%ld:%d:%d\n", row, time, type, val); -} - -void -prv_ev_thread_raw(struct ovni_emu *emu, int row, int64_t time, int type, int val) -{ - prv_ev(emu->prv_thread, row, time, type, val); -} - -void -prv_ev_thread(struct ovni_emu *emu, int row, int type, int val) -{ - prv_ev_thread_raw(emu, row, emu->delta_time, type, val); -} +#include +#include static void -prv_ev_cpu_raw(struct ovni_emu *emu, int row, int64_t time, int type, int val) +write_header(FILE *f, long long duration, int nrows) { - prv_ev(emu->prv_cpu, row, time, type, val); + fprintf(f, "#Paraver (19/01/38 at 03:14):%020lld_ns:0:1:1(%d:1)\n", + duration, nrows); +} + +int +prv_open_file(struct prv *prv, struct bay *bay, long nrows, FILE *file) +{ + memset(prv, 0, sizeof(struct prv)); + + prv->nrows = nrows; + prv->bay = bay; + prv->file = file; + + /* Write fake header to allocate the space */ + write_header(file, 0LL, nrows); + + return 0; +} + +int +prv_open(struct prv *prv, struct bay *bay, long nrows, const char *path) +{ + FILE *f = fopen(path, "w"); + + if (f == NULL) { + die("prv_open: cannot open file '%s' for writting: %s\n", + path, strerror(errno)); + return -1; + } + + return prv_open_file(prv, bay, nrows, f); } void -prv_ev_cpu(struct ovni_emu *emu, int row, int type, int val) +prv_close(struct prv *prv) { - prv_ev_cpu_raw(emu, row, emu->delta_time, type, val); + /* Fix the header with the current duration */ + fseek(prv->file, 0, SEEK_SET); + write_header(prv->file, prv->time, prv->nrows); + fclose(prv->file); } -void -prv_ev_autocpu_raw(struct ovni_emu *emu, int64_t time, int type, int val) +static struct prv_chan * +find_prv_chan(struct prv *prv, const char *name) { - if (emu->cur_thread == NULL) - die("prv_ev_autocpu_raw: current thread is NULL\n"); + struct prv_chan *rchan = NULL; + HASH_FIND_STR(prv->channels, name, rchan); - struct ovni_cpu *cpu = emu->cur_thread->cpu; - - if (cpu == NULL) - die("prv_ev_autocpu_raw: current thread CPU is NULL\n"); - - /* FIXME: Use the global index of the CPUs */ - if (cpu->i < 0) - die("prv_ev_autocpu_raw: CPU index is negative\n"); - - /* Begin at 1 */ - int row = emu->cur_loom->offset_ncpus + cpu->i + 1; - - prv_ev_cpu_raw(emu, row, time, type, val); + return rchan; } -void -prv_ev_autocpu(struct ovni_emu *emu, int type, int val) +static int +write_line(struct prv *prv, long row_base1, long type, long value) { - prv_ev_autocpu_raw(emu, emu->delta_time, type, val); + int ret = fprintf(prv->file, "2:0:1:1:%ld:%ld:%ld:%ld\n", + row_base1, prv->time, type, value); + + if (ret < 0) + return -1; + else + return 0; } -void -prv_header(FILE *f, int nrows) +static int +emit(struct prv *prv, struct prv_chan *rchan) { - fprintf(f, "#Paraver (19/01/38 at 03:14):%020ld_ns:0:1:1(%d:1)\n", 0LU, nrows); + struct value value; + struct chan *chan = rchan->chan; + if (chan_read(chan, &value) != 0) { + err("prv_emit: chan_read %s failed\n", chan->name); + return -1; + } + + /* Ensure we don't emit the same value twice */ + if (rchan->last_value_set) { + if (value_is_equal(&value, &rchan->last_value)) { + char buf[128]; + err("prv_emit: cannot emit value %s twice for channel %s\n", + value_str(value, buf), chan->name); + return -1; + } + } + + if (value.type != VALUE_INT64) { + char buf[128]; + err("prv_emit: chan_read %s only int64 supported: %s\n", + chan->name, value_str(value, buf)); + return -1; + } + + if (write_line(prv, rchan->row_base1, rchan->type, value.i) != 0) { + err("prv_emit: write_line failed for channel %s\n", + chan->name); + return -1; + } + + rchan->last_value = value; + rchan->last_value_set = 1; + + return 0; } -void -prv_fix_header(FILE *f, uint64_t duration, int nrows) +static int +cb_prv(struct chan *chan, void *ptr) { - /* Go to the first byte */ - fseek(f, 0, SEEK_SET); - fprintf(f, "#Paraver (19/01/38 at 03:14):%020ld_ns:0:1:1(%d:1)\n", duration, nrows); + UNUSED(chan); + struct prv_chan *rchan = ptr; + struct prv *prv = rchan->prv; + + return emit(prv, rchan); +} + +int +prv_register(struct prv *prv, long row, long type, struct chan *chan) +{ + struct prv_chan *rchan = find_prv_chan(prv, chan->name); + if (rchan != NULL) { + err("prv_register: channel %s already registered\n", + chan->name); + return -1; + } + + rchan = calloc(1, sizeof(struct prv_chan)); + if (rchan == NULL) { + err("prv_register: calloc failed\n"); + return -1; + } + + rchan->chan = chan; + rchan->row_base1 = row + 1; + rchan->type = type; + rchan->prv = prv; + rchan->last_value = value_null(); + rchan->last_value_set = 0; + + /* Add emit callback */ + if (bay_add_cb(prv->bay, BAY_CB_EMIT, chan, cb_prv, rchan) != 0) { + err("prv_register: bay_add_cb failed\n"); + return -1; + } + + /* Add to hash table */ + HASH_ADD_STR(prv->channels, chan->name, rchan); + + return 0; +} + +int +prv_advance(struct prv *prv, int64_t time) +{ + if (time < prv->time) { + err("prv_advance: cannot move to previous time\n"); + return -1; + } + + prv->time = time; + return 0; } diff --git a/src/emu/prv.h b/src/emu/prv.h index 149611e..fefbad5 100644 --- a/src/emu/prv.h +++ b/src/emu/prv.h @@ -1,35 +1,38 @@ -/* Copyright (c) 2021 Barcelona Supercomputing Center (BSC) +/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: GPL-3.0-or-later */ -#ifndef OVNI_PRV_H -#define OVNI_PRV_H +#ifndef PRV_H +#define PRV_H -#include "emu.h" -#include "ovni.h" -#include +#include "chan.h" +#include "bay.h" +#include "uthash.h" +#include -void -prv_ev(FILE *f, int row, int64_t time, int type, int val); +struct prv; -void -prv_ev_thread_raw(struct ovni_emu *emu, int row, int64_t time, int type, int val); +struct prv_chan { + struct prv *prv; + struct chan *chan; + long row_base1; + long type; + int last_value_set; + struct value last_value; + UT_hash_handle hh; /* Indexed by chan->name */ +}; -void -prv_ev_thread(struct ovni_emu *emu, int row, int type, int val); +struct prv { + FILE *file; + struct bay *bay; + int64_t time; + long nrows; + struct prv_chan *channels; +}; -void -prv_ev_cpu(struct ovni_emu *emu, int row, int type, int val); +int prv_open(struct prv *prv, struct bay *bay, long nrows, const char *path); +int prv_open_file(struct prv *prv, struct bay *bay, long nrows, FILE *file); +int prv_register(struct prv *prv, long row, long type, struct chan *c); +int prv_advance(struct prv *prv, int64_t time); +void prv_close(struct prv *prv); -void -prv_ev_autocpu(struct ovni_emu *emu, int type, int val); - -void -prv_ev_autocpu_raw(struct ovni_emu *emu, int64_t time, int type, int val); - -void -prv_header(FILE *f, int nrows); - -void -prv_fix_header(FILE *f, uint64_t duration, int nrows); - -#endif /* OVNI_PRV_H */ +#endif /* PRV_H */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 58b5a4e..4d99bd7 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,3 +13,4 @@ unit_test(bay.c) unit_test(bay-hash-speed.c) unit_test(mux.c) unit_test(value.c) +unit_test(prv.c) diff --git a/test/unit/prv.c b/test/unit/prv.c new file mode 100644 index 0000000..f18d92a --- /dev/null +++ b/test/unit/prv.c @@ -0,0 +1,125 @@ +#define _GNU_SOURCE + +#include "emu/prv.h" +#include "common.h" + +#include +#include + +#define NROWS 10 + +static void +test_emit(const char *path) +{ + long type = 100; + long value_base = 1000; + struct chan chan[NROWS]; + + struct bay bay; + bay_init(&bay); + + struct prv prv; + prv_open(&prv, &bay, NROWS, path); + + for (int i = 0; i < NROWS; i++) { + char buf[MAX_CHAN_NAME]; + sprintf(buf, "testchan.%d", i); + chan_init(&chan[i], CHAN_SINGLE, buf); + + if (bay_register(&bay, &chan[i]) != 0) + die("bay_register failed\n"); + } + + for (int i = 0; i < NROWS; i++) + if (prv_register(&prv, i, type, &chan[i]) != 0) + die("prv_register failed\n"); + + for (int i = 0; i < NROWS; i++) + if (chan_set(&chan[i], value_int64(value_base + i)) != 0) + die("chan_set failed\n"); + + prv_advance(&prv, 10000); + + if (bay_propagate(&bay) != 0) + die("bay_propagate failed\n"); + + /* Ensure all writes are flushed to the buffer and + * the header has been fixed */ + prv_close(&prv); + + FILE *f = fopen(path, "r"); + int c; + while ((c = fgetc(f)) != EOF) { + fputc(c, stderr); + } + fclose(f); + + err("test emit OK\n"); +} + +static void +test_duplicate(const char *path) +{ + /* Ensure that we detect duplicate values being emitted in the Paraver + * trace */ + + long type = 100; + + struct bay bay; + bay_init(&bay); + + struct prv prv; + prv_open(&prv, &bay, NROWS, path); + + struct chan chan; + chan_init(&chan, CHAN_SINGLE, "testchan"); + + /* Allow setting the same value in the channel */ + chan_prop_set(&chan, CHAN_DUPLICATES, 1); + + if (bay_register(&bay, &chan) != 0) + die("bay_register failed\n"); + + if (prv_register(&prv, 0, type, &chan) != 0) + die("prv_register failed\n"); + + if (chan_set(&chan, value_int64(1000)) != 0) + die("chan_set failed\n"); + + if (prv_advance(&prv, 10000) != 0) + die("prv_advance failed\n"); + + if (bay_propagate(&bay) != 0) + die("bay_propagate failed\n"); + + /* Set the same value again, which shouldn't fail */ + if (chan_set(&chan, value_int64(1000)) != 0) + die("chan_set failed\n"); + + /* Now the propagation phase must fail, as we cannot write the same + * value in the prv trace */ + if (bay_propagate(&bay) == 0) + die("bay_propagate didn't fail\n"); + + /* Ensure all writes are flushed to the buffer and + * the header has been fixed */ + prv_close(&prv); + + err("test duplicate OK\n"); +} + +int main(void) +{ + /* Create temporary trace file */ + char fname[] = "/tmp/ovni.prv.XXXXXX"; + int fd = mkstemp(fname); + if (fd < 0) + die("mkstemp failed\n"); + + test_emit(fname); + test_duplicate(fname); + + close(fd); + + return 0; +}