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:
		
							parent
							
								
									93ab5a5833
								
							
						
					
					
						commit
						505245d54c
					
				| @ -143,7 +143,10 @@ char *ovni_attr_get_json(const char *key); | ||||
| void ovni_attr_flush(void); | ||||
| 
 | ||||
| /* 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_push(int32_t type, int64_t value); | ||||
| void ovni_mark_pop(int32_t type, int64_t value); | ||||
|  | ||||
| @ -49,6 +49,7 @@ add_library(emu STATIC | ||||
|   value.c | ||||
|   ovni/event.c | ||||
|   ovni/setup.c | ||||
|   ovni/mark.c | ||||
|   nanos6/setup.c | ||||
|   nanos6/event.c | ||||
|   nanos6/breakdown.c | ||||
|  | ||||
| @ -16,6 +16,7 @@ | ||||
| #include "proc.h" | ||||
| #include "thread.h" | ||||
| #include "value.h" | ||||
| #include "mark.h" | ||||
| 
 | ||||
| static int | ||||
| pre_thread_execute(struct emu *emu, struct thread *th) | ||||
| @ -478,8 +479,7 @@ model_ovni_event(struct emu *emu) | ||||
| 			/* Ignore sorting events */ | ||||
| 			return 0; | ||||
| 		case 'M': | ||||
| 			/* TODO: Ignore mark events for now */ | ||||
| 			return 0; | ||||
| 			return mark_event(emu); | ||||
| 		default: | ||||
| 			err("unknown ovni event category %c", | ||||
| 					emu->ev->c); | ||||
|  | ||||
							
								
								
									
										259
									
								
								src/emu/ovni/mark.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/emu/ovni/mark.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										19
									
								
								src/emu/ovni/mark.h
									
									
									
									
									
										Normal 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 */ | ||||
| @ -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 */ | ||||
| 
 | ||||
| #ifndef OVNI_PRIV_H | ||||
| @ -10,6 +10,7 @@ | ||||
|  * execution by the kernel. */ | ||||
| 
 | ||||
| #include "emu.h" | ||||
| #include "mark.h" | ||||
| #include "model_cpu.h" | ||||
| #include "model_thread.h" | ||||
| #include <stdint.h> | ||||
| @ -39,6 +40,10 @@ struct ovni_cpu { | ||||
| 	struct model_cpu m; | ||||
| }; | ||||
| 
 | ||||
| struct ovni_emu { | ||||
| 	struct ovni_mark_emu mark; | ||||
| }; | ||||
| 
 | ||||
| int model_ovni_probe(struct emu *emu); | ||||
| int model_ovni_create(struct emu *emu); | ||||
| int model_ovni_connect(struct emu *emu); | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "emu.h" | ||||
| #include "emu_prv.h" | ||||
| #include "ev_spec.h" | ||||
| #include "mark.h" | ||||
| #include "model.h" | ||||
| #include "model_chan.h" | ||||
| #include "model_cpu.h" | ||||
| @ -178,6 +179,19 @@ model_ovni_create(struct emu *emu) | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1067,7 +1067,7 @@ ovni_attr_flush(void) | ||||
| 
 | ||||
| /** creates a new mark type. */ | ||||
| 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) | ||||
| 		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"); | ||||
| 
 | ||||
| 	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. */ | ||||
|  | ||||
| @ -18,7 +18,7 @@ main(void) | ||||
| { | ||||
| 	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")) | ||||
| 		die("missing mark title"); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user