Remove boilerplate from the models
This commit is contained in:
		
							parent
							
								
									819b9aefa7
								
							
						
					
					
						commit
						cd39230089
					
				| @ -18,6 +18,9 @@ add_library(emu STATIC | ||||
|   emu_args.c | ||||
|   emu_ev.c | ||||
|   model.c | ||||
|   model_cpu.c | ||||
|   model_thread.c | ||||
|   model_pvt.c | ||||
|   models.c | ||||
|   player.c | ||||
|   stream.c | ||||
| @ -40,12 +43,8 @@ add_library(emu STATIC | ||||
|   ovni/probe.c | ||||
|   ovni/create.c | ||||
|   ovni/event.c | ||||
|   nanos6/probe.c | ||||
|   nanos6/connect.c | ||||
|   nanos6/create.c | ||||
|   nanos6/setup.c | ||||
|   nanos6/event.c | ||||
|   nanos6/pvt.c | ||||
|   nanos6/finish.c | ||||
|   nosv/probe.c | ||||
|   nosv/connect.c | ||||
|   nosv/create.c | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| #include "emu_hook.h" | ||||
| 
 | ||||
| struct model_spec { | ||||
| 	char *name; | ||||
| 	const char *name; | ||||
| 	int model; | ||||
| 	char *depends; | ||||
| 	emu_hook_t *probe; | ||||
|  | ||||
							
								
								
									
										18
									
								
								src/emu/model_chan.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/emu/model_chan.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| /* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
 | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #ifndef MODEL_CHAN_H | ||||
| #define MODEL_CHAN_H | ||||
| 
 | ||||
| #include "model_pvt.h" | ||||
| 
 | ||||
| struct model_chan_spec { | ||||
| 	int nch; | ||||
| 	const char *prefix; | ||||
| 	const char **ch_names; | ||||
| 	const int *ch_stack; | ||||
| 	const struct model_pvt_spec *pvt; | ||||
| 	const int *track; | ||||
| }; | ||||
| 
 | ||||
| #endif /* MODEL_CHAN_H */ | ||||
							
								
								
									
										135
									
								
								src/emu/model_cpu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/emu/model_cpu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| #include "model_cpu.h" | ||||
| 
 | ||||
| #include "model_thread.h" | ||||
| 
 | ||||
| static struct model_cpu * | ||||
| get_model_cpu(struct cpu *cpu, int id) | ||||
| { | ||||
| 	return EXT(cpu, id); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_chan(struct model_cpu *cpu, const struct model_chan_spec *spec, int64_t gindex) | ||||
| { | ||||
| 	cpu->track = calloc(spec->nch, sizeof(struct track)); | ||||
| 	if (cpu->track == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	for (int i = 0; i < spec->nch; i++) { | ||||
| 		struct track *track = &cpu->track[i]; | ||||
| 
 | ||||
| 		const char *name = cpu->spec->model->name; | ||||
| 		const char *ch_name = spec->ch_names[i]; | ||||
| 
 | ||||
| 		if (track_init(track, cpu->bay, TRACK_TYPE_TH, "%s.cpu%ld.%s", | ||||
| 					name, gindex, ch_name) != 0) { | ||||
| 			err("track_init failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_cpu(struct cpu *syscpu, struct bay *bay, const struct model_cpu_spec *spec) | ||||
| { | ||||
| 	/* The first member must be a struct model_cpu */ | ||||
| 	struct model_cpu *cpu = calloc(1, spec->size); | ||||
| 	if (cpu == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	cpu->spec = spec; | ||||
| 	cpu->bay = bay; | ||||
| 
 | ||||
| 	if (init_chan(cpu, spec->chan, syscpu->gindex) != 0) { | ||||
| 		err("init_chan failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	extend_set(&syscpu->ext, spec->model->model, cpu); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_cpu_create(struct emu *emu, const struct model_cpu_spec *spec) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	struct bay *bay = &emu->bay; | ||||
| 
 | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (init_cpu(c, bay, spec) != 0) { | ||||
| 			err("init_cpu failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_cpu(struct emu *emu, struct cpu *scpu, int id) | ||||
| { | ||||
| 	struct model_cpu *cpu = get_model_cpu(scpu, id); | ||||
| 	const struct model_chan_spec *chan_spec = cpu->spec->chan; | ||||
| 
 | ||||
| 	for (int i = 0; i < chan_spec->nch; i++) { | ||||
| 		struct track *track = &cpu->track[i]; | ||||
| 
 | ||||
| 		/* Choose select CPU channel based on tracking mode (only
 | ||||
| 		 * TRACK_TH_RUN allowed, as active may cause collisions) */ | ||||
| 		int mode = chan_spec->track[i]; | ||||
| 		struct chan *sel = cpu_get_th_chan(scpu, mode); | ||||
| 		if (track_set_select(track, mode, sel, NULL) != 0) { | ||||
| 			err("track_select failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Add each thread as input */ | ||||
| 		for (struct thread *t = emu->system.threads; t; t = t->gnext) { | ||||
| 			struct model_thread *th = EXT(t, id); | ||||
| 
 | ||||
| 			/* Choose input channel from the thread output channels
 | ||||
| 			 * based on CPU tracking mode */ | ||||
| 			struct value key = value_int64(t->gindex); | ||||
| 			struct chan *inp = track_get_output(&th->track[i], mode); | ||||
| 
 | ||||
| 			if (track_add_input(track, mode, key, inp) != 0) { | ||||
| 				err("track_add_input failed"); | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Set the PRV output */ | ||||
| 		track_set_default(track, mode); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_cpu_connect(struct emu *emu, const struct model_cpu_spec *spec) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	int id = spec->model->model; | ||||
| 
 | ||||
| 	/* Connect track channels */ | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (connect_cpu(emu, c, id) != 0) { | ||||
| 			err("connect_cpu failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect channels to Paraver trace */ | ||||
| 	if (model_pvt_connect_cpu(emu, spec) != 0) { | ||||
| 		err("model_pvt_connect_cpu failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										30
									
								
								src/emu/model_cpu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/emu/model_cpu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| /* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
 | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #ifndef MODEL_CPU_H | ||||
| #define MODEL_CPU_H | ||||
| 
 | ||||
| struct model_cpu_spec; | ||||
| 
 | ||||
| #include "emu.h" | ||||
| #include "bay.h" | ||||
| #include "track.h" | ||||
| #include "model.h" | ||||
| #include "model_chan.h" | ||||
| 
 | ||||
| struct model_cpu_spec { | ||||
| 	size_t size; | ||||
| 	const struct model_chan_spec *chan; | ||||
| 	const struct model_spec *model; | ||||
| }; | ||||
| 
 | ||||
| struct model_cpu { | ||||
| 	const struct model_cpu_spec *spec; | ||||
| 	struct bay *bay; | ||||
| 	struct track *track; | ||||
| }; | ||||
| 
 | ||||
| int model_cpu_create(struct emu *emu, const struct model_cpu_spec *spec); | ||||
| int model_cpu_connect(struct emu *emu, const struct model_cpu_spec *spec); | ||||
| 
 | ||||
| #endif /* MODEL_CPU_H */ | ||||
							
								
								
									
										171
									
								
								src/emu/model_pvt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/emu/model_pvt.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | ||||
| #include "model_pvt.h" | ||||
| 
 | ||||
| static const char *pcf_suffix[TRACK_TH_MAX] = { | ||||
| 	[TRACK_TH_ANY] = "", | ||||
| 	[TRACK_TH_RUN] = "of the RUNNING thread", | ||||
| 	[TRACK_TH_ACT] = "of the ACTIVE thread", | ||||
| }; | ||||
| 
 | ||||
| static int | ||||
| create_values(const struct model_pvt_spec *pvt, | ||||
| 		struct pcf_type *t, int i) | ||||
| { | ||||
| 	const struct pcf_value_label(*q)[] = pvt->label[i]; | ||||
| 
 | ||||
| 	if (q == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	for (const struct pcf_value_label *p = *q; p->label != NULL; p++) | ||||
| 		pcf_add_value(t, p->value, p->label); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| create_type(const struct model_pvt_spec *pvt, | ||||
| 		struct pcf *pcf, int i, int track_mode) | ||||
| { | ||||
| 	long type = pvt->type[i]; | ||||
| 
 | ||||
| 	if (type == -1) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Compute the label by joining the two parts */ | ||||
| 	const char *prefix = pvt->prefix[i]; | ||||
| 	const char *suffix = pcf_suffix[track_mode]; | ||||
| 
 | ||||
| 	char label[MAX_PCF_LABEL]; | ||||
| 	int ret = snprintf(label, MAX_PCF_LABEL, "%s %s", | ||||
| 			prefix, suffix); | ||||
| 
 | ||||
| 	if (ret >= MAX_PCF_LABEL) { | ||||
| 		err("computed type label too long"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	struct pcf_type *pcftype = pcf_add_type(pcf, type, label); | ||||
| 
 | ||||
| 	if (create_values(pvt, pcftype, i) != 0) { | ||||
| 		err("create_values failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_pcf(const struct model_chan_spec *chan, struct pcf *pcf) | ||||
| { | ||||
| 	const struct model_pvt_spec *pvt = chan->pvt; | ||||
| 
 | ||||
| 	/* Create default types and values */ | ||||
| 	for (int i = 0; i < chan->nch; i++) { | ||||
| 		int track_mode = chan->track[i]; | ||||
| 		if (create_type(pvt, pcf, i, track_mode) != 0) { | ||||
| 			err("create_type failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv, int id) | ||||
| { | ||||
| 	struct model_cpu *cpu = EXT(scpu, id); | ||||
| 	const struct model_chan_spec *spec = cpu->spec->chan; | ||||
| 	for (int i = 0; i < spec->nch; i++) { | ||||
| 		struct chan *out = track_get_default(&cpu->track[i]); | ||||
| 		long type = spec->pvt->type[i]; | ||||
| 		long row = scpu->gindex; | ||||
| 		if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { | ||||
| 			err("prv_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_pvt_connect_cpu(struct emu *emu, const struct model_cpu_spec *spec) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	int id = spec->model->model; | ||||
| 
 | ||||
| 	/* Get cpu PRV */ | ||||
| 	struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); | ||||
| 	if (pvt == NULL) { | ||||
| 		err("cannot find cpu pvt"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect CPU channels to PRV */ | ||||
| 	struct prv *prv = pvt_get_prv(pvt); | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (connect_cpu_prv(emu, c, prv, id) != 0) { | ||||
| 			err("connect_cpu_prv failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Init CPU PCF */ | ||||
| 	struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 	if (init_pcf(spec->chan, pcf) != 0) { | ||||
| 		err("init_pcf failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_thread_prv(struct emu *emu, struct thread *sth, struct prv *prv, int id) | ||||
| { | ||||
| 	struct model_thread *th = EXT(sth, id); | ||||
| 	const struct model_chan_spec *spec = th->spec->chan; | ||||
| 	for (int i = 0; i < spec->nch; i++) { | ||||
| 		struct chan *out = track_get_default(&th->track[i]); | ||||
| 		long type = spec->pvt->type[i]; | ||||
| 		long row = sth->gindex; | ||||
| 		if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { | ||||
| 			err("prv_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_pvt_connect_thread(struct emu *emu, const struct model_thread_spec *spec) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	int id = spec->model->model; | ||||
| 
 | ||||
| 	/* Get cpu PRV */ | ||||
| 	struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread"); | ||||
| 	if (pvt == NULL) { | ||||
| 		err("cannot find cpu pvt"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect thread channels to PRV */ | ||||
| 	struct prv *prv = pvt_get_prv(pvt); | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		if (connect_thread_prv(emu, t, prv, id) != 0) { | ||||
| 			err("connect_thread_prv failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Init thread PCF */ | ||||
| 	struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 	if (init_pcf(spec->chan, pcf) != 0) { | ||||
| 		err("init_pcf failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/emu/model_pvt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/emu/model_pvt.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| /* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
 | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #ifndef MODEL_PRV_H | ||||
| #define MODEL_PRV_H | ||||
| 
 | ||||
| #include "emu.h" | ||||
| #include "pv/pcf.h" | ||||
| 
 | ||||
| struct model_pvt_spec { | ||||
| 	const int *type; | ||||
| 	const char **prefix; | ||||
| 	const char **suffix; | ||||
| 	const struct pcf_value_label (**label)[]; | ||||
| }; | ||||
| 
 | ||||
| #include "model_cpu.h" | ||||
| #include "model_thread.h" | ||||
| 
 | ||||
| int model_pvt_connect_cpu(struct emu *emu, const struct model_cpu_spec *spec); | ||||
| int model_pvt_connect_thread(struct emu *emu, const struct model_thread_spec *spec); | ||||
| 
 | ||||
| #endif /* MODEL_PRV_H */ | ||||
							
								
								
									
										112
									
								
								src/emu/model_thread.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/emu/model_thread.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| #include "model_thread.h" | ||||
| 
 | ||||
| #include "model_pvt.h" | ||||
| 
 | ||||
| static int | ||||
| init_chan(struct model_thread *th, const struct model_chan_spec *spec, int64_t gindex) | ||||
| { | ||||
| 	const char *fmt = "%s.thread%ld.%s"; | ||||
| 	const char *prefix = spec->prefix; | ||||
| 
 | ||||
| 	th->ch = calloc(spec->nch, sizeof(struct chan)); | ||||
| 	if (th->ch == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	for (int i = 0; i < spec->nch; i++) { | ||||
| 		struct chan *c = &th->ch[i]; | ||||
| 		int type = spec->ch_stack[i]; | ||||
| 		const char *ch_name = spec->ch_names[i]; | ||||
| 		chan_init(c, type, fmt, prefix, gindex, ch_name); | ||||
| 
 | ||||
| 		if (bay_register(th->bay, c) != 0) { | ||||
| 			err("bay_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	th->track = calloc(spec->nch, sizeof(struct track)); | ||||
| 	if (th->track == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	for (int i = 0; i < spec->nch; i++) { | ||||
| 		struct track *track = &th->track[i]; | ||||
| 
 | ||||
| 		const char *ch_name = spec->ch_names[i]; | ||||
| 
 | ||||
| 		if (track_init(track, th->bay, TRACK_TYPE_TH, fmt, | ||||
| 					prefix, gindex, ch_name) != 0) { | ||||
| 			err("track_init failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_thread(struct thread *systh, struct bay *bay, const struct model_thread_spec *spec) | ||||
| { | ||||
| 	struct model_thread *th = calloc(1, spec->size); | ||||
| 	if (th == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	th->spec = spec; | ||||
| 	th->bay = bay; | ||||
| 
 | ||||
| 	if (init_chan(th, spec->chan, systh->gindex) != 0) { | ||||
| 		err("init_chan failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	extend_set(&systh->ext, spec->model->model, th); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_thread_create(struct emu *emu, const struct model_thread_spec *spec) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	struct bay *bay = &emu->bay; | ||||
| 
 | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		if (init_thread(t, bay, spec) != 0) { | ||||
| 			err("init_thread failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| model_thread_connect(struct emu *emu, const struct model_thread_spec *spec) | ||||
| { | ||||
| 	int id = spec->model->model; | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		struct model_thread *th = EXT(t, id); | ||||
| 		struct chan *sel = &t->chan[TH_CHAN_STATE]; | ||||
| 		const int *modes = th->spec->chan->track; | ||||
| 		int nch = th->spec->chan->nch; | ||||
| 		if (track_connect_thread(th->track, th->ch, modes, sel, nch) != 0) { | ||||
| 			err("track_thread failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect channels to Paraver trace */ | ||||
| 	if (model_pvt_connect_thread(emu, spec) != 0) { | ||||
| 		err("model_pvt_connect_thread failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/emu/model_thread.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/emu/model_thread.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| /* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
 | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| 
 | ||||
| #ifndef MODEL_THREAD_H | ||||
| #define MODEL_THREAD_H | ||||
| 
 | ||||
| struct model_thread_spec; | ||||
| 
 | ||||
| #include "emu.h" | ||||
| #include "bay.h" | ||||
| #include "track.h" | ||||
| #include "model.h" | ||||
| #include "model_chan.h" | ||||
| 
 | ||||
| struct model_thread_spec { | ||||
| 	size_t size; | ||||
| 	const struct model_chan_spec *chan; | ||||
| 	const struct model_spec *model; | ||||
| }; | ||||
| 
 | ||||
| struct model_thread { | ||||
| 	const struct model_thread_spec *spec; | ||||
| 	struct bay *bay; | ||||
| 	struct chan *ch; | ||||
| 	struct track *track; | ||||
| }; | ||||
| 
 | ||||
| int model_thread_create(struct emu *emu, const struct model_thread_spec *spec); | ||||
| int model_thread_connect(struct emu *emu, const struct model_thread_spec *spec); | ||||
| 
 | ||||
| #endif /* MODEL_THREAD_H */ | ||||
| @ -1,95 +0,0 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| static const int th_track[CH_MAX] = { | ||||
| 	[CH_TASKID]    = TRACK_TH_RUN, | ||||
| 	[CH_TYPE]      = TRACK_TH_RUN, | ||||
| 	[CH_SUBSYSTEM] = TRACK_TH_ACT, | ||||
| 	[CH_RANK]      = TRACK_TH_RUN, | ||||
| 	[CH_THREAD]    = TRACK_TH_ANY, | ||||
| }; | ||||
| 
 | ||||
| static const int cpu_track[CH_MAX] = { | ||||
| 	[CH_TASKID]    = TRACK_TH_RUN, | ||||
| 	[CH_TYPE]      = TRACK_TH_RUN, | ||||
| 	[CH_SUBSYSTEM] = TRACK_TH_RUN, | ||||
| 	[CH_RANK]      = TRACK_TH_RUN, | ||||
| 	[CH_THREAD]    = TRACK_TH_RUN, | ||||
| }; | ||||
| 
 | ||||
| int | ||||
| nanos6_get_track(enum nanos6_chan c, enum nanos6_chan_type type) | ||||
| { | ||||
| 	if (type == CT_TH) | ||||
| 		return th_track[c]; | ||||
| 	else | ||||
| 		return cpu_track[c]; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_cpu(struct emu *emu, struct cpu *scpu) | ||||
| { | ||||
| 	struct nanos6_cpu *cpu = EXT(scpu, '6'); | ||||
| 	for (int i = 0; i < CH_MAX; i++) { | ||||
| 		struct track *track = &cpu->track[i]; | ||||
| 
 | ||||
| 		/* Choose select CPU channel based on tracking mode (only
 | ||||
| 		 * TRACK_TH_RUN allowed, as active may cause collisions) */ | ||||
| 		int mode = nanos6_get_track(i, CT_CPU); | ||||
| 		struct chan *sel = cpu_get_th_chan(scpu, mode); | ||||
| 		if (track_set_select(track, mode, sel, NULL) != 0) { | ||||
| 			err("track_select failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Add each thread as input */ | ||||
| 		for (struct thread *t = emu->system.threads; t; t = t->gnext) { | ||||
| 			struct nanos6_thread *th = EXT(t, '6'); | ||||
| 
 | ||||
| 			/* Choose input channel from the thread output channels
 | ||||
| 			 * based on CPU tracking mode */ | ||||
| 			struct value key = value_int64(t->gindex); | ||||
| 			struct chan *inp = track_get_output(&th->track[i], mode); | ||||
| 
 | ||||
| 			if (track_add_input(track, mode, key, inp) != 0) { | ||||
| 				err("track_add_input failed"); | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Set the PRV output */ | ||||
| 		track_set_default(track, nanos6_get_track(i, CT_CPU)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_connect(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* threads */ | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		struct nanos6_thread *th = EXT(t, '6'); | ||||
| 		struct chan *sel = &t->chan[TH_CHAN_STATE]; | ||||
| 		if (track_connect_thread(th->track, th->ch, th_track, sel, CH_MAX) != 0) { | ||||
| 			err("track_thread failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* cpus */ | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (connect_cpu(emu, c) != 0) { | ||||
| 			err("connect_cpu failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (nanos6_init_pvt(emu) != 0) { | ||||
| 		err("init_pvt failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -1,154 +0,0 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| static const char *chan_name[CH_MAX] = { | ||||
| 	[CH_TASKID]    = "taskid", | ||||
| 	[CH_TYPE]      = "task_type", | ||||
| 	[CH_SUBSYSTEM] = "subsystem", | ||||
| 	[CH_RANK]      = "rank", | ||||
| 	[CH_THREAD]    = "thread_type", | ||||
| }; | ||||
| 
 | ||||
| static const int chan_stack[CH_MAX] = { | ||||
| 	[CH_SUBSYSTEM] = 1, | ||||
| 	[CH_THREAD] = 1, | ||||
| }; | ||||
| 
 | ||||
| static int | ||||
| init_chans(struct bay *bay, struct chan *chans, const char *fmt, int64_t gindex) | ||||
| { | ||||
| 	for (int i = 0; i < CH_MAX; i++) { | ||||
| 		struct chan *c = &chans[i]; | ||||
| 		int type = chan_stack[i] ? CHAN_STACK : CHAN_SINGLE; | ||||
| 		chan_init(c, type, fmt, gindex, chan_name[i]); | ||||
| 
 | ||||
| 		if (bay_register(bay, c) != 0) { | ||||
| 			err("bay_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_tracks(struct bay *bay, struct track *tracks, const char *fmt, int64_t gindex) | ||||
| { | ||||
| 	for (int i = 0; i < CH_MAX; i++) { | ||||
| 		struct track *track = &tracks[i]; | ||||
| 
 | ||||
| 		if (track_init(track, bay, TRACK_TYPE_TH, fmt, gindex, chan_name[i]) != 0) { | ||||
| 			err("track_init failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_cpu(struct bay *bay, struct cpu *syscpu) | ||||
| { | ||||
| 	struct nanos6_cpu *cpu = calloc(1, sizeof(struct nanos6_cpu)); | ||||
| 	if (cpu == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	cpu->track = calloc(CH_MAX, sizeof(struct track)); | ||||
| 	if (cpu->track == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	char *fmt = "nanos6.cpu%ld.%s"; | ||||
| 	if (init_tracks(bay, cpu->track, fmt, syscpu->gindex) != 0) { | ||||
| 		err("init_tracks failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	extend_set(&syscpu->ext, '6', cpu); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_thread(struct bay *bay, struct thread *systh) | ||||
| { | ||||
| 	struct nanos6_thread *th = calloc(1, sizeof(struct nanos6_thread)); | ||||
| 	if (th == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	th->ch = calloc(CH_MAX, sizeof(struct chan)); | ||||
| 	if (th->ch == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	th->track = calloc(CH_MAX, sizeof(struct track)); | ||||
| 	if (th->track == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	char *fmt = "nanos6.thread%ld.%s"; | ||||
| 	if (init_chans(bay, th->ch, fmt, systh->gindex) != 0) { | ||||
| 		err("init_chans failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (init_tracks(bay, th->track, fmt, systh->gindex) != 0) { | ||||
| 		err("init_tracks failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	th->task_stack.thread = systh; | ||||
| 
 | ||||
| 	extend_set(&systh->ext, '6', th); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_proc(struct proc *sysproc) | ||||
| { | ||||
| 	struct nanos6_proc *proc = calloc(1, sizeof(struct nanos6_proc)); | ||||
| 	if (proc == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	extend_set(&sysproc->ext, '6', proc); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_create(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 	struct bay *bay = &emu->bay; | ||||
| 
 | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (init_cpu(bay, c) != 0) { | ||||
| 			err("init_cpu failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		if (init_thread(bay, t) != 0) { | ||||
| 			err("init_thread failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (struct proc *p = sys->procs; p; p = p->gnext) { | ||||
| 		if (init_proc(p) != 0) { | ||||
| 			err("init_proc failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -95,7 +95,7 @@ simple(struct emu *emu) | ||||
| 	int st = entry[2]; | ||||
| 
 | ||||
| 	struct nanos6_thread *th = EXT(emu->thread, '6'); | ||||
| 	struct chan *ch = &th->ch[chind]; | ||||
| 	struct chan *ch = &th->m.ch[chind]; | ||||
| 
 | ||||
| 	if (action == PUSH) { | ||||
| 		return chan_push(ch, value_int64(st)); | ||||
| @ -117,19 +117,19 @@ chan_task_stopped(struct emu *emu) | ||||
| 	struct nanos6_thread *th = EXT(emu->thread, '6'); | ||||
| 
 | ||||
| 	struct value null = value_null(); | ||||
| 	if (chan_set(&th->ch[CH_TASKID], null) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TASKID], null) != 0) { | ||||
| 		err("chan_set taskid failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (chan_set(&th->ch[CH_TYPE], null) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TYPE], null) != 0) { | ||||
| 		err("chan_set type failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	struct proc *proc = emu->proc; | ||||
| 	if (proc->rank >= 0) { | ||||
| 		if (chan_set(&th->ch[CH_RANK], null) != 0) { | ||||
| 		if (chan_set(&th->m.ch[CH_RANK], null) != 0) { | ||||
| 			err("chan_set rank failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| @ -153,17 +153,17 @@ chan_task_running(struct emu *emu, struct task *task) | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (chan_set(&th->ch[CH_TASKID], value_int64(task->id)) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TASKID], value_int64(task->id)) != 0) { | ||||
| 		err("chan_set taskid failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (chan_set(&th->ch[CH_TYPE], value_int64(task->type->gid)) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TYPE], value_int64(task->type->gid)) != 0) { | ||||
| 		err("chan_set type failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (proc->rank >= 0) { | ||||
| 		struct value vrank = value_int64(proc->rank + 1); | ||||
| 		if (chan_set(&th->ch[CH_RANK], vrank) != 0) { | ||||
| 		if (chan_set(&th->m.ch[CH_RANK], vrank) != 0) { | ||||
| 			err("chan_set rank failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| @ -205,14 +205,14 @@ chan_task_switch(struct emu *emu, | ||||
| 
 | ||||
| 	/* No need to change the rank as we will switch to tasks from
 | ||||
| 	 * same thread */ | ||||
| 	if (chan_set(&th->ch[CH_TASKID], value_int64(next->id)) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TASKID], value_int64(next->id)) != 0) { | ||||
| 		err("chan_set taskid failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO: test when switching to another task with the same type. We
 | ||||
| 	 * should emit the same type state value as previous task. */ | ||||
| 	if (chan_set(&th->ch[CH_TYPE], value_int64(next->type->gid)) != 0) { | ||||
| 	if (chan_set(&th->m.ch[CH_TYPE], value_int64(next->type->gid)) != 0) { | ||||
| 		err("chan_set type failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| @ -339,7 +339,7 @@ enforce_task_rules(struct emu *emu, char tr, struct task *next) | ||||
| 
 | ||||
| 	struct nanos6_thread *th = EXT(emu->thread, '6'); | ||||
| 	struct value ss; | ||||
| 	if (chan_read(&th->ch[CH_SUBSYSTEM], &ss) != 0) { | ||||
| 	if (chan_read(&th->m.ch[CH_SUBSYSTEM], &ss) != 0) { | ||||
| 		err("chan_read failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @ -1,44 +0,0 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| static int | ||||
| end_lint(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Ensure we run out of subsystem states */ | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		struct nanos6_thread *th = EXT(t, '6'); | ||||
| 		struct chan *ch = &th->ch[CH_SUBSYSTEM]; | ||||
| 		int stacked = ch->data.stack.n; | ||||
| 		if (stacked > 0) { | ||||
| 			struct value top; | ||||
| 			if (chan_read(ch, &top) != 0) { | ||||
| 				err("chan_read failed for subsystem"); | ||||
| 				return -1; | ||||
| 			} | ||||
| 
 | ||||
| 			err("thread %d ended with %d stacked nanos6 subsystems, top=\"%s\"\n", | ||||
| 					t->tid, stacked, nanos6_ss_name(top.i)); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_finish(struct emu *emu) | ||||
| { | ||||
| 	if (nanos6_finish_pvt(emu) != 0) { | ||||
| 		err("finish_pvt failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* When running in linter mode perform additional checks */ | ||||
| 	if (emu->args.linter_mode && end_lint(emu) != 0) { | ||||
| 		err("end_lint failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -9,15 +9,12 @@ | ||||
| #include "mux.h" | ||||
| #include "task.h" | ||||
| #include "track.h" | ||||
| #include "model_cpu.h" | ||||
| #include "model_thread.h" | ||||
| #include "model_pvt.h" | ||||
| 
 | ||||
| /* Private enums */ | ||||
| 
 | ||||
| enum nanos6_chan_type { | ||||
| 	CT_TH = 0, | ||||
| 	CT_CPU, | ||||
| 	CT_MAX | ||||
| }; | ||||
| 
 | ||||
| enum nanos6_chan { | ||||
| 	CH_TASKID = 0, | ||||
| 	CH_TYPE, | ||||
| @ -68,13 +65,12 @@ enum nanos6_thread_type { | ||||
| }; | ||||
| 
 | ||||
| struct nanos6_thread { | ||||
| 	struct chan *ch; | ||||
| 	struct track *track; | ||||
| 	struct model_thread m; | ||||
| 	struct task_stack task_stack; | ||||
| }; | ||||
| 
 | ||||
| struct nanos6_cpu { | ||||
| 	struct track *track; | ||||
| 	struct model_cpu m; | ||||
| }; | ||||
| 
 | ||||
| struct nanos6_proc { | ||||
| @ -87,9 +83,4 @@ int nanos6_connect(struct emu *emu); | ||||
| int nanos6_event(struct emu *emu); | ||||
| int nanos6_finish(struct emu *emu); | ||||
| 
 | ||||
| int nanos6_init_pvt(struct emu *emu); | ||||
| int nanos6_finish_pvt(struct emu *emu); | ||||
| const char *nanos6_ss_name(int ss); | ||||
| int nanos6_get_track(enum nanos6_chan c, enum nanos6_chan_type type); | ||||
| 
 | ||||
| #endif /* NANOS6_PRIV_H */ | ||||
|  | ||||
| @ -1,20 +0,0 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| struct model_spec model_nanos6 = { | ||||
| 	.name = "nanos6", | ||||
| 	.model = '6', | ||||
| 	.create  = nanos6_create, | ||||
| 	.connect = nanos6_connect, | ||||
| 	.event   = nanos6_event, | ||||
| 	.probe   = nanos6_probe, | ||||
| 	.finish  = nanos6_finish, | ||||
| }; | ||||
| 
 | ||||
| int | ||||
| nanos6_probe(struct emu *emu) | ||||
| { | ||||
| 	if (emu->system.nthreads == 0) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -1,293 +0,0 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| /* TODO: Assign types on runtime and generate configs */ | ||||
| 
 | ||||
| static const char *pvt_name[CT_MAX] = { | ||||
| 	[CT_TH]  = "thread", | ||||
| 	[CT_CPU] = "cpu", | ||||
| }; | ||||
| 
 | ||||
| static const int pvt_type[] = { | ||||
| 	[CH_TASKID]    = 35, | ||||
| 	[CH_TYPE]      = 36, | ||||
| 	[CH_SUBSYSTEM] = 37, | ||||
| 	[CH_RANK]      = 38, | ||||
| 	[CH_THREAD]    = 39, | ||||
| }; | ||||
| 
 | ||||
| static const char *pcf_prefix[CH_MAX] = { | ||||
| 	[CH_TASKID]    = "Nanos6 task ID", | ||||
| 	[CH_TYPE]      = "Nanos6 task type", | ||||
| 	[CH_SUBSYSTEM] = "Nanos6 subsystem", | ||||
| 	[CH_RANK]      = "Nanos6 task MPI rank", | ||||
| 	[CH_THREAD]    = "Nanos6 thread type", | ||||
| }; | ||||
| 
 | ||||
| static const char *pcf_suffix[TRACK_TH_MAX] = { | ||||
| 	[TRACK_TH_ANY] = "", | ||||
| 	[TRACK_TH_RUN] = "of the RUNNING thread", | ||||
| 	[TRACK_TH_ACT] = "of the ACTIVE thread", | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label nanos6_ss_values[] = { | ||||
| 	{ ST_TASK_BODY,        "Task: Running body" }, | ||||
| 	{ ST_TASK_CREATING,    "Task: Creating" }, | ||||
| 	{ ST_TASK_SUBMIT,      "Task: Submitting" }, | ||||
| 	{ ST_TASK_SPAWNING,    "Task: Spawning function" }, | ||||
| 	{ ST_TASK_FOR,         "Task: Running task for" }, | ||||
| 	{ ST_SCHED_SERVING,    "Scheduler: Serving tasks" }, | ||||
| 	{ ST_SCHED_ADDING,     "Scheduler: Adding ready tasks" }, | ||||
| 	{ ST_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, | ||||
| 	{ ST_DEP_REG,          "Dependency: Registering" }, | ||||
| 	{ ST_DEP_UNREG,        "Dependency: Unregistering" }, | ||||
| 	{ ST_BLK_TASKWAIT,     "Blocking: Taskwait" }, | ||||
| 	{ ST_BLK_BLOCKING,     "Blocking: Blocking current task" }, | ||||
| 	{ ST_BLK_UNBLOCKING,   "Blocking: Unblocking remote task" }, | ||||
| 	{ ST_BLK_WAITFOR,      "Blocking: Wait for deadline" }, | ||||
| 	{ ST_HANDLING_TASK,    "Worker: Handling task" }, | ||||
| 	{ ST_WORKER_LOOP,      "Worker: Looking for work" }, | ||||
| 	{ ST_SWITCH_TO,        "Worker: Switching to another thread" }, | ||||
| 	{ ST_MIGRATE,          "Worker: Migrating CPU" }, | ||||
| 	{ ST_SUSPEND,          "Worker: Suspending thread" }, | ||||
| 	{ ST_RESUME,           "Worker: Resuming another thread" }, | ||||
| 	{ ST_ALLOCATING,       "Memory: Allocating" }, | ||||
| 	{ ST_FREEING,          "Memory: Freeing" }, | ||||
| 
 | ||||
| 	{ EV_SCHED_SEND,       "EV Scheduler: Send task" }, | ||||
| 	{ EV_SCHED_RECV,       "EV Scheduler: Recv task" }, | ||||
| 	{ EV_SCHED_SELF,       "EV Scheduler: Self-assign task" }, | ||||
| 	{ EV_CPU_IDLE,         "EV CPU: Becomes idle" }, | ||||
| 	{ EV_CPU_ACTIVE,       "EV CPU: Becomes active" }, | ||||
| 	{ EV_SIGNAL,           "EV Worker: Wakening another thread" }, | ||||
| 	{ -1, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label nanos6_thread_type[] = { | ||||
| 	{ ST_TH_EXTERNAL,   "External" }, | ||||
| 	{ ST_TH_WORKER,     "Worker" }, | ||||
| 	{ ST_TH_LEADER,     "Leader" }, | ||||
| 	{ ST_TH_MAIN,       "Main" }, | ||||
| 	{ -1, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label (*pcf_chan_value_labels[CH_MAX])[] = { | ||||
| 	[CH_SUBSYSTEM] = &nanos6_ss_values, | ||||
| 	[CH_THREAD]    = &nanos6_thread_type, | ||||
| }; | ||||
| 
 | ||||
| /* ------------------------------ pcf ------------------------------ */ | ||||
| 
 | ||||
| static int | ||||
| create_values(struct pcf_type *t, int c) | ||||
| { | ||||
| 	const struct pcf_value_label(*q)[] = pcf_chan_value_labels[c]; | ||||
| 
 | ||||
| 	if (q == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	for (const struct pcf_value_label *p = *q; p->label != NULL; p++) | ||||
| 		pcf_add_value(t, p->value, p->label); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| create_type(struct pcf *pcf, enum nanos6_chan c, enum nanos6_chan_type ct) | ||||
| { | ||||
| 	long type = pvt_type[c]; | ||||
| 
 | ||||
| 	if (type == -1) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Compute the label by joining the two parts */ | ||||
| 	const char *prefix = pcf_prefix[c]; | ||||
| 	int track_mode = nanos6_get_track(c, ct); | ||||
| 	const char *suffix = pcf_suffix[track_mode]; | ||||
| 
 | ||||
| 	char label[MAX_PCF_LABEL]; | ||||
| 	int ret = snprintf(label, MAX_PCF_LABEL, "%s %s", | ||||
| 			prefix, suffix); | ||||
| 
 | ||||
| 	if (ret >= MAX_PCF_LABEL) { | ||||
| 		err("computed type label too long"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	struct pcf_type *pcftype = pcf_add_type(pcf, type, label); | ||||
| 
 | ||||
| 	return create_values(pcftype, c); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_pcf(struct pcf *pcf, enum nanos6_chan_type ct) | ||||
| { | ||||
| 	/* Create default types and values */ | ||||
| 	for (enum nanos6_chan c = 0; c < CH_MAX; c++) { | ||||
| 		if (create_type(pcf, c, ct) != 0) { | ||||
| 			err("create_type failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* ------------------------------ prv ------------------------------ */ | ||||
| 
 | ||||
| static int | ||||
| connect_thread_prv(struct emu *emu, struct thread *thread, struct prv *prv) | ||||
| { | ||||
| 	struct nanos6_thread *th = EXT(thread, '6'); | ||||
| 	for (int i = 0; i < CH_MAX; i++) { | ||||
| 		struct chan *out = track_get_default(&th->track[i]); | ||||
| 		long type = pvt_type[i]; | ||||
| 		long row = thread->gindex; | ||||
| 		if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { | ||||
| 			err("prv_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv) | ||||
| { | ||||
| 	struct nanos6_cpu *cpu = EXT(scpu, '6'); | ||||
| 	for (int i = 0; i < CH_MAX; i++) { | ||||
| 		struct chan *out = track_get_default(&cpu->track[i]); | ||||
| 		long type = pvt_type[i]; | ||||
| 		long row = scpu->gindex; | ||||
| 		if (prv_register(prv, row, type, &emu->bay, out, PRV_DUP)) { | ||||
| 			err("prv_register failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_threads(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Get thread PRV */ | ||||
| 	struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread"); | ||||
| 	if (pvt == NULL) { | ||||
| 		err("cannot find thread pvt"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect thread channels to PRV */ | ||||
| 	struct prv *prv = pvt_get_prv(pvt); | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		if (connect_thread_prv(emu, t, prv) != 0) { | ||||
| 			err("connect_thread_prv failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Init thread PCF */ | ||||
| 	struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 	if (init_pcf(pcf, CT_TH) != 0) { | ||||
| 		err("init_pcf failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| connect_cpus(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Get cpu PRV */ | ||||
| 	struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); | ||||
| 	if (pvt == NULL) { | ||||
| 		err("cannot find cpu pvt"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Connect CPU channels to PRV */ | ||||
| 	struct prv *prv = pvt_get_prv(pvt); | ||||
| 	for (struct cpu *c = sys->cpus; c; c = c->next) { | ||||
| 		if (connect_cpu_prv(emu, c, prv) != 0) { | ||||
| 			err("connect_cpu_prv failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Init CPU PCF */ | ||||
| 	struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 	if (init_pcf(pcf, CT_CPU) != 0) { | ||||
| 		err("init_pcf failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Connect all outputs to the paraver trace and setup PCF types */ | ||||
| int | ||||
| nanos6_init_pvt(struct emu *emu) | ||||
| { | ||||
| 	if (connect_threads(emu) != 0) { | ||||
| 		err("connect_threads failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (connect_cpus(emu) != 0) { | ||||
| 		err("connect_cpus failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_finish_pvt(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Emit task types for all channel types and processes */ | ||||
| 	for (enum chan_type ct = 0; ct < CHAN_MAXTYPE; ct++) { | ||||
| 		struct pvt *pvt = recorder_find_pvt(&emu->recorder, pvt_name[ct]); | ||||
| 		if (pvt == NULL) { | ||||
| 			err("cannot find pvt with name '%s'", pvt_name[ct]); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 		long typeid = pvt_type[CH_TYPE]; | ||||
| 		struct pcf_type *pcftype = pcf_find_type(pcf, typeid); | ||||
| 
 | ||||
| 		for (struct proc *p = sys->procs; p; p = p->gnext) { | ||||
| 			struct nanos6_proc *nanos6proc = EXT(p, '6'); | ||||
| 			struct task_info *info = &nanos6proc->task_info; | ||||
| 			if (task_create_pcf_types(pcftype, info->types) != 0) { | ||||
| 				err("task_create_pcf_types failed"); | ||||
| 				return -1; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| const char * | ||||
| nanos6_ss_name(int ss) | ||||
| { | ||||
| 	static const char *unknown = "(unknown)"; | ||||
| 	const char *name = unknown; | ||||
| 	const struct pcf_value_label *pv; | ||||
| 	for (pv = &nanos6_ss_values[0]; pv->label; pv++) { | ||||
| 		if (pv->value == ss) { | ||||
| 			name = pv->label; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return name; | ||||
| } | ||||
							
								
								
									
										307
									
								
								src/emu/nanos6/setup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								src/emu/nanos6/setup.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,307 @@ | ||||
| #include "nanos6_priv.h" | ||||
| 
 | ||||
| static const char model_name[] = "nanos6"; | ||||
| static const int model_id = '6'; | ||||
| 
 | ||||
| struct model_spec model_nanos6 = { | ||||
| 	.name = model_name, | ||||
| 	.model = model_id, | ||||
| 	.create  = nanos6_create, | ||||
| 	.connect = nanos6_connect, | ||||
| 	.event   = nanos6_event, | ||||
| 	.probe   = nanos6_probe, | ||||
| 	.finish  = nanos6_finish, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------- channels ------------------ */ | ||||
| 
 | ||||
| static const char *chan_name[CH_MAX] = { | ||||
| 	[CH_TASKID]    = "taskid", | ||||
| 	[CH_TYPE]      = "task_type", | ||||
| 	[CH_SUBSYSTEM] = "subsystem", | ||||
| 	[CH_RANK]      = "rank", | ||||
| 	[CH_THREAD]    = "thread_type", | ||||
| }; | ||||
| 
 | ||||
| static const int chan_stack[CH_MAX] = { | ||||
| 	[CH_SUBSYSTEM] = 1, | ||||
| 	[CH_THREAD] = 1, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------- pvt ------------------ */ | ||||
| 
 | ||||
| static const int pvt_type[] = { | ||||
| 	[CH_TASKID]    = 35, | ||||
| 	[CH_TYPE]      = 36, | ||||
| 	[CH_SUBSYSTEM] = 37, | ||||
| 	[CH_RANK]      = 38, | ||||
| 	[CH_THREAD]    = 39, | ||||
| }; | ||||
| 
 | ||||
| static const char *pcf_prefix[CH_MAX] = { | ||||
| 	[CH_TASKID]    = "Nanos6 task ID", | ||||
| 	[CH_TYPE]      = "Nanos6 task type", | ||||
| 	[CH_SUBSYSTEM] = "Nanos6 subsystem", | ||||
| 	[CH_RANK]      = "Nanos6 task MPI rank", | ||||
| 	[CH_THREAD]    = "Nanos6 thread type", | ||||
| }; | ||||
| 
 | ||||
| static const char *pcf_suffix[TRACK_TH_MAX] = { | ||||
| 	[TRACK_TH_ANY] = "", | ||||
| 	[TRACK_TH_RUN] = "of the RUNNING thread", | ||||
| 	[TRACK_TH_ACT] = "of the ACTIVE thread", | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label nanos6_ss_values[] = { | ||||
| 	{ ST_TASK_BODY,        "Task: Running body" }, | ||||
| 	{ ST_TASK_CREATING,    "Task: Creating" }, | ||||
| 	{ ST_TASK_SUBMIT,      "Task: Submitting" }, | ||||
| 	{ ST_TASK_SPAWNING,    "Task: Spawning function" }, | ||||
| 	{ ST_TASK_FOR,         "Task: Running task for" }, | ||||
| 	{ ST_SCHED_SERVING,    "Scheduler: Serving tasks" }, | ||||
| 	{ ST_SCHED_ADDING,     "Scheduler: Adding ready tasks" }, | ||||
| 	{ ST_SCHED_PROCESSING, "Scheduler: Processing ready tasks" }, | ||||
| 	{ ST_DEP_REG,          "Dependency: Registering" }, | ||||
| 	{ ST_DEP_UNREG,        "Dependency: Unregistering" }, | ||||
| 	{ ST_BLK_TASKWAIT,     "Blocking: Taskwait" }, | ||||
| 	{ ST_BLK_BLOCKING,     "Blocking: Blocking current task" }, | ||||
| 	{ ST_BLK_UNBLOCKING,   "Blocking: Unblocking remote task" }, | ||||
| 	{ ST_BLK_WAITFOR,      "Blocking: Wait for deadline" }, | ||||
| 	{ ST_HANDLING_TASK,    "Worker: Handling task" }, | ||||
| 	{ ST_WORKER_LOOP,      "Worker: Looking for work" }, | ||||
| 	{ ST_SWITCH_TO,        "Worker: Switching to another thread" }, | ||||
| 	{ ST_MIGRATE,          "Worker: Migrating CPU" }, | ||||
| 	{ ST_SUSPEND,          "Worker: Suspending thread" }, | ||||
| 	{ ST_RESUME,           "Worker: Resuming another thread" }, | ||||
| 	{ ST_ALLOCATING,       "Memory: Allocating" }, | ||||
| 	{ ST_FREEING,          "Memory: Freeing" }, | ||||
| 
 | ||||
| 	{ EV_SCHED_SEND,       "EV Scheduler: Send task" }, | ||||
| 	{ EV_SCHED_RECV,       "EV Scheduler: Recv task" }, | ||||
| 	{ EV_SCHED_SELF,       "EV Scheduler: Self-assign task" }, | ||||
| 	{ EV_CPU_IDLE,         "EV CPU: Becomes idle" }, | ||||
| 	{ EV_CPU_ACTIVE,       "EV CPU: Becomes active" }, | ||||
| 	{ EV_SIGNAL,           "EV Worker: Waking another thread" }, | ||||
| 	{ -1, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label nanos6_thread_type[] = { | ||||
| 	{ ST_TH_EXTERNAL,   "External" }, | ||||
| 	{ ST_TH_WORKER,     "Worker" }, | ||||
| 	{ ST_TH_LEADER,     "Leader" }, | ||||
| 	{ ST_TH_MAIN,       "Main" }, | ||||
| 	{ -1, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pcf_value_label (*pcf_labels[CH_MAX])[] = { | ||||
| 	[CH_SUBSYSTEM] = &nanos6_ss_values, | ||||
| 	[CH_THREAD]    = &nanos6_thread_type, | ||||
| }; | ||||
| 
 | ||||
| static const struct model_pvt_spec pvt_spec = { | ||||
| 	.type = pvt_type, | ||||
| 	.prefix = pcf_prefix, | ||||
| 	.suffix = pcf_suffix, | ||||
| 	.label = pcf_labels, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------- tracking ------------------ */ | ||||
| 
 | ||||
| static const int th_track[CH_MAX] = { | ||||
| 	[CH_TASKID]    = TRACK_TH_RUN, | ||||
| 	[CH_TYPE]      = TRACK_TH_RUN, | ||||
| 	[CH_SUBSYSTEM] = TRACK_TH_ACT, | ||||
| 	[CH_RANK]      = TRACK_TH_RUN, | ||||
| 	[CH_THREAD]    = TRACK_TH_ANY, | ||||
| }; | ||||
| 
 | ||||
| static const int cpu_track[CH_MAX] = { | ||||
| 	[CH_TASKID]    = TRACK_TH_RUN, | ||||
| 	[CH_TYPE]      = TRACK_TH_RUN, | ||||
| 	[CH_SUBSYSTEM] = TRACK_TH_RUN, | ||||
| 	[CH_RANK]      = TRACK_TH_RUN, | ||||
| 	[CH_THREAD]    = TRACK_TH_RUN, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------- chan_spec ------------------ */ | ||||
| 
 | ||||
| static const struct model_chan_spec th_chan = { | ||||
| 	.nch = CH_MAX, | ||||
| 	.prefix = model_name, | ||||
| 	.ch_names = chan_name, | ||||
| 	.ch_stack = chan_stack, | ||||
| 	.pvt = &pvt_spec, | ||||
| 	.track = th_track, | ||||
| }; | ||||
| 
 | ||||
| static const struct model_chan_spec cpu_chan = { | ||||
| 	.nch = CH_MAX, | ||||
| 	.prefix = model_name, | ||||
| 	.ch_names = chan_name, | ||||
| 	.ch_stack = chan_stack, | ||||
| 	.pvt = &pvt_spec, | ||||
| 	.track = cpu_track, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------- models ------------------ */ | ||||
| 
 | ||||
| static const struct model_cpu_spec cpu_spec = { | ||||
| 	.size = sizeof(struct nanos6_cpu), | ||||
| 	.chan = &cpu_chan, | ||||
| 	.model = &model_nanos6, | ||||
| }; | ||||
| 
 | ||||
| static const struct model_thread_spec th_spec = { | ||||
| 	.size = sizeof(struct nanos6_thread), | ||||
| 	.chan = &th_chan, | ||||
| 	.model = &model_nanos6, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------------------------------------------- */ | ||||
| 
 | ||||
| int | ||||
| nanos6_probe(struct emu *emu) | ||||
| { | ||||
| 	if (emu->system.nthreads == 0) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_proc(struct proc *sysproc) | ||||
| { | ||||
| 	struct nanos6_proc *proc = calloc(1, sizeof(struct nanos6_proc)); | ||||
| 	if (proc == NULL) { | ||||
| 		err("calloc failed:"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	extend_set(&sysproc->ext, model_id, proc); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_create(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	if (model_thread_create(emu, &th_spec) != 0) { | ||||
| 		err("model_thread_init failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (model_cpu_create(emu, &cpu_spec) != 0) { | ||||
| 		err("model_cpu_init failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Init task stack thread pointer */ | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		struct nanos6_thread *th = EXT(t, model_id); | ||||
| 		th->task_stack.thread = t; | ||||
| 	} | ||||
| 
 | ||||
| 	for (struct proc *p = sys->procs; p; p = p->gnext) { | ||||
| 		if (init_proc(p) != 0) { | ||||
| 			err("init_proc failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_connect(struct emu *emu) | ||||
| { | ||||
| 	if (model_thread_connect(emu, &th_spec) != 0) { | ||||
| 		err("model_thread_connect failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (model_cpu_connect(emu, &cpu_spec) != 0) { | ||||
| 		err("model_cpu_connect failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* TODO: Automatically check all stack channels at the end */ | ||||
| static int | ||||
| end_lint(struct emu *emu) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Ensure we run out of subsystem states */ | ||||
| 	for (struct thread *t = sys->threads; t; t = t->gnext) { | ||||
| 		struct nanos6_thread *th = EXT(t, model_id); | ||||
| 		struct chan *ch = &th->m.ch[CH_SUBSYSTEM]; | ||||
| 		int stacked = ch->data.stack.n; | ||||
| 		if (stacked > 0) { | ||||
| 			struct value top; | ||||
| 			if (chan_read(ch, &top) != 0) { | ||||
| 				err("chan_read failed for subsystem"); | ||||
| 				return -1; | ||||
| 			} | ||||
| 
 | ||||
| 			err("thread %d ended with %d stacked nanos6 subsystems\n", | ||||
| 					t->tid, stacked); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| finish_pvt(struct emu *emu, const char *name) | ||||
| { | ||||
| 	struct system *sys = &emu->system; | ||||
| 
 | ||||
| 	/* Emit task types for all channel types and processes */ | ||||
| 	struct pvt *pvt = recorder_find_pvt(&emu->recorder, name); | ||||
| 	if (pvt == NULL) { | ||||
| 		err("cannot find pvt with name '%s'", name); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	struct pcf *pcf = pvt_get_pcf(pvt); | ||||
| 	long typeid = pvt_type[CH_TYPE]; | ||||
| 	struct pcf_type *pcftype = pcf_find_type(pcf, typeid); | ||||
| 
 | ||||
| 	for (struct proc *p = sys->procs; p; p = p->gnext) { | ||||
| 		struct nanos6_proc *proc = EXT(p, model_id); | ||||
| 		struct task_info *info = &proc->task_info; | ||||
| 		if (task_create_pcf_types(pcftype, info->types) != 0) { | ||||
| 			err("task_create_pcf_types failed"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| nanos6_finish(struct emu *emu) | ||||
| { | ||||
| 	/* Fill task types */ | ||||
| 	if (finish_pvt(emu, "thread") != 0) { | ||||
| 		err("finish_pvt thread failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (finish_pvt(emu, "cpu") != 0) { | ||||
| 		err("finish_pvt cpu failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* When running in linter mode perform additional checks */ | ||||
| 	if (emu->args.linter_mode && end_lint(emu) != 0) { | ||||
| 		err("end_lint failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -7,4 +7,4 @@ ovni_test(nested-tasks-bad.c SHOULD_FAIL | ||||
| ovni_test(task-types.c MP) | ||||
| ovni_test(blocking.c MP) | ||||
| ovni_test(ss-mismatch.c SHOULD_FAIL | ||||
|   REGEX "thread [0-9]\\+ ended with 1 stacked nanos6 subsystems, top=\"Worker: Looking for work\"") | ||||
|   REGEX "thread [0-9]\\+ ended with 1 stacked nanos6 subsystems") | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user