#include "dat.h" #include "fns.h" #include "error.h" #include "interp.h" #include #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)<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<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, }; 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->hprof = 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->hprof; 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); } /* 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 };