summaryrefslogtreecommitdiff
path: root/emu/port/devprof.c
diff options
context:
space:
mode:
Diffstat (limited to 'emu/port/devprof.c')
-rw-r--r--emu/port/devprof.c831
1 files changed, 831 insertions, 0 deletions
diff --git a/emu/port/devprof.c b/emu/port/devprof.c
new file mode 100644
index 00000000..f3a768c3
--- /dev/null
+++ b/emu/port/devprof.c
@@ -0,0 +1,831 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+#include <isa.h>
+#include "runt.h"
+
+extern Pool* imagmem;
+extern void (*memmonitor)(int, ulong, ulong, ulong);
+
+static void cpxec(Prog *);
+static void memprof(int, void*, ulong);
+static void memprofmi(int, ulong, ulong, ulong);
+
+extern Inst* pc2dispc(Inst*, Module*);
+
+static int interval = 100; /* Sampling interval in milliseconds */
+
+enum
+{
+ HSIZE = 32,
+};
+
+#define HASH(m) ((m)%HSIZE)
+
+/* cope with multiple profilers some day */
+
+typedef struct Record Record;
+struct Record
+{
+ int id;
+ char* name;
+ char* path;
+ Inst* base;
+ int size;
+ /*Module* m; */
+ ulong mtime;
+ Qid qid;
+ Record* hash;
+ Record* link;
+ ulong bucket[1];
+};
+
+struct
+{
+ Lock l;
+ vlong time;
+ Record* hash[HSIZE];
+ Record* list;
+} profile;
+
+typedef struct Pmod Pmod;
+struct Pmod
+{
+ char* name;
+ Pmod* link;
+} *pmods;
+
+#define QSHIFT 4
+#define QID(q) ((ulong)(q).path&0xf)
+#define QPID(pid) ((pid)<<QSHIFT)
+#define PID(q) ((q).vers)
+#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1))
+
+enum
+{
+ Qdir,
+ Qname,
+ Qpath,
+ Qhist,
+ Qpctl,
+ Qctl,
+};
+
+Dirtab profdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "name", {Qname}, 0, 0444,
+ "path", {Qpath}, 0, 0444,
+ "histogram", {Qhist}, 0, 0444,
+ "pctl", {Qpctl}, 0, 0222,
+ "ctl", {Qctl}, 0, 0222,
+};
+
+enum{
+ Pnil, /* null profiler */
+ Psam, /* sampling profiler */
+ Pcov, /* coverage profiler */
+ Pmem, /* heap memory profiler */
+};
+
+enum{
+ Mnone = 0,
+ Mmain = 1,
+ Mheap = 2,
+ Mimage = 4,
+};
+
+static int profiler = Pnil;
+static int mprofiler = Mnone;
+
+static int ids;
+static int samplefn;
+
+static void sampler(void*);
+
+static Record*
+getrec(int id)
+{
+ Record *r;
+
+ for(r = profile.list; r != nil; r = r->link)
+ if(r->id == id)
+ break;
+ return r;
+}
+
+static void
+addpmod(char *m)
+{
+ Pmod *p = malloc(sizeof(Pmod));
+
+ if(p == nil)
+ return;
+ p->name = malloc(strlen(m)+1);
+ if(p->name == nil){
+ free(p);
+ return;
+ }
+ strcpy(p->name, m);
+ p->link = pmods;
+ pmods = p;
+}
+
+static void
+freepmods(void)
+{
+ Pmod *p, *np;
+
+ for(p = pmods; p != nil; p = np){
+ free(p->name);
+ np = p->link;
+ free(p);
+ }
+ pmods = nil;
+}
+
+static int
+inpmods(char *m)
+{
+ Pmod *p;
+
+ for(p = pmods; p != nil; p = p->link)
+ if(strcmp(p->name, m) == 0)
+ return 1;
+ return 0;
+}
+
+static void
+freeprof(void)
+{
+ int i;
+ Record *r, *nr;
+
+ ids = 0;
+ profiler = Pnil;
+ mprofiler = Mnone;
+ freepmods();
+ for(r = profile.list; r != nil; r = nr){
+ free(r->name);
+ free(r->path);
+ nr = r->link;
+ free(r);
+ }
+ profile.list = nil;
+ profile.time = 0;
+ for(i = 0; i < HSIZE; i++)
+ profile.hash[i] = nil;
+}
+
+static int
+profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Qid qid;
+ Record *r;
+ ulong path, perm, len;
+ Dirtab *tab;
+
+ USED(name);
+ USED(d);
+ USED(nd);
+
+ if(s == DEVDOTDOT) {
+ mkqid(&qid, Qdir, 0, QTDIR);
+ devdir(c, qid, "#P", 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if(c->qid.path == Qdir && c->qid.type & QTDIR) {
+ acquire();
+ if(s-- == 0){
+ tab = &profdir[Qctl];
+ mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
+ release();
+ return 1;
+ }
+ r = profile.list;
+ while(s-- && r != nil)
+ r = r->link;
+ if(r == nil) {
+ release();
+ return -1;
+ }
+ sprint(up->genbuf, "%.8lux", (ulong)r->id);
+ mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR);
+ devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp);
+ release();
+ return 1;
+ }
+ if(s >= nelem(profdir)-1)
+ error(Enonexist); /* was return -1; */
+ tab = &profdir[s];
+ path = PATH(c->qid);
+
+ acquire();
+ r = getrec(PID(c->qid));
+ if(r == nil) {
+ release();
+ error(Enonexist); /* was return -1; */
+ }
+
+ perm = tab->perm;
+ len = tab->length;
+ mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, len, eve, perm, dp);
+ release();
+ return 1;
+}
+
+static Chan*
+profattach(char *spec)
+{
+ return devattach('P', spec);
+}
+
+static Walkqid*
+profwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, profgen);
+}
+
+static int
+profstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, profgen);
+}
+
+static Chan*
+profopen(Chan *c, int omode)
+{
+ int qid;
+ Record *r;
+
+ if(c->qid.type & QTDIR) {
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(omode&OTRUNC)
+ error(Eperm);
+
+ qid = QID(c->qid);
+ if(qid == Qctl || qid == Qpctl){
+ if (omode != OWRITE)
+ error(Eperm);
+ }
+ else{
+ if(omode != OREAD)
+ error(Eperm);
+ }
+
+ if(qid != Qctl){
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ }
+
+ c->offset = 0;
+ c->flag |= COPEN;
+ c->mode = openmode(omode);
+ if(QID(c->qid) == Qhist)
+ c->aux = nil;
+ return c;
+}
+
+static int
+profwstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ Record *r;
+
+ if(strcmp(up->env->user, eve))
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ n = convM2D(dp, n, &d, nil);
+ if(n == 0)
+ error(Eshortstat);
+ d.mode &= 0777;
+ /* TO DO: copy to c->aux->perm, once that exists */
+ return n;
+}
+
+static void
+profclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+profread(Chan *c, void *va, long n, vlong offset)
+{
+ int i;
+ Record *r;
+ char *a = va;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, profgen);
+ acquire();
+ r = getrec(PID(c->qid));
+ release();
+ if(r == nil)
+ error(Ethread);
+ switch(QID(c->qid)){
+ case Qname:
+ return readstr(offset, va, n, r->name);
+ case Qpath:
+ return readstr(offset, va, n, r->path);
+ case Qhist:
+ i = (int)c->aux;
+ while(i < r->size && r->bucket[i] == 0)
+ i++;
+ if(i >= r->size)
+ return 0;
+ c->aux = (void*)(i+1);
+ if(n < 20)
+ error(Etoosmall);
+ return sprint(a, "%d %lud", i, r->bucket[i]);
+ case Qctl:
+ error(Eperm);
+ }
+ return 0;
+}
+
+static long
+profwrite(Chan *c, void *va, long n, vlong offset)
+{
+ int i;
+ char *a = va;
+ char buf[128], *fields[128];
+ void (*f)(int, ulong, ulong, ulong);
+
+ USED(va);
+ USED(n);
+ USED(offset);
+
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+ switch(QID(c->qid)){
+ case Qctl:
+ if(n > sizeof(buf)-1)
+ n = sizeof(buf)-1;
+ memmove(buf, a, n);
+ buf[n] = 0;
+ i = getfields(buf, fields, nelem(fields), 1, " \t\n");
+ if(i > 0 && strcmp(fields[0], "module") == 0){
+ f = memmonitor;
+ memmonitor = nil;
+ freepmods();
+ while(--i > 0)
+ addpmod(fields[i]);
+ memmonitor = f;
+ return n;
+ }
+ if(i == 1){
+ if(strcmp(fields[0], "start") == 0){
+ if(profiler == Pnil) {
+ profiler = Psam;
+ if(!samplefn){
+ samplefn = 1;
+ kproc("prof", sampler, 0, 0);
+ }
+ }
+ }
+ else if(strncmp(fields[0], "startmp", 7) == 0){
+ if(profiler == Pnil){
+ profiler = Pmem;
+ for(a = &fields[0][7]; *a != '\0'; a++){
+ if(*a == '1'){
+ memmonitor = memprofmi;
+ mprofiler |= Mmain;
+ }
+ else if(*a == '2'){
+ heapmonitor = memprof;
+ mprofiler |= Mheap;
+ }
+ else if(*a == '3'){
+ memmonitor = memprofmi;
+ mprofiler |= Mimage;
+ }
+ };
+ }
+ }
+ else if(strcmp(fields[0], "stop") == 0){
+ profiler = Pnil;
+ mprofiler = Mnone;
+ }
+ else if(strcmp(fields[0], "end") == 0){
+ profiler = Pnil;
+ mprofiler = Mnone;
+ memmonitor = nil;
+ freeprof();
+ interval = 100;
+ }
+ else
+ error(Ebadarg);
+ }
+ else if (i == 2){
+ if(strcmp(fields[0], "interval") == 0)
+ interval = strtoul(fields[1], nil, 0);
+ else if(strcmp(fields[0], "startcp") == 0){
+ Prog *p;
+
+ acquire();
+ p = progpid(strtoul(fields[1], nil, 0));
+ if(p == nil){
+ release();
+ return -1;
+ }
+ if(profiler == Pnil){
+ profiler = Pcov;
+ p->xec = cpxec;
+ }
+ release();
+ }
+ else
+ error(Ebadarg);
+ }
+ else
+ error(Ebadarg);
+ return n;
+ default:
+ error(Eperm);
+ }
+ return 0;
+}
+
+static Record*
+newmodule(Module *m, int vm, int scale, int origin)
+{
+ int dsize;
+ Record *r, **l;
+
+ if(!vm)
+ acquire();
+ if((m->compiled && m->pctab == nil) || m->prog == nil) {
+ if(!vm)
+ release();
+ return nil;
+ }
+ if(m->compiled)
+ dsize = m->nprog * sizeof(r->bucket[0]);
+ else
+ dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]);
+ dsize *= scale;
+ dsize += origin;
+ r = malloc(sizeof(Record)+dsize);
+ if(r == nil) {
+ if(!vm)
+ release();
+ return nil;
+ }
+
+ r->id = ++ids;
+ if(ids == (1<<8)-1)
+ ids = 0;
+ kstrdup(&r->name, m->name);
+ kstrdup(&r->path, m->path);
+ r->base = m->prog;
+ r->size = dsize/sizeof(r->bucket[0]);
+ /* r->m = m; */
+ r->mtime = m->mtime;
+ r->qid.path = m->qid.path;
+ r->qid.vers = m->qid.vers;
+ memset(r->bucket, 0, dsize);
+ r->link = profile.list;
+ profile.list = r;
+
+ l = &profile.hash[HASH(m->mtime)];
+ r->hash = *l;
+ *l = r;
+
+ if(!vm)
+ release();
+ return r;
+}
+
+#define LIMBO(m) ((m)->path[0] != '$')
+
+Module*
+limbomodule(void)
+{
+ Frame *f;
+ uchar *fp;
+ Module *m;
+
+ m = R.M->m;
+ if(LIMBO(m))
+ return m;
+ for(fp = R.FP ; fp != nil; fp = f->fp){
+ f = (Frame*)fp;
+ if(f->mr != nil){
+ m = f->mr->m;
+ if(LIMBO(m))
+ return m;
+ }
+ }
+ return nil;
+}
+
+static Record*
+mlook(Module *m, int limbo, int vm, int scale, int origin)
+{
+ Record *r;
+ void (*f)(int, ulong, ulong, ulong);
+
+ if(limbo)
+ m = limbomodule();
+ if(m == nil)
+ return nil;
+ for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){
+ if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){
+ r->base = m->prog;
+ return r;
+ }
+ }
+ if(pmods == nil || inpmods(m->name) || inpmods(m->path)){
+ f = memmonitor;
+ memmonitor = nil; /* prevent monitoring of our memory usage */
+ r = newmodule(m, vm, scale, origin);
+ memmonitor = f;
+ return r;
+ }
+ return nil;
+}
+
+static void
+sampler(void* a)
+{
+ int i;
+ Module *m;
+ Record *r;
+ Inst *p;
+
+ USED(a);
+ for(;;) {
+ osmillisleep(interval);
+ if(profiler != Psam)
+ break;
+ lock(&profile.l);
+ profile.time += interval;
+ if(R.M == H || (m = R.M->m) == nil){
+ unlock(&profile.l);
+ continue;
+ }
+ p = R.PC;
+ r = mlook(m, 0, 0, 1, 0);
+ if(r == nil){
+ unlock(&profile.l);
+ continue;
+ }
+ if(m->compiled && m->pctab != nil)
+ p = pc2dispc(p, m);
+ if((i = p-r->base) >= 0 && i < r->size)
+ r->bucket[i]++;
+ unlock(&profile.l);
+ }
+ samplefn = 0;
+ pexit("", 0);
+}
+
+/*
+ * coverage profiling
+ */
+
+static void
+cpxec(Prog *p)
+{
+ int op, i;
+ Module *m;
+ Record *r;
+ Prog *n;
+
+ R = p->R;
+ R.MP = R.M->MP;
+ R.IC = p->quanta;
+
+ if(p->kill != nil){
+ char *m;
+ m = p->kill;
+ p->kill = nil;
+ error(m);
+ }
+
+ if(R.M->compiled)
+ comvec();
+ else{
+ m = R.M->m;
+ r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+ do{
+ dec[R.PC->add]();
+ op = R.PC->op;
+ if(r != nil){
+ i = R.PC-r->base;
+ if(i >= 0 && i < r->size)
+ r->bucket[i]++;
+ }
+ R.PC++;
+ optab[op]();
+ if(op == ISPAWN || op == IMSPAWN){
+ n = delruntail(Pdebug); /* any state will do */
+ n->xec = cpxec;
+ addrun(n);
+ }
+ if(m != R.M->m){
+ m = R.M->m;
+ r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil;
+ }
+ }while(--R.IC != 0);
+ }
+
+ p->R = R;
+}
+
+/* memory profiling */
+
+enum{
+ Mhalloc,
+ Mhfree,
+ Mgcfree,
+ Mmfree,
+ Mmalloc,
+ Mifree,
+ Mialloc,
+};
+
+#ifdef HEAP_ALIGN
+
+static void
+memprof(int c, void *v, ulong n)
+{
+ int i, j, k;
+ ulong kk, *b;
+ Module *m;
+ Record *r;
+ Inst *p;
+ Heap *h;
+
+ USED(v);
+ USED(n);
+ if(profiler != Pmem){
+ memmonitor = nil;
+ heapmonitor = nil;
+ return;
+ }
+ lock(&profile.l);
+ m = nil;
+ if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){
+ unlock(&profile.l);
+ return;
+ }
+ h = v;
+ if(c == Mhalloc || c == Mmalloc || c == Mialloc){
+ p = R.PC;
+ if(m->compiled && m->pctab != nil)
+ p = pc2dispc(p, m);
+ if((r = mlook(m, 1, 1, 2, 2)) == nil){
+ unlock(&profile.l);
+ return;
+ }
+ i = p-r->base;
+ k = (r->id<<24) | i;
+ if(c == Mhalloc){
+ h->pad = k;
+ j = hmsize(h)-sizeof(Heap);
+ }
+ else if(c == Mmalloc){
+ setmalloctag(v, k);
+ j = msize(v);
+ }
+ else{
+ ((ulong*)v)[1] = k;
+ j = poolmsize(imagmem, v)-sizeof(ulong);
+ }
+ }
+ else{
+ if(c == Mmfree)
+ k = getmalloctag(v);
+ else if(c == Mifree)
+ k = ((ulong*)v)[1];
+ else
+ k = h->pad;
+ if((r = getrec(k>>24)) == nil){
+ unlock(&profile.l);
+ return;
+ }
+ i = k&0xffffff;
+ if(c == Mmfree)
+ j = msize(v);
+ else if(c == Mifree)
+ j = poolmsize(imagmem, v)-sizeof(ulong);
+ else
+ j = hmsize(h)-sizeof(Heap);
+ j = -j;
+ }
+ i = 2*(i+1);
+ b = r->bucket;
+ if(i >= 0 && i < r->size){
+ if(0){
+ if(c == 1){
+ b[0] -= j;
+ b[i] -= j;
+ }
+ else if(c == 2){
+ b[1] -= j;
+ b[i+1] -= j;
+ }
+ }
+ else{
+ b[0] += j;
+ if((int)b[0] < 0)
+ b[0] = 0;
+ b[i] += j;
+ if((int)b[i] < 0)
+ b[i] = 0;
+ if(j > 0){
+ if((kk = b[0]) > b[1])
+ b[1] = kk;
+ if((kk = b[i]) > b[i+1])
+ b[i+1] = kk;
+ }
+ }
+ }
+ unlock(&profile.l);
+}
+
+#else
+
+static void
+memprof(int c, void *v, ulong n)
+{
+ USED(c);
+ USED(v);
+ USED(n);
+}
+
+#endif
+
+/* main and image memory */
+static void
+memprofmi(int c, ulong pc, ulong v, ulong n)
+{
+ USED(pc);
+
+ if(c&2){
+ if(!(mprofiler&Mimage))
+ return;
+ }
+ else{
+ if(!(mprofiler&Mmain))
+ return;
+ }
+ switch(c){
+ case 0:
+ c = Mmalloc;
+ break;
+ case 2:
+ c = Mialloc;
+ break;
+ case 0 | 1<<8:
+ c = Mmfree;
+ break;
+ case 2 | 1<<8:
+ c = Mifree;
+ break;
+ default:
+ print("bad profile code %d\n", c);
+ }
+ memprof(c, (void*)v, n);
+}
+
+Dev profdevtab = {
+ 'P',
+ "prof",
+
+ devinit,
+ profattach,
+ profwalk,
+ profstat,
+ profopen,
+ devcreate,
+ profclose,
+ profread,
+ devbread,
+ profwrite,
+ devbwrite,
+ devremove,
+ profwstat
+};