#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "netif.h" /* this does not work */ #pragma profile 0 typedef struct Probe Probe; struct Probe { struct Probe *next; void *func; void *start; void *end; int enabled; char name[16]; }; enum { Qdir, Qctl, Qdata, }; enum { ProbeEntry = 1, ProbeExit }; /* fix me make this programmable */ enum { defaultlogsize = 1024, printsize = 64, }; typedef struct Probelog Probelog; struct Probelog { uvlong ticks; /* yeah, waste a whole int on something stupid but ... */ int info; ulong pc; /* these are different depending on type */ long dat[4]; }; static Rendez profprobesleep; static QLock profprobeslock; /* this will contain as many entries as there are valid pc values */ static Probe **probemap; static Probe *probes; static Lock loglk; static Probelog *profprobelog = nil; int probeactive = 0; /* profprobe indices. These are just unsigned longs. You mask them * to get an index. This makes fifo empty/full etc. trivial. */ static ulong pw = 0, pr = 0; static int probesactive = 0; static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1; int codesize = 0; static char eventname[] = { [ProbeEntry] = 'E', [ProbeExit] = 'X' }; static Dirtab profprobedir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "profprobectl", {Qctl}, 0, 0664, "profprobe", {Qdata}, 0, 0440, }; char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', }; /* big-endian ... */ void hex32(ulong l, char *c) { int i; for(i = 8; i; i--){ c[i-1] = hex[l&0xf]; l >>= 4; } } void hex64(uvlong l, char *c) { hex32(l>>32, c); hex32(l, &c[8]); } static int lognonempty(void *) { return pw - pr; } static int logfull(void) { return (pw - pr) >= logsize; } static ulong idx(ulong f) { return f & logmask; } struct Probe ** probeslot(void *pc) { int index; struct Probe **p; // if ((ulong)pc < (ulong)start) || ((ulong)pc > (ulong)end)) // panic("probed: pc %p", pc); // index = (ulong)pc - (ulong)start; index = (ulong)pc - (ulong)KTZERO; if (index > codesize) return nil; p = &probemap[index]; return p; } struct Probe * probed(void *pc) { struct Probe **p; p = probeslot(pc); return *p; } /* it is recommended that you call these with something sane. */ /* these next two functions assume you locked profprobelock */ void probeon(struct Probe *p) { unsigned char *cp; struct Probe **slot; slot = probeslot(p->start); print("probeon: slot %p\n", slot); for(cp = p->start; cp <= p->end; slot++, cp++) *slot = p; p->enabled = 1; probesactive++; } void probeoff(struct Probe *p) { unsigned char *cp; struct Probe **slot; slot = probeslot(p->start); print("probeoff: slot %p\n", slot); for(cp = p->start; cp <= p->end; slot++, cp++) *slot = nil; p->enabled = 0; probesactive--; } /* can return NULL, meaning, no record for you */ static struct Probelog * newpl(void) { ulong index; /* if (logfull()){ wakeup(&profprobesleep); return nil; } */ ilock(&loglk); index = pw++; iunlock(&loglk); return &profprobelog[idx(index)]; } void profin(ulong pc, ulong a1, ulong a2, ulong a3) { uintptr kgetcallerpc(void *firstarg); struct Probelog *pl; if (! probeactive) return; if (! probed((void *)pc)) return; pl = newpl(); if (! pl) return; cycles(&pl->ticks); pl->pc = (ulong)pc; if (up) pl->dat[0] = up->pid; else pl->dat[0] = (unsigned long)-1; pl->dat[1] = a1; pl->dat[2] = a2; pl->dat[3] = a3; pl->info = ProbeEntry; } void profout(ulong pc, ulong retval) { struct Probelog *pl; if (! probeactive) return; /* return here, it works */ if (! probed((void *)pc)) return; /* return here, it works */ pl = newpl(); if (! pl) return; // MOST RECENT CHANGE ENABLE CYCLES cycles(&pl->ticks); pl->pc = (ulong)pc; if (up) pl->dat[0] = up->pid; else pl->dat[0] = (unsigned long)-1; pl->dat[1] = retval; pl->dat[2] = 0; pl->dat[3] = 0; pl->info = ProbeExit; } static Probe * mkprobe(void *func, void *start, void *end) { Probe *p; p = mallocz(sizeof p[0], 1); p->func = func; p->start = start; p->end = end; return p; } static void freeprobe(Probe *p) { free(p); } static Chan* profprobeattach(char *spec) { return devattach('T', spec); } static Walkqid* profprobewalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, profprobedir, nelem(profprobedir), devgen); } static int profprobestat(Chan *c, uchar *db, int n) { return devstat(c, db, n, profprobedir, nelem(profprobedir), devgen); } static Chan* profprobeopen(Chan *c, int omode) { /* if there is no profprobelog, allocate one. Open always fails * if the basic alloc fails. You can resize it later. */ codesize = ((ulong)etext) - KTZERO; print("codesize %d\n", codesize); if (! probemap) probemap = mallocz(sizeof(struct probemap *)*codesize, 1); if (! probemap) error("probemap malloc failed"); if (! profprobelog) profprobelog = mallocz(sizeof(*profprobelog)*logsize, 1); /* I guess malloc doesn't toss an error */ if (! profprobelog) error("profprobelog malloc failed"); c = devopen(c, omode, profprobedir, nelem(profprobedir), devgen); return c; } static void profprobeclose(Chan *) { } static long profproberead(Chan *c, void *a, long n, vlong offset) { char *buf; char *cp = a; struct Probelog *pl; Probe *p; int i; static QLock gate; if(c->qid.type == QTDIR) return devdirread(c, a, n, profprobedir, nelem(profprobedir), devgen); switch((ulong)c->qid.path){ default: error("profproberead: bad qid"); case Qctl: buf = malloc(READSTR); i = 0; qlock(&profprobeslock); i += snprint(buf + i, READSTR - i, "logsize %lud\n", logsize); for(p = probes; p != nil; p = p->next) i += snprint(buf + i, READSTR - i, "probe %p %p new %s\n", p->start, p->end, p->name); for(p = probes; p != nil; p = p->next) i += snprint(buf + i, READSTR - i, "#probe %p probed? %p\n", p->func, probed(p->func)); for(p = probes; p != nil; p = p->next) if (p->enabled) i += snprint(buf + i, READSTR - i, "probe %s on\n", p->name); i += snprint(buf + i, READSTR - i, "#profprobehits %lud, in queue %lud\n", pw, pw-pr); snprint(buf + i, READSTR - i, "#profprobelog %p\n", profprobelog); snprint(buf + i, READSTR - i, "#probeactive %d\n", probeactive); qunlock(&profprobeslock); n = readstr(offset, a, n, buf); free(buf); break; case Qdata: // qlock(&gate); if(waserror()){ // qunlock(&gate); nexterror(); } // sleep(&profprobesleep, lognonempty, nil); i = 0; while(lognonempty((void *)0)){ int j; pl = profprobelog + idx(pr); if ((i + printsize) >= n) break; /* simple format */ cp[0] = eventname[pl->info]; cp ++; *cp++ = ' '; hex32(pl->pc, cp); cp[8] = ' '; cp += 9; hex64(pl->ticks, cp); cp[16] = ' '; cp += 17; for(j = 0; j < 4; j++){ hex32(pl->dat[j], cp); cp[8] = ' '; cp += 9; } /* adjust for extra skip above */ cp--; *cp++ = '\n'; pr++; i += printsize; } poperror(); // qunlock(&gate); n = i; break; } return n; } static long profprobewrite(Chan *c, void *a, long n, vlong) { char *tok[5]; char *ep, *s = nil; Probe *p, **pp; int ntok; int saveactive = probeactive; probeactive = 0; qlock(&profprobeslock); if(waserror()){ qunlock(&profprobeslock); if(s != nil) free(s); nexterror(); } switch((ulong)c->qid.path){ default: error("profproberead: bad qid"); case Qctl: s = malloc(n + 1); memmove(s, a, n); s[n] = 0; ntok = tokenize(s, tok, nelem(tok)); print("write ctl s is :%s: ntok %d\n", s, ntok); print("what is tok[0]? %s\n", tok[0]); if(!strcmp(tok[0], "probe")){ /* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */ if(ntok < 3) error("devprofprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]"); for(pp = &probes; *pp != nil; pp = &(*pp)->next){ print("Check %p against %p\n", tok[1], (*pp)->name); if(!strcmp(tok[1], (*pp)->name)) break; } p = *pp; print("Check tok[3] which is %p or tok[2] which is %p\n", tok[3], tok[2]); if((ntok > 3) && (!strcmp(tok[3], "new"))){ ulong addr; void *start, *end, *func; if (ntok != 5) error("probe ktextstart ktextend new name"); addr = strtoul(tok[1], &ep, 0); func = start = (void*)addr; if(*ep) error("devprofprobe: start address not in recognized format"); addr = strtoul(tok[2], &ep, 0); end = (void*)addr; if(*ep) error("devprofprobe: end address not in recognized format"); /* What do we do here? start and end are weird * if((addr < (ulong)start) || (addr > (ulong)end) error("devprofprobe: address out of bounds"); */ if(p) error("devprofprobe: 0x%p already has profprobe"); p = mkprobe(func, start, end); p->next = probes; if(ntok < 5) snprint(p->name, sizeof p->name, "%p", func); else strncpy(p->name, tok[4], sizeof p->name); probes = p; } else if(!strcmp(tok[2], "on")){ if(p == nil) error("devprofprobe: 0x%p not found"); print("turn on probe %p probed %p\n", p->func, probed(p->func)); if (! probed(p->func)){ probeon(p); saveactive += 1; } } else if(!strcmp(tok[2], "off")){ if(p == nil) error("devprofprobe: 0x%p not found"); if(probed(p->func)){ probeoff(p); } } else if(!strcmp(tok[2], "del")){ if(p == nil) error("devprofprobe: 0x%p not found"); if(probed(p->func)){ probeoff(p); } *pp = p->next; freeprobe(p); } else if(!strcmp(tok[2], "mv")){ if(p == nil) error("devprofprobe: 0x%p not found"); if(ntok < 4) error("devprofprobe: rename without new name?"); strncpy(p->name, tok[3], sizeof p->name); } } else if(!strcmp(tok[0], "size")){ int l, size; struct Probelog *newprofprobelog; l = strtoul(tok[1], &ep, 0); if(*ep) error("devprofprobe: size not in recognized format"); size = 1 << l; /* sort of foolish. Alloc new profprobe first, then free old. */ /* and too bad if there are unread profprobes */ newprofprobelog = mallocz(sizeof(*newprofprobelog)*size, 1); /* does malloc throw waserror? I don't know */ if (newprofprobelog){ free(profprobelog); profprobelog = newprofprobelog; logsize = size; logmask = size - 1; pr = pw = 0; } else error("devprofprobe: can't allocate that much"); } else { error("devprofprobe: usage: 'profprobe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or: 'size' buffersize (power of 2)"); } free(s); break; } poperror(); qunlock(&profprobeslock); probeactive = saveactive; return n; } Dev profprobedevtab = { 'T', "profprobe", devreset, devinit, devshutdown, profprobeattach, profprobewalk, profprobestat, profprobeopen, devcreate, profprobeclose, profproberead, devbread, profprobewrite, devbwrite, devremove, devwstat, };