123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- #include "probe.h"
- enum {
- Qdir,
- Qctl,
- Qdata,
- };
- enum {
- ProbeEntry = 1,
- ProbeExit
- };
- /* fix me make this programmable */
- enum {
- defaultlogsize = 1024,
- printsize = 64,
- };
- typedef struct Probelog Probelog;
- struct Probelog {
- uint64_t ticks;
- /* yeah, waste a whole int on something stupid but ... */
- int info;
- uint32_t pc;
- /* these are different depending on type */
- int32_t dat[4];
- };
- static Rendez probesleep;
- static QLock probeslk;
- static Probe *probes;
- static Lock loglk;
- static Probelog *probelog = nil;
- /* probe indices. These are just unsigned longs. You mask them
- * to get an index. This makes fifo empty/full etc. trivial.
- */
- static uint32_t pw = 0, pr = 0;
- static int probesactive = 0;
- static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1;
- static char eventname[] = {
- [ProbeEntry] = 'E',
- [ProbeExit] = 'X'
- };
- static Dirtab probedir[]={
- {".", {Qdir, 0, QTDIR}, 0, DMDIR|0555},
- {"probectl", {Qctl}, 0, 0664},
- {"probe", {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(uint32_t l, char *c)
- {
- int i;
- for(i = 8; i; i--){
- c[i-1] = hex[l&0xf];
- l >>= 4;
- }
- }
- void
- hex64(uint64_t 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 uint32_t
- idx(uint32_t f)
- {
- return f & logmask;
- }
- /* can return NULL, meaning, no record for you */
- static struct Probelog *
- newpl(void)
- {
- uint32_t index;
- if (logfull()){
- wakeup(&probesleep);
- return nil;
- }
- ilock(&loglk);
- index = pw++;
- iunlock(&loglk);
- return &probelog[idx(index)];
- }
- static void
- probeentry(Probe *p)
- {
- struct Probelog *pl;
- //print("probeentry %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
- pl = newpl();
- if (! pl)
- return;
- cycles(&pl->ticks);
- pl->pc = (uint32_t)p->func;
- pl->dat[0] = p->argp[0];
- pl->dat[1] = p->argp[1];
- pl->dat[2] = p->argp[2];
- pl->dat[3] = p->argp[3];
- pl->info = ProbeEntry;
- }
- static void
- probeexit(Probe *p)
- {
- //print("probeexit %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
- struct Probelog *pl;
- pl = newpl();
- if (! pl)
- return;
- cycles(&pl->ticks);
- pl->pc = (uint32_t)p->func;
- pl->dat[0] = p->rval;
- pl->info = ProbeExit;
- }
- static Chan*
- probeattach(char *spec)
- {
- return devattach('+', spec);
- }
- static Walkqid*
- probewalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, probedir, nelem(probedir), devgen);
- }
- static int32_t
- probestat(Chan *c, uint8_t *db, int32_t n)
- {
- return devstat(c, db, n, probedir, nelem(probedir), devgen);
- }
- static Chan*
- probeopen(Chan *c, int omode)
- {
- /* if there is no probelog, allocate one. Open always fails
- * if the basic alloc fails. You can resize it later.
- */
- if (! probelog)
- probelog = malloc(sizeof(*probelog)*logsize);
- /* I guess malloc doesn't toss an error */
- if (! probelog)
- error("probelog malloc failed");
- c = devopen(c, omode, probedir, nelem(probedir), devgen);
- return c;
- }
- static void
- probeclose(Chan *)
- {
- }
- static int32_t
- proberead(Chan *c, void *a, int32_t n, int64_t 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, probedir, nelem(probedir), devgen);
- switch((uint32_t)c->qid.path){
- default:
- error("proberead: bad qid");
- case Qctl:
- buf = malloc(READSTR);
- i = 0;
- qlock(&probeslk);
- i += snprint(buf + i, READSTR - i, "logsize %lu\n", logsize);
- for(p = probes; p != nil; p = p->next)
- i += snprint(buf + i, READSTR - i, "probe %p new %s\n",
- p->func, p->name);
- 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, "#probehits %lu, in queue %lu\n",
- pw, pw-pr);
- snprint(buf + i, READSTR - i, "#probelog %p\n", probelog);
- qunlock(&probeslk);
- n = readstr(offset, a, n, buf);
- free(buf);
- break;
- case Qdata:
- qlock(&gate);
- if(waserror()){
- qunlock(&gate);
- nexterror();
- }
- while(!lognonempty(nil))
- tsleep(&probesleep, lognonempty, nil, 5000);
- i = 0;
- while(lognonempty((void *)0)){
- int j;
- pl = probelog + 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 int32_t
- probewrite(Chan *c, void *a, int32_t n, int64_t)
- {
- char *tok[5];
- char *ep, *s = nil;
- Probe *p, **pp;
- int ntok;
- qlock(&probeslk);
- if(waserror()){
- qunlock(&probeslk);
- if(s != nil) free(s);
- nexterror();
- }
- switch((uint32_t)c->qid.path){
- default:
- error("proberead: bad qid");
- case Qctl:
- s = malloc(n + 1);
- memmove(s, a, n);
- s[n] = 0;
- ntok = tokenize(s, tok, nelem(tok));
- if(!strcmp(tok[0], "probe")){ /* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */
- if(ntok < 3)
- error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");
- for(pp = &probes; *pp != nil; pp = &(*pp)->next)
- if(!strcmp(tok[1], (*pp)->name))
- break;
- p = *pp;
- if(!strcmp(tok[2], "new")){
- uint32_t addr;
- void *func;
- addr = strtoul(tok[1], &ep, 0);
- func = (void*)addr;
- if(*ep)
- error("devprobe: address not in recognized format");
- // if(addr < ((uint32_t) start) || addr > ((uint32_t) end))
- // error("devprobe: address out of bounds");
- if(p != nil)
- error("devprobe: 0x%p already has probe");
- p = mkprobe(func, probeentry, probeexit);
- p->next = probes;
- if(ntok < 4)
- snprint(p->name, sizeof p->name, "%p", func);
- else
- strncpy(p->name, tok[3], sizeof p->name);
- probes = p;
- } else if(!strcmp(tok[2], "on")){
- if(p == nil)
- error("devprobe: probe not found");
- if(!p->enabled)
- probeinstall(p);
- print("probeinstall in devprobe\n");
- probesactive++;
- } else if(!strcmp(tok[2], "off")){
- if(p == nil)
- error("devprobe: probe not found");
- if(p->enabled)
- probeuninstall(p);
- probesactive--;
- } else if(!strcmp(tok[2], "del")){
- if(p == nil)
- error("devprobe: probe not found");
- if(p->enabled)
- probeuninstall(p);
- probesactive--;
- *pp = p->next;
- freeprobe(p);
- } else if(!strcmp(tok[2], "mv")){
- if(p == nil)
- error("devprobe: probe not found");
- if(ntok < 4)
- error("devprobe: rename without new name?");
- strncpy(p->name, tok[3], sizeof p->name);
- }
- } else if(!strcmp(tok[0], "size")){
- int l, size;
- struct Probelog *newprobelog;
- l = strtoul(tok[1], &ep, 0);
- if(*ep)
- error("devprobe: size not in recognized format");
- size = 1 << l;
- /* sort of foolish. Alloc new probe first, then free old. */
- /* and too bad if there are unread probes */
- newprobelog = malloc(sizeof(*newprobelog)*size);
- /* does malloc throw waserror? I don't know */
- free(probelog);
- probelog = newprobelog;
- logsize = size;
- pr = pw = 0;
- } else {
- error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or: 'size' buffersize (power of 2)");
- }
- free(s);
- break;
- }
- poperror();
- qunlock(&probeslk);
- return n;
- }
- Dev probedevtab = {
- .dc = '+',
- .name = "probe",
- .reset = devreset,
- .init = devinit,
- .shutdown = devshutdown,
- .attach = probeattach,
- .walk = probewalk,
- .stat = probestat,
- .open = probeopen,
- .create = devcreate,
- .close = probeclose,
- .read = proberead,
- .bread = devbread,
- .write = probewrite,
- .bwrite = devbwrite,
- .remove = devremove,
- .wstat = devwstat,
- };
|