Add PRV trace support for patchbay channels

This commit is contained in:
Rodrigo Arias 2023-01-16 15:45:54 +01:00 committed by Rodrigo Arias Mallo
parent d394bb88c3
commit b3b1164fed
5 changed files with 301 additions and 83 deletions

View File

@ -14,6 +14,7 @@ add_library(chan STATIC
chan.c
bay.c
mux.c
prv.c
)
#add_library(emu STATIC

View File

@ -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 <stdio.h>
#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 <stdio.h>
#include <errno.h>
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;
}
void
prv_fix_header(FILE *f, uint64_t duration, int nrows)
{
/* 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);
/* 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;
}
static int
cb_prv(struct chan *chan, void *ptr)
{
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;
}

View File

@ -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 <stdint.h>
#include "chan.h"
#include "bay.h"
#include "uthash.h"
#include <stdio.h>
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 */

View File

@ -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)

125
test/unit/prv.c Normal file
View File

@ -0,0 +1,125 @@
#define _GNU_SOURCE
#include "emu/prv.h"
#include "common.h"
#include <stdio.h>
#include <unistd.h>
#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;
}