Implement emulator logic to parse mark metadata

The marks are parsed from the metadata definition, then merged from all
threads and a new channel for each mark type is created. The channel
type is specified using a flag when calling ovni_mark_type(), so the
channels is set to single or stack. For now, only ovni_mark_push() and
ovni_mark_pop() are implemented.
This commit is contained in:
Rodrigo Arias 2024-06-14 13:30:38 +02:00
parent 93ab5a5833
commit 505245d54c
9 changed files with 315 additions and 7 deletions

View File

@ -143,7 +143,10 @@ char *ovni_attr_get_json(const char *key);
void ovni_attr_flush(void); void ovni_attr_flush(void);
/* Mark */ /* Mark */
void ovni_mark_type(int32_t type, const char *title); enum ovni_mark_flags {
OVNI_MARK_STACK = 1, /*< Use push/pop instead of set */
};
void ovni_mark_type(int32_t type, long flags, const char *title);
void ovni_mark_label(int32_t type, int64_t value, const char *label); void ovni_mark_label(int32_t type, int64_t value, const char *label);
void ovni_mark_push(int32_t type, int64_t value); void ovni_mark_push(int32_t type, int64_t value);
void ovni_mark_pop(int32_t type, int64_t value); void ovni_mark_pop(int32_t type, int64_t value);

View File

@ -49,6 +49,7 @@ add_library(emu STATIC
value.c value.c
ovni/event.c ovni/event.c
ovni/setup.c ovni/setup.c
ovni/mark.c
nanos6/setup.c nanos6/setup.c
nanos6/event.c nanos6/event.c
nanos6/breakdown.c nanos6/breakdown.c

View File

@ -16,6 +16,7 @@
#include "proc.h" #include "proc.h"
#include "thread.h" #include "thread.h"
#include "value.h" #include "value.h"
#include "mark.h"
static int static int
pre_thread_execute(struct emu *emu, struct thread *th) pre_thread_execute(struct emu *emu, struct thread *th)
@ -478,8 +479,7 @@ model_ovni_event(struct emu *emu)
/* Ignore sorting events */ /* Ignore sorting events */
return 0; return 0;
case 'M': case 'M':
/* TODO: Ignore mark events for now */ return mark_event(emu);
return 0;
default: default:
err("unknown ovni event category %c", err("unknown ovni event category %c",
emu->ev->c); emu->ev->c);

259
src/emu/ovni/mark.c Normal file
View File

@ -0,0 +1,259 @@
#include "mark.h"
#include "chan.h"
#include "emu.h"
#include "emu_ev.h"
#include "emu_prv.h"
#include "ovni.h"
#include "ovni_priv.h"
#include "parson.h"
#include "pv/pcf.h"
#include "thread.h"
#include "uthash.h"
#include <errno.h>
struct mark_value {
int32_t type;
char title[MAX_PCF_LABEL];
struct chan ch;
struct pcf_type *pcftype;
UT_hash_handle hh; /* Indexed by type */
};
struct mark_chan {
long type;
char title[MAX_PCF_LABEL];
struct chan ch;
UT_hash_handle hh; /* Indexed by type */
};
static int
parse_labels(struct mark_chan *c, JSON_Object *labels)
{
UNUSED(c);
UNUSED(labels);
/* TODO: Implement */
return 0;
}
static struct mark_chan *
find_mark_chan(struct ovni_mark_emu *m, long type)
{
struct mark_chan *c;
HASH_FIND_LONG(m->chan, &type, c);
return c;
}
static struct mark_chan *
create_mark_chan(struct ovni_mark_emu *m, long type, const char *chan_type, const char *title)
{
struct mark_chan *c = find_mark_chan(m, type);
if (c != NULL) {
err("mark type %d already defined", type);
return NULL;
}
c = calloc(1, sizeof(*c));
if (c == NULL) {
err("calloc failed:");
return NULL;
}
c->type = type;
int len = snprintf(c->title, MAX_PCF_LABEL, "%s", title);
if (len >= MAX_PCF_LABEL) {
err("mark title too long: %s", title);
return NULL;
}
enum chan_type ctype;
if (strcmp(chan_type, "single") == 0) {
ctype = CHAN_SINGLE;
} else if (strcmp(chan_type, "stack") == 0) {
ctype = CHAN_STACK;
} else {
err("chan_type %s not understood", chan_type);
return NULL;
}
chan_init(&c->ch, ctype, "mark%ld", type);
HASH_ADD_LONG(m->chan, type, c);
return c;
}
static int
parse_mark(struct ovni_mark_emu *m, const char *typestr, JSON_Value *markval)
{
errno = 0;
char *endptr = NULL;
long type = strtol(typestr, &endptr, 10);
if (errno != 0 || endptr == typestr || endptr[0] != '\0') {
err("failed to parse type number: %s", typestr);
return -1;
}
if (type < 0 || type >= 100) {
err("mark type should be in [0, 100) range: %ld", type);
return -1;
}
JSON_Object *mark = json_value_get_object(markval);
if (mark == NULL) {
err("json_value_get_object() failed");
return -1;
}
const char *title = json_object_get_string(mark, "title");
if (title == NULL) {
err("json_object_get_string() for title failed");
return -1;
}
const char *chan_type = json_object_get_string(mark, "chan_type");
if (chan_type == NULL) {
err("json_object_get_string() for chan_type failed");
return -1;
}
JSON_Object *labels = json_object_get_object(mark, "labels");
if (labels == NULL) {
err("json_object_get_object() for labels failed");
return -1;
}
struct mark_chan *c = find_mark_chan(m, type);
if (c == NULL) {
c = create_mark_chan(m, type, chan_type, title);
if (c == NULL) {
err("cannot create mark chan");
return -1;
}
} else {
/* It may already exist as defined by other threads, so ensure
* they have the same title. */
if (strcmp(c->title, title) != 0) {
err("mark with type %ld already registered with another title", type);
err(" old: %s", c->title);
err(" new: %s", title);
return -1;
}
}
/* Now populate the mark channel with all value labels */
if (parse_labels(c, labels) != 0) {
err("cannot parse labels");
return -1;
}
return 0;
}
static int
scan_thread(struct ovni_mark_emu *memu, struct thread *t)
{
JSON_Object *obj = json_object_dotget_object(t->meta, "ovni.mark");
/* No marks in this thread */
if (obj == NULL)
return 0;
memu->has_marks = 1;
size_t n = json_object_get_count(obj);
for (size_t i = 0; i < n; i++) {
const char *typestr = json_object_get_name(obj, i);
if (typestr == NULL) {
err("json_object_get_name failed");
return -1;
}
JSON_Value *markval = json_object_get_value_at(obj, i);
if (markval == NULL) {
err("json_object_get_value_at failed");
return -1;
}
if (parse_mark(memu, typestr, markval) != 0) {
err("cannot parse mark");
return -1;
}
}
return 0;
}
/* Scans streams for marks and creates the mark channels */
int
mark_create(struct emu *emu)
{
struct ovni_emu *oemu = EXT(emu, 'O');
struct ovni_mark_emu *memu = &oemu->mark;
memset(memu, 0, sizeof(*memu));
for (struct thread *th = emu->system.threads; th; th = th->gnext) {
if (scan_thread(memu, th) != 0) {
err("scan_thread failed");
return -1;
}
}
return 0;
}
/* Connect the channels to the output PVTs */
int
mark_connect(struct emu *emu)
{
UNUSED(emu);
/* TODO: Implement */
return 0;
}
int
mark_event(struct emu *emu)
{
if (emu->ev->payload_size != 8 + 4) {
err("unexpected payload size %d", emu->ev->payload_size);
return -1;
}
struct ovni_emu *oemu = EXT(emu, 'O');
struct ovni_mark_emu *memu = &oemu->mark;
int64_t value = emu->ev->payload->i64[0];
long type = (long) emu->ev->payload->i32[2]; /* always fits */
struct mark_chan *mc = find_mark_chan(memu, type);
if (mc == NULL) {
err("cannot find mark with type %ld", type);
return -1;
}
/* TODO: Remove stub */
//struct chan *ch = &mc->ch;
switch (emu->ev->v) {
case '[':
//return chan_push(ch, value_int64(value));
info("chan_push(ch, value_int64(%ld))", value);
return 0;
case ']':
//return chan_pop(ch, value_int64(value));
info("chan_pop(ch, value_int64(%ld))", value);
return 0;
default:
err("unknown mark event value %c", emu->ev->v);
return -1;
}
}

19
src/emu/ovni/mark.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MARK_H
#define MARK_H
#include "common.h"
struct emu;
struct mark_chan;
struct ovni_mark_emu {
/* Hash table of channels */
struct mark_chan *chan;
int has_marks;
};
USE_RET int mark_create(struct emu *emu);
USE_RET int mark_connect(struct emu *emu);
USE_RET int mark_event(struct emu *emu);
#endif /* MARK_H */

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) /* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
* SPDX-License-Identifier: GPL-3.0-or-later */ * SPDX-License-Identifier: GPL-3.0-or-later */
#ifndef OVNI_PRIV_H #ifndef OVNI_PRIV_H
@ -10,6 +10,7 @@
* execution by the kernel. */ * execution by the kernel. */
#include "emu.h" #include "emu.h"
#include "mark.h"
#include "model_cpu.h" #include "model_cpu.h"
#include "model_thread.h" #include "model_thread.h"
#include <stdint.h> #include <stdint.h>
@ -39,6 +40,10 @@ struct ovni_cpu {
struct model_cpu m; struct model_cpu m;
}; };
struct ovni_emu {
struct ovni_mark_emu mark;
};
int model_ovni_probe(struct emu *emu); int model_ovni_probe(struct emu *emu);
int model_ovni_create(struct emu *emu); int model_ovni_create(struct emu *emu);
int model_ovni_connect(struct emu *emu); int model_ovni_connect(struct emu *emu);

View File

@ -7,6 +7,7 @@
#include "emu.h" #include "emu.h"
#include "emu_prv.h" #include "emu_prv.h"
#include "ev_spec.h" #include "ev_spec.h"
#include "mark.h"
#include "model.h" #include "model.h"
#include "model_chan.h" #include "model_chan.h"
#include "model_cpu.h" #include "model_cpu.h"
@ -178,6 +179,19 @@ model_ovni_create(struct emu *emu)
return -1; return -1;
} }
struct ovni_emu *oemu = calloc(1, sizeof(*oemu));
if (oemu == NULL) {
err("calloc failed:");
return -1;
}
extend_set(&emu->ext, 'O', oemu);
if (mark_create(emu) != 0) {
err("mark_create failed");
return -1;
}
return 0; return 0;
} }

View File

@ -1067,7 +1067,7 @@ ovni_attr_flush(void)
/** creates a new mark type. */ /** creates a new mark type. */
void void
ovni_mark_type(int32_t type, const char *title) ovni_mark_type(int32_t type, long flags, const char *title)
{ {
if (type < 0 || type >= 100) if (type < 0 || type >= 100)
die("type must be in [0,100) range"); die("type must be in [0,100) range");
@ -1089,7 +1089,14 @@ ovni_mark_type(int32_t type, const char *title)
die("title key too long"); die("title key too long");
if (json_object_dotset_string(meta, key, title) != 0) if (json_object_dotset_string(meta, key, title) != 0)
die("json_object_dotset_string() failed", type); die("json_object_dotset_string() failed for title");
const char *chan_type = flags & OVNI_MARK_STACK ? "stack" : "single";
if (snprintf(key, 128, "ovni.mark.%"PRId32".chan_type", type) >= 128)
die("chan_type key too long");
if (json_object_dotset_string(meta, key, chan_type) != 0)
die("json_object_dotset_string() failed for chan_type");
} }
/** creates a new mark type. */ /** creates a new mark type. */

View File

@ -18,7 +18,7 @@ main(void)
{ {
instr_start(0, 1); instr_start(0, 1);
ovni_mark_type(MARK_COLORS, "Colors"); ovni_mark_type(MARK_COLORS, OVNI_MARK_STACK, "Colors");
if(!ovni_attr_has("ovni.mark.1.title")) if(!ovni_attr_has("ovni.mark.1.title"))
die("missing mark title"); die("missing mark title");