Add emulator player to read events
This commit is contained in:
		
							parent
							
								
									5bd04d8435
								
							
						
					
					
						commit
						5de74f15cb
					
				| @ -17,6 +17,7 @@ add_library(emu STATIC | ||||
|   emu_args.c | ||||
|   emu_stream.c | ||||
|   emu_trace.c | ||||
|   emu_player.c | ||||
|   chan.c | ||||
|   bay.c | ||||
|   mux.c | ||||
|  | ||||
| @ -43,7 +43,13 @@ emu_init(struct emu *emu, int argc, char *argv[]) | ||||
| 
 | ||||
| 	/* Parse the trace and build the emu_system */ | ||||
| 	if (emu_system_init(&emu->system, &emu->args, &emu->trace) != 0) { | ||||
| 		err("emu_init: cannot parse trace '%s'\n", | ||||
| 		err("emu_init: cannot init system for trace '%s'\n", | ||||
| 				emu->args.tracedir); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (emu_player_init(&emu->player, &emu->trace) != 0) { | ||||
| 		err("emu_init: cannot init player for trace '%s'\n", | ||||
| 				emu->args.tracedir); | ||||
| 		return -1; | ||||
| 	} | ||||
| @ -53,3 +59,22 @@ emu_init(struct emu *emu, int argc, char *argv[]) | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| emu_step(struct emu *emu) | ||||
| { | ||||
| 	int ret = emu_player_step(&emu->player); | ||||
| 
 | ||||
| 	/* No more events */ | ||||
| 	if (ret > 0) | ||||
| 		return +1; | ||||
| 
 | ||||
| 	/* Error happened */ | ||||
| 	if (ret < 0) { | ||||
| 		err("emu_step: emu_player_step failed\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Otherwise progress */ | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #include "emu_trace.h" | ||||
| #include "emu_args.h" | ||||
| #include "emu_system.h" | ||||
| #include "emu_player.h" | ||||
| 
 | ||||
| enum error_values { | ||||
| 	ST_BAD = 666, | ||||
| @ -36,12 +37,14 @@ struct emu { | ||||
| 	struct emu_args args; | ||||
| 	struct emu_trace trace; | ||||
| 	struct emu_system system; | ||||
| 	struct emu_player player; | ||||
| 
 | ||||
| 	struct model_spec *model[256]; | ||||
| 	void *model_ctx[256]; | ||||
| }; | ||||
| 
 | ||||
| int emu_init(struct emu *emu, int argc, char *argv[]); | ||||
| int emu_step(struct emu *emu); | ||||
| int emu_model_register(struct emu *emu, struct model_spec *spec, void *ctx); | ||||
| void *emu_model_get_context(struct emu *emu, struct model_spec *spec, int model); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										139
									
								
								src/emu/emu_player.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/emu/emu_player.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | ||||
| #include "emu_player.h" | ||||
| 
 | ||||
| #include "heap.h" | ||||
| #include "utlist.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * heap_node_compare_t - comparison function, returns: | ||||
| 
 | ||||
|  * 	> 0 if a > b | ||||
|  * 	< 0 if a < b | ||||
|  * 	= 0 if a == b | ||||
|  * | ||||
|  * Invert the comparison function to get a min-heap instead | ||||
|  */ | ||||
| static inline int | ||||
| stream_cmp(heap_node_t *a, heap_node_t *b) | ||||
| { | ||||
| 	struct emu_stream *sa, *sb; | ||||
| 
 | ||||
| 	sa = heap_elem(a, struct emu_stream, hh); | ||||
| 	sb = heap_elem(b, struct emu_stream, hh); | ||||
| 
 | ||||
| 	int64_t ca = emu_stream_lastclock(sa); | ||||
| 	int64_t cb = emu_stream_lastclock(sb); | ||||
| 
 | ||||
| 	/* Return the opposite, so we have min-heap */ | ||||
| 	if (ca < cb) | ||||
| 		return +1; | ||||
| 	else if (ca > cb) | ||||
| 		return -1; | ||||
| 	else | ||||
| 		return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| step_stream(struct emu_player *player, struct emu_stream *stream) | ||||
| { | ||||
| 	if (!stream->active) | ||||
| 		return +1; | ||||
| 
 | ||||
| 	int ret = emu_stream_step(stream); | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		err("step_stream: cannot step stream '%s'\n", | ||||
| 				stream->relpath); | ||||
| 		return -1; | ||||
| 	} else if (ret > 0) { | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	heap_insert(&player->heap, &stream->hh, &stream_cmp); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| emu_player_init(struct emu_player *player, struct emu_trace *trace) | ||||
| { | ||||
| 	memset(player, 0, sizeof(struct emu_player)); | ||||
| 
 | ||||
| 	heap_init(&player->heap); | ||||
| 
 | ||||
| 	player->first_event = 1; | ||||
| 	player->stream = NULL; | ||||
| 
 | ||||
| 	/* Load initial streams and events */ | ||||
| 	struct emu_stream *stream; | ||||
| 	DL_FOREACH(trace->streams, stream) { | ||||
| 		int ret = step_stream(player, stream); | ||||
| 		if (ret > 0) { | ||||
| 			/* No more events */ | ||||
| 			continue; | ||||
| 		} else if (ret < 0) { | ||||
| 			err("emu_player_init: step_stream failed\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Returns -1 on error, +1 if there are no more events and 0 if next event
 | ||||
|  * loaded properly */ | ||||
| int | ||||
| emu_player_step(struct emu_player *player) | ||||
| { | ||||
| 	/* Add the stream back if still active */ | ||||
| 	if (player->stream != NULL && step_stream(player, player->stream) < 0) { | ||||
| 		err("player_step: step_stream() failed\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Extract the next stream based on the lastclock */ | ||||
| 	heap_node_t *node = heap_pop_max(&player->heap, stream_cmp); | ||||
| 
 | ||||
| 	/* No more streams */ | ||||
| 	if (node == NULL) | ||||
| 		return +1; | ||||
| 
 | ||||
| 	struct emu_stream *stream = heap_elem(node, struct emu_stream, hh); | ||||
| 
 | ||||
| 	if (stream == NULL) { | ||||
| 		err("player_step: heap_elem() returned NULL\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* This can happen if two events are not ordered in the stream, but the
 | ||||
| 	 * emulator picks other events in the middle. Example: | ||||
| 	 * | ||||
| 	 * Stream A: 10 3 ... | ||||
| 	 * Stream B: 5 12 | ||||
| 	 * | ||||
| 	 * emulator output: | ||||
| 	 * 5 | ||||
| 	 * 10 | ||||
| 	 * 3  -> error! | ||||
| 	 * 12 | ||||
| 	 * ... | ||||
| 	 * */ | ||||
| 	int64_t sclock = emu_stream_lastclock(stream); | ||||
| 
 | ||||
| 	if (player->first_event) { | ||||
| 		player->first_event = 0; | ||||
| 		player->firstclock = sclock; | ||||
| 		player->lastclock = sclock; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sclock < player->lastclock) { | ||||
| 		err("emu_player_step: backwards jump in time %ld -> %ld in stream '%s'\n", | ||||
| 				player->lastclock, sclock, stream->relpath); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	player->stream = stream; | ||||
| 	player->lastclock = sclock; | ||||
| 	player->deltaclock = player->lastclock - player->firstclock; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/emu/emu_player.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/emu/emu_player.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| /* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
 | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #ifndef EMU_PLAYER_H | ||||
| #define EMU_PLAYER_H | ||||
| 
 | ||||
| #include "emu_trace.h" | ||||
| 
 | ||||
| #include <linux/limits.h> | ||||
| 
 | ||||
| struct emu_player { | ||||
| 	heap_head_t heap; | ||||
| 	int64_t firstclock; | ||||
| 	int64_t lastclock; | ||||
| 	int64_t deltaclock; | ||||
| 	int first_event; | ||||
| 	struct emu_stream *stream; | ||||
| }; | ||||
| 
 | ||||
| int emu_player_init(struct emu_player *player, struct emu_trace *trace); | ||||
| int emu_player_step(struct emu_player *player); | ||||
| void emu_player_ev(struct emu_player *player, struct ovni_ev *ev); | ||||
| void emu_player_stream(struct emu_player *player, struct emu_stream *stream); | ||||
| 
 | ||||
| #endif /* EMU_PLAYER_H */ | ||||
| @ -14,7 +14,7 @@ check_stream_header(struct emu_stream *stream) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (stream->size < sizeof(struct ovni_stream_header)) { | ||||
| 	if (stream->size < (int64_t) sizeof(struct ovni_stream_header)) { | ||||
| 		err("stream '%s': incomplete stream header\n", | ||||
| 				stream->path); | ||||
| 		return -1; | ||||
| @ -104,11 +104,18 @@ emu_stream_load(struct emu_stream *stream, const char *tracedir, const char *rel | ||||
| 	} | ||||
| 
 | ||||
| 	stream->offset = sizeof(struct ovni_stream_header); | ||||
| 	stream->usize = stream->size - stream->offset; | ||||
| 
 | ||||
| 	if (stream->offset == stream->size) | ||||
| 		stream->active = 0; | ||||
| 	else | ||||
| 	if (stream->offset < stream->size) { | ||||
| 		stream->active = 1; | ||||
| 	} else if (stream->offset == stream->size) { | ||||
| 		err("warning: stream '%s' has zero events\n", stream->relpath); | ||||
| 		stream->active = 0; | ||||
| 	} else { | ||||
| 		err("emu_stream_load: impossible, offset %ld bigger than size %ld\n", | ||||
| 				stream->offset, stream->size); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* No need to keep the fd open */ | ||||
| 	if (close(fd)) { | ||||
| @ -131,8 +138,102 @@ emu_stream_data_get(struct emu_stream *stream) | ||||
| 	return stream->data; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| emu_stream_clkoff(struct emu_stream *stream, int64_t clkoff) | ||||
| int | ||||
| emu_stream_clkoff_set(struct emu_stream *stream, int64_t clkoff) | ||||
| { | ||||
| 	if (stream->cur_ev) { | ||||
| 		die("emu_stream_clkoff_set: cannot set clokoff in started stream '%s'\n", | ||||
| 				stream->relpath); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (stream->clock_offset != 0) { | ||||
| 		err("emu_stream_clkoff_set: stream '%s' already has a clock offset\n", | ||||
| 				stream->relpath); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	stream->clock_offset = clkoff; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct ovni_ev * | ||||
| emu_stream_ev(struct emu_stream *stream) | ||||
| { | ||||
| 	return stream->cur_ev; | ||||
| } | ||||
| 
 | ||||
| int64_t | ||||
| emu_stream_evclock(struct emu_stream *stream, struct ovni_ev *ev) | ||||
| { | ||||
| 	return (int64_t) ovni_ev_get_clock(ev) + stream->clock_offset; | ||||
| } | ||||
| 
 | ||||
| int64_t | ||||
| emu_stream_lastclock(struct emu_stream *stream) | ||||
| { | ||||
| 	return stream->lastclock; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| emu_stream_step(struct emu_stream *stream) | ||||
| { | ||||
| 	if (!stream->active) { | ||||
| 		err("emu_stream_step: stream is inactive, cannot step\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Only step the offset if we have loaded an event */ | ||||
| 	if (stream->cur_ev != NULL) { | ||||
| 		stream->offset += ovni_ev_size(stream->cur_ev); | ||||
| 
 | ||||
| 		/* It cannot pass the size, otherwise we are reading garbage */ | ||||
| 		if (stream->offset > stream->size) { | ||||
| 			err("emu_stream_step: stream offset %ld exceeds size %ld\n", | ||||
| 					stream->offset, stream->size); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* We have reached the end */ | ||||
| 		if (stream->offset == stream->size) { | ||||
| 			stream->active = 0; | ||||
| 			stream->cur_ev = NULL; | ||||
| 			return +1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	stream->cur_ev = (struct ovni_ev *) &stream->buf[stream->offset]; | ||||
| 
 | ||||
| 	/* Ensure the event fits */ | ||||
| 	if (stream->offset + ovni_ev_size(stream->cur_ev) > stream->size) { | ||||
| 		err("emu_stream_step: stream '%s' ends with incomplete event\n", | ||||
| 				stream->relpath); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Ensure the clock grows monotonically */ | ||||
| 	int64_t clock = emu_stream_evclock(stream, stream->cur_ev); | ||||
| 	if (clock < stream->lastclock) { | ||||
| 		err("clock goes backwards %ld -> %ld in stream '%s' at offset %ld\n", | ||||
| 				stream->lastclock, | ||||
| 				clock, | ||||
| 				stream->relpath, | ||||
| 				stream->offset); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	stream->lastclock = clock; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| double | ||||
| emu_stream_progress(struct emu_stream *stream) | ||||
| { | ||||
| 	if (stream->usize == 0) | ||||
| 		return 1.0; | ||||
| 
 | ||||
| 	int64_t uoffset = stream->offset - sizeof(struct ovni_stream_header); | ||||
| 	double prog = (double) uoffset / (double) stream->usize; | ||||
| 	return prog; | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ | ||||
| #ifndef EMU_STREAM_H | ||||
| #define EMU_STREAM_H | ||||
| 
 | ||||
| #include "ovni.h" | ||||
| #include "heap.h" | ||||
| #include <stdint.h> | ||||
| #include <linux/limits.h> | ||||
| @ -15,8 +16,9 @@ struct emu_stream { | ||||
| 	char relpath[PATH_MAX]; /* To tracedir */ | ||||
| 
 | ||||
| 	uint8_t *buf; | ||||
| 	size_t size; | ||||
| 	size_t offset; | ||||
| 	int64_t size; | ||||
| 	int64_t usize; /* Useful size for events */ | ||||
| 	int64_t offset; | ||||
| 
 | ||||
| 	int active; | ||||
| 
 | ||||
| @ -29,13 +31,21 @@ struct emu_stream { | ||||
| 	struct emu_stream *next; | ||||
| 	struct emu_stream *prev; | ||||
| 
 | ||||
| 	struct ovni_ev *cur_ev; | ||||
| 
 | ||||
| 	void *data; /* To hold system details */ | ||||
| }; | ||||
| 
 | ||||
| int emu_stream_load(struct emu_stream *stream, | ||||
| 		const char *tracedir, const char *relpath); | ||||
| 
 | ||||
| void emu_stream_clkoff(struct emu_stream *stream, int64_t clock_offset); | ||||
| int emu_stream_clkoff_set(struct emu_stream *stream, int64_t clock_offset); | ||||
| 
 | ||||
| double emu_stream_progress(struct emu_stream *stream); | ||||
| int emu_stream_step(struct emu_stream *stream); | ||||
| struct ovni_ev *emu_stream_ev(struct emu_stream *stream); | ||||
| int64_t emu_stream_evclock(struct emu_stream *stream, struct ovni_ev *ev); | ||||
| int64_t emu_stream_lastclock(struct emu_stream *stream); | ||||
| 
 | ||||
| void emu_stream_data_set(struct emu_stream *stream, void *data); | ||||
| void *emu_stream_data_get(struct emu_stream *stream); | ||||
|  | ||||
| @ -850,7 +850,11 @@ init_offsets(struct emu_system *sys) | ||||
| 	struct emu_thread *thread; | ||||
| 	DL_FOREACH2(sys->threads, thread, gnext) { | ||||
| 		struct emu_loom *loom = thread->proc->loom; | ||||
| 		emu_stream_clkoff(thread->stream, loom->clock_offset); | ||||
| 		int64_t offset = loom->clock_offset; | ||||
| 		if (emu_stream_clkoff_set(thread->stream, offset) != 0) { | ||||
| 			err("init_offsets: cannot set clock offset\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | ||||
| @ -16,5 +16,14 @@ int main(void) | ||||
| 	if (emu_init(&emu, argc, argv) != 0) | ||||
| 		die("emu_init failed\n"); | ||||
| 
 | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	while ((ret = emu_step(&emu)) == 0) { | ||||
| 		err("event clock %ld\n", emu.player.deltaclock); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret < 0) | ||||
| 		die("emu_step failed\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @ -28,6 +28,9 @@ test_ok(char *fname) | ||||
| 	const char *relpath = &fname[5]; | ||||
| 	if (emu_stream_load(&stream, "/tmp", relpath) != 0) | ||||
| 		die("emu_stream_load failed"); | ||||
| 
 | ||||
| 	if (stream.active) | ||||
| 		die("stream is active\n"); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user