398 lines
7.1 KiB
C
398 lines
7.1 KiB
C
#include <assert.h>
|
|
#include "uthash.h"
|
|
|
|
#include "ovni.h"
|
|
#include "ovni_trace.h"
|
|
#include "emu.h"
|
|
#include "prv.h"
|
|
|
|
/* This module manages the nos-v subsystem (ss) events, to track which
|
|
* actions are being performed by each thread at a given time. A stack
|
|
* of states is stored in each thread, so we remember the path of
|
|
* execution. Events such as task received by a thread are emitted as
|
|
* fake events with very short period. */
|
|
|
|
|
|
/* --------------------------- ss helpers ------------------------------- */
|
|
|
|
static void
|
|
ss_init(struct ovni_ethread *t)
|
|
{
|
|
t->nss = 0;
|
|
t->show_ss = 0;
|
|
}
|
|
|
|
static void
|
|
ss_push(struct ovni_ethread *t, int st)
|
|
{
|
|
if(t->nss >= MAX_SS_STACK)
|
|
{
|
|
err("thread %d subsystem stack full\n", t->tid);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
t->ss[t->nss] = st;
|
|
t->nss++;
|
|
}
|
|
|
|
static int
|
|
ss_pop(struct ovni_ethread *t, int expected_st)
|
|
{
|
|
int st;
|
|
|
|
if(t->nss <= 0)
|
|
{
|
|
err("thread %d subsystem stack empty\n", t->tid);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
st = t->ss[t->nss - 1];
|
|
|
|
if(st > 0 && st != expected_st)
|
|
{
|
|
err("thread %d expected subsystem state %d (got %d)\n",
|
|
t->tid, expected_st, st);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
t->nss--;
|
|
|
|
return st;
|
|
}
|
|
|
|
static void
|
|
ss_ev(struct ovni_ethread *th, int ev)
|
|
{
|
|
th->ss_event = ev;
|
|
}
|
|
|
|
static int
|
|
ss_last_st(struct ovni_ethread *th)
|
|
{
|
|
if(th->nss == 0)
|
|
return ST_NULL;
|
|
|
|
return th->ss[th->nss - 1];
|
|
}
|
|
|
|
/* --------------------------- pre ------------------------------- */
|
|
|
|
static void
|
|
pre_sched(struct ovni_emu *emu)
|
|
{
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case 'h':
|
|
ss_push(th, ST_SCHED_HUNGRY);
|
|
break;
|
|
case 'f': /* Fill: no longer hungry */
|
|
ss_pop(th, ST_SCHED_HUNGRY);
|
|
break;
|
|
case '[': /* Server enter */
|
|
ss_push(th, ST_SCHED_SERVING);
|
|
break;
|
|
case ']': /* Server exit */
|
|
ss_pop(th, ST_SCHED_SERVING);
|
|
break;
|
|
case '@': ss_ev(th, EV_SCHED_SELF); break;
|
|
case 'r': ss_ev(th, EV_SCHED_RECV); break;
|
|
case 's': ss_ev(th, EV_SCHED_SEND); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pre_submit(struct ovni_emu *emu)
|
|
{
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case '[': ss_push(th, ST_SCHED_SUBMITTING); break;
|
|
case ']': ss_pop(th, ST_SCHED_SUBMITTING); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pre_memory(struct ovni_emu *emu)
|
|
{
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case '[': ss_push(th, ST_MEM_ALLOCATING); break;
|
|
case ']': ss_pop(th, ST_MEM_ALLOCATING); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Hook for the virtual "thread changed" event */
|
|
static void
|
|
pre_thread_change(struct ovni_emu *emu)
|
|
{
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
|
|
/* Only print the subsystem if the thread is running */
|
|
if(th->state == TH_ST_RUNNING)
|
|
th->show_ss = 1;
|
|
else
|
|
th->show_ss = 0;
|
|
}
|
|
|
|
static void
|
|
pre_thread(struct ovni_emu *emu)
|
|
{
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case 'c': pre_thread_change(emu); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Hook for the virtual "cpu changed" event */
|
|
static void
|
|
pre_cpu_change(struct ovni_emu *emu)
|
|
{
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
|
|
/* Only print the subsystem if the thread is running */
|
|
if(th->state == TH_ST_RUNNING)
|
|
th->show_ss = 1;
|
|
else
|
|
th->show_ss = 0;
|
|
}
|
|
|
|
static void
|
|
pre_cpu(struct ovni_emu *emu)
|
|
{
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case 'c': pre_thread_change(emu); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
hook_pre_nosv_ss(struct ovni_emu *emu)
|
|
{
|
|
switch(emu->cur_ev->header.model)
|
|
{
|
|
/* Listen for virtual events as well */
|
|
case '*':
|
|
switch(emu->cur_ev->header.class)
|
|
{
|
|
case 'H': pre_thread(emu); break;
|
|
case 'C': pre_cpu(emu); break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 'V':
|
|
switch(emu->cur_ev->header.class)
|
|
{
|
|
case 'S': pre_sched(emu); break;
|
|
case 'U': pre_submit(emu); break;
|
|
case 'M': pre_memory(emu); break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* --------------------------- emit ------------------------------- */
|
|
|
|
static void
|
|
emit_thread_state(struct ovni_emu *emu, struct ovni_ethread *th,
|
|
int type, int st)
|
|
{
|
|
int row;
|
|
|
|
row = th->gindex + 1;
|
|
|
|
prv_ev_thread(emu, row, type, st);
|
|
}
|
|
|
|
static void
|
|
emit_cpu_state(struct ovni_emu *emu, struct ovni_ethread *th,
|
|
int type, int st)
|
|
{
|
|
/* Detect multiple threads */
|
|
if(th->cpu && th->cpu->nthreads > 1)
|
|
st = ST_BAD;
|
|
|
|
prv_ev_autocpu(emu, type, st);
|
|
}
|
|
|
|
static void
|
|
emit_thread_event(struct ovni_emu *emu, struct ovni_ethread *th,
|
|
int type, int st)
|
|
{
|
|
int64_t t0, t1;
|
|
int row;
|
|
int prev_st;
|
|
|
|
row = th->gindex + 1;
|
|
|
|
t0 = emu->delta_time - 1;
|
|
t1 = emu->delta_time;
|
|
|
|
prev_st = ss_last_st(th);
|
|
|
|
/* Fake event using 1 nanosecond in the past */
|
|
prv_ev_thread_raw(emu, row, t0, type, st);
|
|
prv_ev_thread_raw(emu, row, t1, type, prev_st);
|
|
}
|
|
|
|
static void
|
|
emit_cpu_event(struct ovni_emu *emu, struct ovni_ethread *th,
|
|
int type, int st)
|
|
{
|
|
int64_t t0, t1;
|
|
int row;
|
|
int prev_st;
|
|
|
|
row = th->gindex + 1;
|
|
|
|
t0 = emu->delta_time - 1;
|
|
t1 = emu->delta_time;
|
|
|
|
prev_st = ss_last_st(th);
|
|
|
|
/* Detect multiple threads */
|
|
if(th->cpu && th->cpu->nthreads > 1)
|
|
{
|
|
st = ST_BAD;
|
|
prev_st = ST_BAD;
|
|
}
|
|
|
|
/* Fake event using 1 nanosecond in the past */
|
|
prv_ev_autocpu_raw(emu, t0, type, st);
|
|
prv_ev_autocpu_raw(emu, t1, type, prev_st);
|
|
}
|
|
|
|
static void
|
|
emit_thread_changed(struct ovni_emu *emu)
|
|
{
|
|
dbg("emit_thread_changed\n")
|
|
}
|
|
|
|
static void
|
|
emit_cpu_changed(struct ovni_emu *emu)
|
|
{
|
|
dbg("emit_cpu_changed\n")
|
|
//int i;
|
|
//struct ovni_loom *loom;
|
|
//struct ovni_cpu *cpu;
|
|
|
|
///* Detect multiple threads */
|
|
//loom = emu->cur_loom;
|
|
|
|
//assert(loom->nupdated_cpus > 0);
|
|
|
|
//for(i=0; i<loom->nupdated_cpus; i++)
|
|
//{
|
|
// cpu = loom->updated_cpu[i];
|
|
|
|
// if(cpu->nthreads > 1)
|
|
// {
|
|
// prv_stream_disable(cpu->ss_stream);
|
|
// }
|
|
//}
|
|
}
|
|
|
|
void
|
|
hook_emit_nosv_ss(struct ovni_emu *emu)
|
|
{
|
|
/* We need to emit events when a thread notifies us that the subsystem
|
|
* has changed, but also when the thread is paused, or scheduled to
|
|
* another CPU, as the CPU view must be updated as well. */
|
|
|
|
switch(emu->cur_ev->header.model)
|
|
{
|
|
/* Listen for virtual events as well */
|
|
case '*':
|
|
switch(emu->cur_ev->header.class)
|
|
{
|
|
case 'H':
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case 'c': emit_thread_changed(emu); break;
|
|
default: break;
|
|
}
|
|
break;
|
|
case 'C':
|
|
switch(emu->cur_ev->header.value)
|
|
{
|
|
case 'c': emit_cpu_changed(emu); break;
|
|
default: break;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
break;
|
|
case 'V':
|
|
switch(emu->cur_ev->header.class)
|
|
{
|
|
case 'S': pre_sched(emu); break;
|
|
case 'U': pre_submit(emu); break;
|
|
case 'M': pre_memory(emu); break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
struct ovni_ethread *th;
|
|
|
|
th = emu->cur_thread;
|
|
|
|
if(th->show_ss == 0)
|
|
{
|
|
emit_thread_state(emu, th, PTT_SUBSYSTEM, ST_NULL);
|
|
return;
|
|
}
|
|
|
|
/* Only emit a state if needed */
|
|
if(th->ss_event != EV_NULL)
|
|
{
|
|
emit_thread_event(emu, th, PTT_SUBSYSTEM,
|
|
th->ss_event);
|
|
emit_cpu_event(emu, th, PTC_SUBSYSTEM,
|
|
th->ss_event);
|
|
|
|
return;
|
|
}
|
|
|
|
emit_thread_state(emu, th, PTT_SUBSYSTEM, ss_last_st(th));
|
|
emit_cpu_state(emu, th, PTC_SUBSYSTEM, ss_last_st(th));
|
|
}
|
|
|
|
/* --------------------------- post ------------------------------- */
|
|
|
|
void
|
|
hook_post_nosv_ss(struct ovni_emu *emu)
|
|
{
|
|
emu->cur_thread->ss_event = EV_NULL;
|
|
}
|