#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "netif.h" #include enum { Qdir, Qctl, Qdata, }; enum { CMsize, CMtrace, }; static Dirtab tracedir[]= { ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "ptracectl", {Qctl}, 0, 0664, "ptrace", {Qdata}, 0, 0440, }; static Lock tlk; static int topens; static int tproduced, tconsumed, ntevents, ntmask; static uchar *tevents; static Chan* traceattach(char *spec) { return devattach(L'σ', spec); } static Walkqid* tracewalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, tracedir, nelem(tracedir), devgen); } static long tracestat(Chan *c, uchar *db, long n) { return devstat(c, db, n, tracedir, nelem(tracedir), devgen); } static void _ptrace(Proc *p, int etype, vlong ts, vlong arg) { int i; uchar *te; if (p->trace == 0 || topens == 0) return; lock(&tlk); if (p->trace == 0 || topens == 0 || tproduced - tconsumed >= ntevents){ unlock(&tlk); return; } if(ts == 0) ts = todget(nil); i = (tproduced&ntmask) * PTsize; te = &tevents[i]; PBIT32(te, (int)p->pid); te += BIT32SZ; PBIT32(te, etype); te += BIT32SZ; PBIT32(te, m->machno); te += BIT32SZ; PBIT64(te, ts); te += BIT64SZ; PBIT64(te, arg); tproduced++; if(etype == SDead) p->trace = 0; unlock(&tlk); } static Chan* traceopen(Chan *c, int omode) { int q; q = c->qid.path; switch(q){ case Qdata: lock(&tlk); if (waserror()){ unlock(&tlk); nexterror(); } if(topens > 0) error(Einuse); if(ntevents == 0) error("must set trace size first"); if(up->trace != 0) error("a traced process can't open the trace device"); tproduced = tconsumed = 0; proctrace = _ptrace; topens++; poperror(); unlock(&tlk); break; } c = devopen(c, omode, tracedir, nelem(tracedir), devgen); return c; } static void traceclose(Chan *c) { int q; q = c->qid.path; switch(q){ case Qdata: if(c->flag & COPEN){ lock(&tlk); topens--; tproduced = tconsumed = 0; proctrace = nil; unlock(&tlk); } } } static long traceread(Chan *c, void *a, long n, vlong offset) { int q, xopens, xevents, xprod, xcons, navail, ne, i; char *s, *e; q = c->qid.path; switch(q){ case Qdir: return devdirread(c, a, n, tracedir, nelem(tracedir), devgen); break; case Qctl: e = up->genbuf + sizeof up->genbuf; lock(&tlk); xopens = topens; xevents = ntevents; xprod = tproduced; xcons = tconsumed; unlock(&tlk); s = seprint(up->genbuf, e, "opens %d\n", xopens); s = seprint(s, e, "size %d\n", xevents); s = seprint(s, e, "produced %d\n", xprod); seprint(s, e, "consumed %d\n", xcons); return readstr(offset, a, n, up->genbuf); break; case Qdata: /* no locks! */ navail = tproduced - tconsumed; if(navail <= 0) return 0; if(navail > n / PTsize) navail = n / PTsize; s = a; e = a; while(navail > 0) { if((tconsumed & ntmask) + navail > ntevents) ne = ntevents - (tconsumed & ntmask); else ne = navail; i = ne * PTsize; memmove(e, &tevents[(tconsumed & ntmask)*PTsize], i); tconsumed += ne; e += i; navail -= ne; } return e - s; break; default: error(Egreg); } error("not yet implemented"); return -1; } static Cmdtab proccmd[] = { CMsize, "size", 2, CMtrace, "trace", 0, }; static long tracewrite(Chan *c, void *a, long n, vlong) { int i, q, sz, msk, pid; Cmdbuf *cb; Cmdtab *ct; uchar *nt; Proc *p; q = c->qid.path; switch(q){ case Qctl: break; default: error(Egreg); } cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, proccmd, nelem(proccmd)); switch(ct->index){ case CMsize: sz = atoi(cb->f[1]); if(sz == 0){ lock(&tlk); ntevents = 0; ntmask = 0; unlock(&tlk); } msk = sz-1; if((sz&msk) != 0) error("wrong size. use a power of two."); if(sz > 512*1024) error("size is too large"); lock(&tlk); if(waserror()){ unlock(&tlk); nexterror(); } if(sz > ntevents){ nt = realloc(tevents, PTsize * sz); if(nt == nil) error("not enough memory"); tevents = nt; ntevents = sz; ntmask = msk; } tproduced = 0; tconsumed = 0; poperror(); unlock(&tlk); break; case CMtrace: if(cb->nf != 2 && cb->nf != 3) error(Ebadctl); pid = atoi(cb->f[1]); i = psindex(pid); if(i < 0) error(Eprocdied); p = psincref(i); if(p == nil) error(Eprocdied); qlock(&p->debug); if(waserror()){ qunlock(&p->debug); psdecref(p); nexterror(); } if(p->pid != pid) error(Eprocdied); if(cb->nf == 2) p->trace ^= p->trace; else p->trace = (atoi(cb->f[2]) != 0); poperror(); qunlock(&p->debug); psdecref(p); break; } poperror(); free(cb); return n; } Dev ptracedevtab = { L'σ', "ptrace", devreset, devinit, devshutdown, traceattach, tracewalk, tracestat, traceopen, devcreate, traceclose, traceread, devbread, tracewrite, devbwrite, devremove, devwstat, };