diff --git a/src/emu/CMakeLists.txt b/src/emu/CMakeLists.txt index dce8776..39e5694 100644 --- a/src/emu/CMakeLists.txt +++ b/src/emu/CMakeLists.txt @@ -35,6 +35,7 @@ add_library(emu STATIC thread.c extend.c ovni/probe.c + ovni/create.c ovni/event.c nanos6/probe.c nanos6/connect.c diff --git a/src/emu/extend.h b/src/emu/extend.h index b948d82..eb6333f 100644 --- a/src/emu/extend.h +++ b/src/emu/extend.h @@ -13,4 +13,6 @@ struct extend { void extend_set(struct extend *ext, int id, void *ctx); void *extend_get(struct extend *ext, int id); +#define EXT(st, m) extend_get(&(st)->ext, (m)) + #endif /* EXTEND_H */ diff --git a/src/emu/model.c b/src/emu/model.c index abe86db..d6af3dd 100644 --- a/src/emu/model.c +++ b/src/emu/model.c @@ -31,10 +31,14 @@ model_probe(struct model *model, struct emu *emu) if (spec->probe == NULL) continue; - if (spec->probe(emu) != 0) { + int ret = spec->probe(emu); + if (ret < 0) { err("probe failed for model '%c'", (char) i); return -1; } + + if (ret == 0) + model->enabled[i] = 1; } return 0; } @@ -43,7 +47,7 @@ int model_create(struct model *model, struct emu *emu) { for (int i = 0; i < MAX_MODELS; i++) { - if (!model->registered[i]) + if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; @@ -62,7 +66,7 @@ int model_connect(struct model *model, struct emu *emu) { for (int i = 0; i < MAX_MODELS; i++) { - if (!model->registered[i]) + if (!model->enabled[i]) continue; struct model_spec *spec = model->spec[i]; @@ -81,7 +85,12 @@ int model_event(struct model *model, struct emu *emu, int index) { if (!model->registered[index]) { - err("no model registered for '%c'", (char) index); + err("model not registered for '%c'", (char) index); + return -1; + } + + if (!model->enabled[index]) { + err("model not enabled for '%c'", (char) index); return -1; } diff --git a/src/emu/model.h b/src/emu/model.h index c5d109e..c5333d2 100644 --- a/src/emu/model.h +++ b/src/emu/model.h @@ -21,6 +21,7 @@ struct model_spec { struct model { struct model_spec *spec[MAX_MODELS]; int registered[MAX_MODELS]; + int enabled[MAX_MODELS]; }; void model_init(struct model *model); diff --git a/src/emu/nanos6/probe.c b/src/emu/nanos6/probe.c index c0b5f82..72befa6 100644 --- a/src/emu/nanos6/probe.c +++ b/src/emu/nanos6/probe.c @@ -13,7 +13,7 @@ int nanos6_probe(struct emu *emu) { if (emu->system.nthreads == 0) - return -1; + return 1; return 0; } diff --git a/src/emu/ovni/connect.c b/src/emu/ovni/connect.c new file mode 100644 index 0000000..166ae13 --- /dev/null +++ b/src/emu/ovni/connect.c @@ -0,0 +1,181 @@ +#include "ovni_priv.h" + +static const int th_type[] = { + [CH_FLUSH] = 7, +}; + +static const int *cpu_type = th_type; + + +static int +connect_thread_mux(struct emu *emu, struct thread *thread) +{ + struct ovni_thread *th = extend_get(&thread->ext, 'O'); + for (int i = 0; i < CH_MAX; i++) { + struct chan *inp = &th->ch[i]; + struct chan *sel = &thread->chan[TH_CHAN_STATE]; + struct mux *mux_run = &th->mux_run[i]; + mux_select_func_t selrun = thread_select_running; + if (mux_init(mux_run, &emu->bay, sel, &th->ch_run[i], selrun) != 0) { + err("mux_init failed"); + return -1; + } + + if (mux_add_input(mux_run, value_int64(0), inp) != 0) { + err("mux_add_input failed"); + return -1; + } + } + + return 0; +} + +static int +connect_thread_prv(struct emu *emu, struct thread *thread, struct prv *prv) +{ + struct ovni_thread *th = extend_get(&thread->ext, 'O'); + for (int i = 0; i < CH_MAX; i++) { + struct chan *out = th->ch_out[i]; + long type = th_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 +add_inputs_cpu_mux(struct emu *emu, struct mux *mux, int i) +{ + for (struct thread *t = emu->system.threads; t; t = t->gnext) { + struct ovni_thread *th = extend_get(&t->ext, 'O'); + struct chan *inp = &th->ch_run[i]; + if (mux_add_input(mux, value_int64(t->gindex), inp) != 0) { + err("mux_add_input failed"); + return -1; + } + } + + return 0; +} + +static int +connect_cpu_mux(struct emu *emu, struct cpu *scpu) +{ + struct ovni_cpu *cpu = extend_get(&scpu->ext, 'O'); + for (int i = 0; i < CH_MAX; i++) { + struct mux *mux = &cpu->mux[i]; + struct chan *out = &cpu->ch[i]; + struct chan *sel = &scpu->chan[CPU_CHAN_THRUN]; + + if (mux_init(mux, &emu->bay, sel, out, NULL) != 0) { + err("mux_init failed"); + return -1; + } + + if (add_inputs_cpu_mux(emu, mux, i) != 0) { + err("add_inputs_cpu_mux failed"); + return -1; + } + } + + return 0; +} + +static int +connect_threads(struct emu *emu) +{ + struct system *sys = &emu->system; + + /* threads */ + for (struct thread *t = sys->threads; t; t = t->gnext) { + if (connect_thread_mux(emu, t) != 0) { + err("connect_thread_mux failed"); + return -1; + } + } + + /* Get thread PRV */ + struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread"); + if (pvt == NULL) { + err("cannot find thread pvt"); + return -1; + } + 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; + } + } + + return 0; +} + +static int +connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv) +{ + struct ovni_cpu *cpu = extend_get(&scpu->ext, 'O'); + for (int i = 0; i < CH_MAX; i++) { + struct chan *out = &cpu->ch[i]; + long type = cpu_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_cpus(struct emu *emu) +{ + struct system *sys = &emu->system; + + /* cpus */ + for (struct cpu *c = sys->cpus; c; c = c->next) { + if (connect_cpu_mux(emu, c) != 0) { + err("connect_cpu_mux failed"); + return -1; + } + } + + /* Get cpu PRV */ + struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu"); + if (pvt == NULL) { + err("cannot find cpu pvt"); + return -1; + } + 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; + } + } + + return 0; +} + +int +ovni_connect(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; +} diff --git a/src/emu/ovni/create.c b/src/emu/ovni/create.c new file mode 100644 index 0000000..bd0e6f8 --- /dev/null +++ b/src/emu/ovni/create.c @@ -0,0 +1,90 @@ +#include "ovni_priv.h" + +static const char chan_fmt_cpu[] = "ovni.cpu%ld.%s"; +static const char chan_fmt_th_raw[] = "ovni.thread%ld.%s.raw"; +static const char chan_fmt_th_run[] = "ovni.thread%ld.%s.run"; + +static const char *chan_name[] = { + [CH_FLUSH] = "flush", +}; + +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]; + chan_init(c, CHAN_SINGLE, fmt, gindex, chan_name[i]); + + if (bay_register(bay, c) != 0) { + err("bay_register failed"); + return -1; + } + } + + return 0; +} + +static int +init_cpu(struct bay *bay, struct cpu *syscpu) +{ + struct ovni_cpu *cpu = calloc(1, sizeof(struct ovni_cpu)); + if (cpu == NULL) { + err("calloc failed:"); + return -1; + } + + if (init_chans(bay, cpu->ch, chan_fmt_cpu, syscpu->gindex) != 0) { + err("init_chans failed"); + return -1; + } + + extend_set(&syscpu->ext, 'O', cpu); + return 0; +} + +static int +init_thread(struct bay *bay, struct thread *systh) +{ + struct ovni_thread *th = calloc(1, sizeof(struct ovni_thread)); + if (th == NULL) { + err("calloc failed:"); + return -1; + } + + if (init_chans(bay, th->ch, chan_fmt_th_raw, systh->gindex) != 0) { + err("init_chans failed"); + return -1; + } + + if (init_chans(bay, th->ch_run, chan_fmt_th_run, systh->gindex) != 0) { + err("init_chans failed"); + return -1; + } + + extend_set(&systh->ext, 'O', th); + + return 0; +} + +int +ovni_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; + } + } + + return 0; +} diff --git a/src/emu/ovni/event.c b/src/emu/ovni/event.c index 04467b6..ca25fd5 100644 --- a/src/emu/ovni/event.c +++ b/src/emu/ovni/event.c @@ -301,80 +301,83 @@ pre_affinity(struct emu *emu) default: err("unknown affinity event value %c\n", emu->ev->v); -// return -1 + return -1; } return 0; } -//static int -//compare_int64(const void *a, const void *b) -//{ -// int64_t aa = *(const int64_t *) a; -// int64_t bb = *(const int64_t *) b; -// -// if (aa < bb) -// return -1; -// else if (aa > bb) -// return +1; -// else -// return 0; -//} +static int +compare_int64(const void *a, const void *b) +{ + int64_t aa = *(const int64_t *) a; + int64_t bb = *(const int64_t *) b; -//static int -//pre_burst(struct emu *emu) -//{ -// struct thread *th = emu->thread; -// -// if (th->nbursts >= MAX_BURSTS) { -// err("too many bursts"); -// return -1; -// } -// -// th->burst_time[th->nbursts++] = emu->delta_time; -// if (th->nbursts == MAX_BURSTS) { -// int n = MAX_BURSTS - 1; -// int64_t deltas[MAX_BURSTS - 1]; -// for (int i = 0; i < n; i++) { -// deltas[i] = th->burst_time[i + 1] - th->burst_time[i]; -// } -// -// qsort(deltas, n, sizeof(int64_t), compare_int64); -// -// double avg = 0.0; -// double maxdelta = 0; -// for (int i = 0; i < n; i++) { -// if (deltas[i] > maxdelta) -// maxdelta = deltas[i]; -// avg += deltas[i]; -// } -// -// avg /= (double) n; -// double median = deltas[n / 2]; -// -// err("%s burst stats: median %.0f ns, avg %.1f ns, max %.0f ns\n", -// emu->cur_loom->dname, median, avg, maxdelta); -// -// th->nbursts = 0; -// } -//} + if (aa < bb) + return -1; + else if (aa > bb) + return +1; + else + return 0; +} + +static int +pre_burst(struct emu *emu) +{ + struct ovni_thread *th = EXT(emu->thread, 'O'); + + if (th->nbursts >= MAX_BURSTS) { + err("too many bursts"); + return -1; + } + + th->burst_time[th->nbursts++] = emu->ev->dclock; + + if (th->nbursts != MAX_BURSTS) + return 0; + + int n = MAX_BURSTS - 1; + int64_t deltas[MAX_BURSTS - 1]; + for (int i = 0; i < n; i++) + deltas[i] = th->burst_time[i + 1] - th->burst_time[i]; + + qsort(deltas, n, sizeof(int64_t), compare_int64); + + double avg = 0.0; + double maxdelta = 0; + for (int i = 0; i < n; i++) { + if (deltas[i] > maxdelta) + maxdelta = deltas[i]; + avg += deltas[i]; + } + + avg /= (double) n; + double median = deltas[n / 2]; + + err("%s burst stats: median %.0f ns, avg %.1f ns, max %.0f ns\n", + emu->loom->id, median, avg, maxdelta); + + th->nbursts = 0; + + return 0; +} static int pre_flush(struct emu *emu) { - struct thread *th = emu->thread; - struct chan *flush = &th->chan[TH_CHAN_FLUSH]; + struct ovni_thread *th = extend_get(&emu->thread->ext, 'O'); + struct chan *flush = &th->ch[CH_FLUSH]; switch (emu->ev->v) { case '[': - if (chan_push(flush, value_int64(1)) != 0) { - err("chan_push failed"); + if (chan_set(flush, value_int64(1)) != 0) { + err("chan_set failed"); return -1; } break; case ']': - if (chan_pop(flush, value_int64(1)) != 0) { - err("chan_pop failed"); + if (chan_set(flush, value_null()) != 0) { + err("chan_set failed"); return -1; } break; @@ -398,15 +401,15 @@ process_ev(struct emu *emu) return pre_thread(emu); case 'A': return pre_affinity(emu); -// case 'B': -// pre_burst(emu); -// break; + case 'B': + pre_burst(emu); + break; case 'F': return pre_flush(emu); default: err("unknown ovni event category %c\n", emu->ev->c); -// return -1; + return -1; } return 0; diff --git a/src/emu/ovni/ovni_priv.h b/src/emu/ovni/ovni_priv.h index 074753a..2d473f9 100644 --- a/src/emu/ovni/ovni_priv.h +++ b/src/emu/ovni/ovni_priv.h @@ -6,23 +6,37 @@ /* The user-space thread "ovni" execution model tracks the state of processes and * threads running in the CPUs by instrumenting the threads before and after - * they are going to sleep. It jovni provides an approximate view of the real + * they are going to sleep. It just provides an approximate view of the real * execution by the kernel. */ #include "emu.h" #include "chan.h" +#include enum ovni_chan_type { - UST_CHAN_FLUSH = 0, - UST_CHAN_BURST, - UST_CHAN_MAX, + CH_FLUSH = 0, + CH_MAX, }; +#define MAX_BURSTS 100 + struct ovni_thread { - struct chan chan[UST_CHAN_MAX]; + struct chan ch[CH_MAX]; + struct chan ch_run[CH_MAX]; + struct chan mux_run[CH_MAX]; + + /* Burst times */ + int nbursts; + int64_t burst_time[MAX_BURSTS]; +}; + +struct ovni_cpu { + struct chan ch[CH_MAX]; + struct mux mux[CH_MAX]; }; int ovni_probe(struct emu *emu); +int ovni_create(struct emu *emu); int ovni_event(struct emu *emu); #endif /* OVNI_PRIV_H */ diff --git a/src/emu/ovni/probe.c b/src/emu/ovni/probe.c index e7d6580..566901b 100644 --- a/src/emu/ovni/probe.c +++ b/src/emu/ovni/probe.c @@ -6,7 +6,7 @@ struct model_spec model_ovni = { .name = "ovni", .model = 'O', - .create = NULL, + .create = ovni_create, .connect = NULL, .event = ovni_event, .probe = ovni_probe, @@ -16,7 +16,7 @@ int ovni_probe(struct emu *emu) { if (emu->system.nthreads == 0) - return -1; + return 1; return 0; } diff --git a/src/emu/thread.c b/src/emu/thread.c index a8bd0ab..80686d6 100644 --- a/src/emu/thread.c +++ b/src/emu/thread.c @@ -11,18 +11,12 @@ static const char *chan_name[] = { [TH_CHAN_CPU] = "cpu_gindex", [TH_CHAN_TID] = "tid_active", [TH_CHAN_STATE] = "state", - [TH_CHAN_FLUSH] = "flush", -}; - -static const int chan_stack[] = { - [TH_CHAN_FLUSH] = 1, }; static const int chan_type[] = { [TH_CHAN_TID] = 2, [TH_CHAN_STATE] = 4, [TH_CHAN_CPU] = 6, - [TH_CHAN_FLUSH] = 7, }; static int @@ -103,12 +97,7 @@ thread_init_end(struct thread *th) } for (int i = 0; i < TH_CHAN_MAX; i++) { - enum chan_type type = CHAN_SINGLE; - - if (chan_stack[i]) - type = CHAN_STACK; - - chan_init(&th->chan[i], type, + chan_init(&th->chan[i], CHAN_SINGLE, chan_fmt, th->gindex, chan_name[i]); } diff --git a/src/emu/thread.h b/src/emu/thread.h index 4f604b1..9cc7711 100644 --- a/src/emu/thread.h +++ b/src/emu/thread.h @@ -31,7 +31,6 @@ enum thread_chan { TH_CHAN_CPU = 0, TH_CHAN_TID, TH_CHAN_STATE, - TH_CHAN_FLUSH, TH_CHAN_MAX, };