diff options
Diffstat (limited to 'os/port/devprof.c')
| -rw-r--r-- | os/port/devprof.c | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/os/port/devprof.c b/os/port/devprof.c new file mode 100644 index 00000000..f0ee3ba8 --- /dev/null +++ b/os/port/devprof.c @@ -0,0 +1,722 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include <interp.h> +#include <isa.h> +#include "runt.h" + +static void cpxec(Prog *); +static void memprof(int, Heap*, 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, /* memory profiler */ +}; + +static int profiler = Pnil; + +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; + freepmods(); + for(r = profile.list; r != nil; r = nr){ + 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]; + + 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){ + freepmods(); + while(--i > 0) + addpmod(fields[i]); + 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(strcmp(fields[0], "startmp") == 0){ + if(profiler == Pnil){ + profiler = Pmem; + heapmonitor = memprof; + } + } + else if(strcmp(fields[0], "stop") == 0) + profiler = Pnil; + else if(strcmp(fields[0], "end") == 0){ + profiler = Pnil; + 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; + } +/* print("newmodule %x %s %s %d %d %d\n", m, m->name, m->path, m->mtime, m->qid.path, m->qid.vers); */ + 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; +} + +static Record* +mlook(Module *m, int vm, int scale, int origin) +{ + Record *r; + + for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ + /* if(r->m == m){ /* bug - r->m could be old exited module */ + 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)){ + if(0 && profiler == Pmem) + heapmonitor = nil; + r = newmodule(m, vm, scale, origin); + if(0 && profiler == Pmem) + heapmonitor = memprof; + return r; + } + return nil; +} + +static void +sampler(void* a) +{ + int i; + Module *m; + Record *r; + Inst *p; + + USED(a); + for(;;) { + tsleep(&up->sleep, return0, nil, 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, 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, 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, 1, 1, 0) : nil; + } + }while(--R.IC != 0); + } + + p->R = R; +} + +/* memory profiling */ + +enum{ + Halloc, + Hfree, + Hgcfree, +}; + +#define MPAD sizeof(double) + +static void +memprof(int c, Heap *h, ulong n) +{ + int i, j, k; + ulong kk, *b; + Module *m; + Record *r; + Inst *p; + +/* print("%d %x %uld\n", c, h, n); */ + USED(h); + if(profiler != Pmem){ + heapmonitor = nil; + return; + } + lock(&profile.l); + m = nil; + if(c != Hgcfree && (R.M == H || (m = R.M->m) == nil)){ + unlock(&profile.l); + return; + } + j = n; + if(c == 0 || c == 4){ /* heap or main allocation */ + p = R.PC; + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((r = mlook(m, 1, 2, 2)) == nil){ + unlock(&profile.l); + return; + } + i = p-r->base; + k = (r->id<<24) | i; + if(c == 0){ + h->pad = k; + k = sizeof(Heap); + } + else{ + *(ulong*)h = k; + k = MPAD; + } + /* 31 is pool quanta - dependency on alloc.c */ + j = ((j+k+BHDRSIZE+31)&~31) - (k+BHDRSIZE); + } + else{ + /* c == 1 is ref count free */ + /* c == 2 is gc free */ + /* c == 3 is main free */ + if(c == 3) + k = *(ulong*)h; + else + k = h->pad; + if((r = getrec(k>>24)) == nil){ + unlock(&profile.l); + return; + } + i = k&0xffffff; + if(c == 3) + j = msize(h)-MPAD; + 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); +} + +Dev profdevtab = { + 'P', + "prof", + + devreset, + devinit, + devshutdown, + profattach, + profwalk, + profstat, + profopen, + devcreate, + profclose, + profread, + devbread, + profwrite, + devbwrite, + devremove, + profwstat +}; |
