diff --git a/sys/src/9/port/devdtracy.c b/sys/src/9/port/devdtracy.c new file mode 100644 index 000000000..e35bb6d7e --- /dev/null +++ b/sys/src/9/port/devdtracy.c @@ -0,0 +1,530 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include + +Lock *machlocks; + +typedef struct DTKChan DTKChan; +typedef struct DTKAux DTKAux; +QLock dtracylock; + +struct DTKChan { + DTChan *ch; + int ref; + int idx; +}; +struct DTKAux { + char *str; +}; + +static void +prog(DTKChan *p, char *s) +{ + DTClause *c; + int rc; + + dtcrun(p->ch, DTCSTOP); + dtcreset(p->ch); + while(*s != 0){ + s = dtclunpack(s, &c); + if(s == nil) + error("invalid program"); + rc = dtcaddcl(p->ch, c); + dtclfree(c); + if(rc < 0){ + dtcreset(p->ch); + error("failed to add clause"); + } + } +} + +enum { + /* Qdir */ + Qclone = 1, +}; + +enum { + Qdir, + Qctl, + Qprog, + Qbuf, + Qepid, +}; + +static Dirtab dtracydir[] = { + "ctl", { Qctl, 0, 0 }, 0, 0660, + "prog", { Qprog, 0, 0 }, 0, 0660, + "buf", { Qbuf, 0, 0, }, 0, 0440, + "epid", { Qepid, 0, 0 }, 0, 0440, +}; + +enum { + CMstop, + CMgo, +}; + +static Cmdtab dtracyctlmsg[] = { + CMstop, "stop", 1, + CMgo, "go", 1, +}; + +DTKChan **dtktab; +int ndtktab; + +static DTKChan * +dtklook(vlong n) +{ + if((uvlong)n >= ndtktab) return nil; + return dtktab[n]; +} +#define QIDPATH(q,e) ((q) + 1 << 8 | (e)) +#define SLOT(q) ((vlong)((q).path >> 8) - 1) +#define FILE(q) ((int)(q).path & 0xff) + +static DTKChan * +dtknew(void) +{ + DTKChan *p; + DTKChan **newtab; + int i; + + p = malloc(sizeof(DTKChan)); + if(p == nil) error(Enomem); + for(i = 0; i < ndtktab; i++) + if(dtktab[i] == nil){ + dtktab[i] = p; + p->idx = i; + break; + } + if(i == ndtktab){ + newtab = realloc(dtktab, (ndtktab + 1) * sizeof(DTKChan *)); + if(newtab == nil) error(Enomem); + dtktab = newtab; + dtktab[ndtktab] = p; + p->idx = ndtktab++; + } + p->ch = dtcnew(); + return p; +} + +static void +dtkfree(DTKChan *p) +{ + int idx; + + idx = p->idx; + dtcfree(p->ch); + free(p); + dtktab[idx] = nil; +} + +static void +dtracyinit(void) +{ + machlocks = smalloc(sizeof(Lock) * conf.nmach); + dtinit(conf.nmach); +} + +static Chan* +dtracyattach(char *spec) +{ + return devattach(L'Δ', spec); +} + +static int +dtracygen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) +{ + Dirtab *tab; + uvlong path; + + if(s == DEVDOTDOT){ + devdir(c, (Qid){Qdir, 0, QTDIR}, "#Δ", 0, eve, 0555, dp); + return 1; + } + if(c->qid.path == Qdir){ + if(s-- == 0) goto clone; + if(s >= ndtktab) return -1; + if(dtklook(s) == nil) return 0; + sprint(up->genbuf, "%d", s); + devdir(c, (Qid){QIDPATH(s, Qdir), 0, QTDIR}, up->genbuf, 0, eve, DMDIR|0555, dp); + return 1; + } + if(c->qid.path == Qclone){ + clone: + strcpy(up->genbuf, "clone"); + devdir(c, (Qid){Qclone, 0, QTFILE}, up->genbuf, 0, eve, 0444, dp); + return 1; + } + if(s >= nelem(dtracydir)) + return -1; + tab = &dtracydir[s]; + path = QIDPATH(SLOT(c->qid), 0); + devdir(c, (Qid){tab->qid.path|path, tab->qid.vers, tab->qid.type}, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +dtracywalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *rc; + + eqlock(&dtracylock); + if(waserror()){ + qunlock(&dtracylock); + nexterror(); + } + rc = devwalk(c, nc, name, nname, nil, 0, dtracygen); + qunlock(&dtracylock); + poperror(); + return rc; +} + +static int +dtracystat(Chan *c, uchar *dp, int n) +{ + int rc; + + eqlock(&dtracylock); + if(waserror()){ + qunlock(&dtracylock); + nexterror(); + } + rc = devstat(c, dp, n, nil, 0, dtracygen); + qunlock(&dtracylock); + poperror(); + return rc; +} + +static Chan* +dtracyopen(Chan *c, int omode) +{ + DTKChan *p; + Chan *ch; + + eqlock(&dtracylock); + if(waserror()){ + qunlock(&dtracylock); + nexterror(); + } + if(c->qid.path == Qclone){ + if(!iseve()) error(Eperm); + p = dtknew(); + c->qid.path = QIDPATH(p->idx, Qctl); + } + p = dtklook(SLOT(c->qid)); + if(SLOT(c->qid) >= 0 && p == nil) error(Enonexist); + if(FILE(c->qid) != Qdir && !iseve()) error(Eperm); + ch = devopen(c, omode, nil, 0, dtracygen); + if(p != nil) p->ref++; + qunlock(&dtracylock); + poperror(); + ch->aux = smalloc(sizeof(DTKAux)); + return ch; +} + +static void +dtracyclose(Chan *ch) +{ + DTKAux *aux; + DTKChan *p; + + if(ch->aux != nil){ + eqlock(&dtracylock); + p = dtklook(SLOT(ch->qid)); + if(p != nil && --p->ref == 0) + dtkfree(p); + qunlock(&dtracylock); + aux = ch->aux; + free(aux->str); + free(ch->aux); + ch->aux = nil; + } +} + +static int +epidread(DTKAux *aux, DTChan *c, char *a, long n, vlong off) +{ + Fmt f; + DTEnab *e; + + if(off == 0){ + free(aux->str); + aux->str = nil; + } + if(aux->str == nil){ + fmtstrinit(&f); + for(e = c->enab; e != nil; e = e->channext){ + fmtprint(&f, "%d %d %d %s:%s:%s\n", e->epid, e->gr->id, e->gr->reclen, + e->prob->provider == nil ? "" : e->prob->provider, + e->prob->function == nil ? "" : e->prob->function, + e->prob->name == nil ? "" : e->prob->name); + } + aux->str = fmtstrflush(&f); + } + return readstr(off, a, n, aux->str); +} + +static long +dtracyread(Chan *c, void *a, long n, vlong off) +{ + int rc; + DTKChan *p; + + eqlock(&dtracylock); + if(waserror()){ + qunlock(&dtracylock); + nexterror(); + } + if(SLOT(c->qid) == -1) + switch((int)c->qid.path){ + case Qdir: + rc = devdirread(c, a, n, nil, 0, dtracygen); + goto out; + default: + error(Egreg); + } + p = dtklook(SLOT(c->qid)); + if(p == nil) error(Enonexist); + switch(FILE(c->qid)){ + case Qdir: + rc = devdirread(c, a, n, nil, 0, dtracygen); + break; + case Qctl: + sprint(up->genbuf, "%d", p->idx); + rc = readstr(off, a, n, up->genbuf); + break; + case Qbuf: + while(rc = dtcread(p->ch, a, n), rc == 0) + tsleep(&up->sleep, return0, 0, 250); + break; + case Qepid: + rc = epidread(c->aux, p->ch, a, n, off); + break; + default: + error(Egreg); + return 0; + } +out: + qunlock(&dtracylock); + poperror(); + return rc; +} + +static long +dtracywrite(Chan *c, void *a, long n, vlong) +{ + int rc; + DTKChan *p; + Cmdbuf *cb; + Cmdtab *ct; + + eqlock(&dtracylock); + if(waserror()){ + qunlock(&dtracylock); + nexterror(); + } + if(SLOT(c->qid) == -1) + switch((int)c->qid.path){ + case Qdir: + error(Eperm); + default: + error(Egreg); + } + p = dtklook(SLOT(c->qid)); + if(p == nil) error(Enonexist); + switch(FILE(c->qid)){ + case Qdir: + error(Eperm); + return 0; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, dtracyctlmsg, nelem(dtracyctlmsg)); + switch(ct->index){ + case CMstop: dtcrun(p->ch, DTCSTOP); break; + case CMgo: dtcrun(p->ch, DTCGO); break; + default: + error(Egreg); + } + poperror(); + free(cb); + rc = n; + break; + case Qprog: + { + char *buf; + + buf = smalloc(n+1); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, a, n); + prog(p, buf); + free(buf); + poperror(); + rc = n; + break; + } + default: + error(Egreg); + return 0; + } + qunlock(&dtracylock); + poperror(); + return rc; +} + + +Dev dtracydevtab = { + L'Δ', + "dtracy", + + devreset, + dtracyinit, + devshutdown, + dtracyattach, + dtracywalk, + dtracystat, + dtracyopen, + devcreate, + dtracyclose, + dtracyread, + devbread, + dtracywrite, + devbwrite, + devremove, + devwstat, +}; + +void * +dtmalloc(ulong n) +{ + void *v; + + v = smalloc(n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +void +dtfree(void *v) +{ + free(v); +} + +void * +dtrealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(v != nil) + setrealloctag(v, getcallerpc(&v)); + return v; +} + +void +dtmachlock(int i) +{ + ilock(&machlocks[i]); +} + +void +dtmachunlock(int i) +{ + iunlock(&machlocks[i]); +} + +void +dtcoherence(void) +{ + coherence(); +} + +uvlong +dttime(void) +{ + return fastticks(nil); +} + +uvlong +dtgetvar(int v) +{ + switch(v){ + case DTV_PID: + return up != nil ? up->pid : 0; + case DTV_MACHNO: + return m->machno; + default: + return 0; + } +} + +int +dtpeek(uvlong addr, void *buf, int len) +{ + if((uintptr)addr != addr || up == nil || !okaddr((uintptr) addr, len, 0)) return -1; + memmove(buf, (void *) addr, len); + return 0; +} + +static DTProbe *timerprobe; + +static void +dtracytimer(void *) +{ + for(;;){ + tsleep(&up->sleep, return0, nil, 1000); + dtptrigger(timerprobe, m->machno, 0, 0, 0, 0); + } +} + +static void +timerprovide(DTProvider *prov, DTName) +{ + static int provided; + + if(provided) return; + provided = 1; + timerprobe = dtpnew((DTName){"timer", "", "1s"}, prov, nil); +} + +static int +timerenable(DTProbe *) +{ + static int gotkproc; + + if(!gotkproc){ + kproc("dtracytimer", dtracytimer, nil); + gotkproc=1; + } + return 0; +} + +static void +timerdisable(DTProbe *) +{ +} + +DTProvider dtracyprov_timer = { + .name = "timer", + .provide = timerprovide, + .enable = timerenable, + .disable = timerdisable, +}; + +extern DTProvider dtracyprov_sys; + +DTProvider *dtproviders[] = { + &dtracyprov_timer, + &dtracyprov_sys, + nil, +}; + diff --git a/sys/src/9/port/dtracysys.c b/sys/src/9/port/dtracysys.c new file mode 100644 index 000000000..019ba4634 --- /dev/null +++ b/sys/src/9/port/dtracysys.c @@ -0,0 +1,249 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "/sys/src/libc/9syscall/sys.h" + +#include +#include + +static DTProbe **dtpsysentry, **dtpsysreturn; + +typedef uintptr Syscall(va_list); +extern Syscall *systab[]; + +#define WRAP0(x,y,z)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + dtptrigger(dtpsysentry[y], m->machno, 0, 0, 0, 0);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, 0, 0, 0, 0);\ + return rc;\ +} +#define WRAP1(x,y,z,type0)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + va_list vb = va;\ + type0 arg0 = va_arg(vb, type0);\ + dtptrigger(dtpsysentry[y], m->machno, (uvlong)arg0, 0, 0, 0);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, (uvlong)arg0, 0, 0, 0);\ + return rc;\ +} +#define WRAP2(x,y,z,type0,type1)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + va_list vb = va;\ + type0 arg0 = va_arg(vb, type0);\ + type1 arg1 = va_arg(vb, type1);\ + dtptrigger(dtpsysentry[y], m->machno, (uvlong)arg0, (uvlong)arg1, 0, 0);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, (uvlong)arg0, (uvlong)arg1, 0, 0);\ + return rc;\ +} +#define WRAP3(x,y,z,type0,type1,type2)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + va_list vb = va;\ + type0 arg0 = va_arg(vb, type0);\ + type1 arg1 = va_arg(vb, type1);\ + type2 arg2 = va_arg(vb, type2);\ + dtptrigger(dtpsysentry[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, 0);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, 0);\ + return rc;\ +} +#define WRAP4(x,y,z,type0,type1,type2,type3)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + va_list vb = va;\ + type0 arg0 = va_arg(vb, type0);\ + type1 arg1 = va_arg(vb, type1);\ + type2 arg2 = va_arg(vb, type2);\ + type3 arg3 = va_arg(vb, type3);\ + dtptrigger(dtpsysentry[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, (uvlong)arg3);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, (uvlong)arg3);\ + return rc;\ +} +/*TODO*/ +#define WRAP5(x,y,z,type0,type1,type2,type3,type4)\ + Syscall z; uintptr x(va_list va){\ + uintptr rc;\ + va_list vb = va;\ + type0 arg0 = va_arg(vb, type0);\ + type1 arg1 = va_arg(vb, type1);\ + type2 arg2 = va_arg(vb, type2);\ + type3 arg3 = va_arg(vb, type3);\ + dtptrigger(dtpsysentry[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, (uvlong)arg3);\ + rc = z(va);\ + dtptrigger(dtpsysreturn[y], m->machno, (uvlong)arg0, (uvlong)arg1, (uvlong)arg2, (uvlong)arg3);\ + return rc;\ +} + +WRAP0(dtwrap_sysr1, SYSR1, sysr1) +WRAP1(dtwrap_sys_errstr, _ERRSTR, sys_errstr, char*) +WRAP3(dtwrap_sysbind, BIND, sysbind, char*, char*, int) +WRAP1(dtwrap_syschdir, CHDIR, syschdir, char*) +WRAP1(dtwrap_sysclose, CLOSE, sysclose, int) +WRAP2(dtwrap_sysdup, DUP, sysdup, int, int) +WRAP1(dtwrap_sysalarm, ALARM, sysalarm, ulong) +WRAP2(dtwrap_sysexec, EXEC, sysexec, char *, char **) +WRAP1(dtwrap_sysexits, EXITS, sysexits, char *) +WRAP3(dtwrap_sys_fsession, _FSESSION, sys_fsession, int, char *, uint) +WRAP2(dtwrap_sysfauth, FAUTH, sysfauth, int, char *) +WRAP2(dtwrap_sys_fstat, _FSTAT, sys_fstat, int, uchar *) +WRAP1(dtwrap_syssegbrk, SEGBRK, syssegbrk, void *) +WRAP4(dtwrap_sys_mount, _MOUNT, sys_mount, int, char *, int, char *) +WRAP2(dtwrap_sysopen, OPEN, sysopen, char *, int) +WRAP3(dtwrap_sys_read, _READ, sys_read, int, void*, long) +WRAP3(dtwrap_sysoseek, OSEEK, sysoseek, int, long, int) +WRAP1(dtwrap_syssleep, SLEEP, syssleep, long) +WRAP2(dtwrap_sys_stat, _STAT, sys_stat, char *, uchar *) +WRAP1(dtwrap_sysrfork, RFORK, sysrfork, int) +WRAP3(dtwrap_sys_write, _WRITE, sys_write, int, void *, long) +WRAP1(dtwrap_syspipe, PIPE, syspipe, int*) +WRAP3(dtwrap_syscreate, CREATE, syscreate, char*, int, int) +WRAP3(dtwrap_sysfd2path, FD2PATH, sysfd2path, int, char*, uint) +WRAP1(dtwrap_sysbrk_, BRK_, sysbrk_, uintptr) +WRAP1(dtwrap_sysremove, REMOVE, sysremove, char *) +WRAP0(dtwrap_sys_wstat, _WSTAT, sys_wstat) +WRAP0(dtwrap_sys_fwstat, _FWSTAT, sys_fwstat) +WRAP2(dtwrap_sysnotify, NOTIFY, sysnotify, char *, void *) +WRAP1(dtwrap_sysnoted, NOTED, sysnoted, int) +WRAP4(dtwrap_syssegattach, SEGATTACH, syssegattach, int, char *, uintptr, ulong) +WRAP1(dtwrap_syssegdetach, SEGDETACH, syssegdetach, uintptr) +WRAP2(dtwrap_syssegfree, SEGFREE, syssegfree, uintptr, ulong) +WRAP2(dtwrap_syssegflush, SEGFLUSH, syssegflush, void*, ulong) +WRAP2(dtwrap_sysrendezvous, RENDEZVOUS, sysrendezvous, uintptr, uintptr) +WRAP2(dtwrap_sysunmount, UNMOUNT, sysunmount, char *, char *) +WRAP1(dtwrap_sys_wait, _WAIT, sys_wait, void*) +WRAP2(dtwrap_syssemacquire, SEMACQUIRE, syssemacquire, long*, int) +WRAP2(dtwrap_syssemrelease, SEMRELEASE, syssemrelease, long*, long) +WRAP4(dtwrap_sysfversion, FVERSION, sysfversion, int, int, char *, int) +WRAP2(dtwrap_syserrstr, ERRSTR, syserrstr, char *, uint) +WRAP3(dtwrap_sysstat, STAT, sysstat, char *, uchar *, uint) +WRAP3(dtwrap_sysfstat, FSTAT, sysfstat, int, uchar *, uint) +WRAP3(dtwrap_syswstat, WSTAT, syswstat, char *, uchar *, uint) +WRAP3(dtwrap_sysfwstat, FWSTAT, sysfwstat, int, uchar *, uint) +WRAP5(dtwrap_sysmount, MOUNT, sysmount, int, int, char *, int, char *) +WRAP2(dtwrap_sysawait, AWAIT, sysawait, char *, uint) +WRAP4(dtwrap_syspread, PREAD, syspread, int, void *, long, vlong) +WRAP4(dtwrap_syspwrite, PWRITE, syspwrite, int, void *, long, vlong) +WRAP2(dtwrap_systsemacquire, TSEMACQUIRE, systsemacquire, long *, ulong) + + +/* TODO: amd64 */ +WRAP4(dtwrap_sysseek, SEEK, sysseek, vlong*, int, vlong, int) +WRAP1(dtwrap_sys_nsec, _NSEC, sys_nsec, vlong*) + +static Syscall *wraptab[]={ + [SYSR1] dtwrap_sysr1, + [_ERRSTR] dtwrap_sys_errstr, + [BIND] dtwrap_sysbind, + [CHDIR] dtwrap_syschdir, + [CLOSE] dtwrap_sysclose, + [DUP] dtwrap_sysdup, + [ALARM] dtwrap_sysalarm, + [EXEC] dtwrap_sysexec, + [EXITS] dtwrap_sysexits, + [_FSESSION] dtwrap_sys_fsession, + [FAUTH] dtwrap_sysfauth, + [_FSTAT] dtwrap_sys_fstat, + [SEGBRK] dtwrap_syssegbrk, + [_MOUNT] dtwrap_sys_mount, + [OPEN] dtwrap_sysopen, + [_READ] dtwrap_sys_read, + [OSEEK] dtwrap_sysoseek, + [SLEEP] dtwrap_syssleep, + [_STAT] dtwrap_sys_stat, + [RFORK] dtwrap_sysrfork, + [_WRITE] dtwrap_sys_write, + [PIPE] dtwrap_syspipe, + [CREATE] dtwrap_syscreate, + [FD2PATH] dtwrap_sysfd2path, + [BRK_] dtwrap_sysbrk_, + [REMOVE] dtwrap_sysremove, + [_WSTAT] dtwrap_sys_wstat, + [_FWSTAT] dtwrap_sys_fwstat, + [NOTIFY] dtwrap_sysnotify, + [NOTED] dtwrap_sysnoted, + [SEGATTACH] dtwrap_syssegattach, + [SEGDETACH] dtwrap_syssegdetach, + [SEGFREE] dtwrap_syssegfree, + [SEGFLUSH] dtwrap_syssegflush, + [RENDEZVOUS] dtwrap_sysrendezvous, + [UNMOUNT] dtwrap_sysunmount, + [_WAIT] dtwrap_sys_wait, + [SEMACQUIRE] dtwrap_syssemacquire, + [SEMRELEASE] dtwrap_syssemrelease, + [SEEK] dtwrap_sysseek, + [FVERSION] dtwrap_sysfversion, + [ERRSTR] dtwrap_syserrstr, + [STAT] dtwrap_sysstat, + [FSTAT] dtwrap_sysfstat, + [WSTAT] dtwrap_syswstat, + [FWSTAT] dtwrap_sysfwstat, + [MOUNT] dtwrap_sysmount, + [AWAIT] dtwrap_sysawait, + [PREAD] dtwrap_syspread, + [PWRITE] dtwrap_syspwrite, + [TSEMACQUIRE] dtwrap_systsemacquire, + [_NSEC] dtwrap_sys_nsec, +}; + +static void +sysprovide(DTProvider *prov, DTName) +{ + static int provided; + char buf[32]; + int i; + + if(provided) return; + provided = 1; + dtpsysentry = smalloc(sizeof(Syscall *) * nsyscall); + dtpsysreturn = smalloc(sizeof(Syscall *) * nsyscall); + for(i = 0; i < nsyscall; i++){ + if(systab[i] == nil || sysctab[i] == nil) continue; + strecpy(buf, buf + sizeof(buf), sysctab[i]); + if(isupper(buf[0])) buf[0] += 'a' - 'A'; + if(i == SYSR1) strcpy(buf, "r1"); + dtpsysentry[i] = dtpnew((DTName){"sys", buf, "entry"}, prov, (void *) i); + dtpsysreturn[i] = dtpnew((DTName){"sys", buf, "return"}, prov, (void *) i); + } +} + +static int +sysenable(DTProbe *p) +{ + int i; + Syscall *z; + + i = (int) p->aux; + assert(i >= 0 && i < nsyscall); + if(dtpsysentry[i]->nenable + dtpsysreturn[i]->nenable == 0) + z = systab[i], systab[i] = wraptab[i], wraptab[i] = z; + return 0; +} + +static void +sysdisable(DTProbe *p) +{ + int i; + Syscall *z; + + i = (int) p->aux; + assert(i >= 0 && i < nsyscall); + if(dtpsysentry[i]->nenable + dtpsysreturn[i]->nenable == 0) + z = systab[i], systab[i] = wraptab[i], wraptab[i] = z; +} + +DTProvider dtracyprov_sys = { + .name = "sys", + .provide = sysprovide, + .enable = sysenable, + .disable = sysdisable, +}; diff --git a/sys/src/cmd/dtracy/act.c b/sys/src/cmd/dtracy/act.c new file mode 100644 index 000000000..335d1bd23 --- /dev/null +++ b/sys/src/cmd/dtracy/act.c @@ -0,0 +1,571 @@ +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +/* this contains the code to prepare the kernel data structures and to parse records */ + +Clause *clause; +Clause **clauses; +int nclauses; + +/* we could just rely on the types in the expression tree but i'm paranoid */ +typedef struct Val Val; +struct Val { + enum { + VALINT, + VALSTR, + } type; + union { + vlong v; + char *s; + }; +}; + +Val +mkval(int type, ...) +{ + Val r; + va_list va; + + r.type = type; + va_start(va, type); + switch(type){ + case VALINT: r.v = va_arg(va, uvlong); break; + case VALSTR: r.s = va_arg(va, char*); break; + } + va_end(va); + return r; +} + +void +clausebegin(void) +{ + clause = emalloc(sizeof(Clause)); + clause->id = nclauses; +} + +void +addprobe(char *s) +{ + clause->probs = erealloc(clause->probs, sizeof(char *) * (clause->nprob + 1)); + clause->probs[clause->nprob++] = strdup(s); +} + +void +addstat(int type, ...) +{ + Stat *s; + va_list va; + + clause->stats = erealloc(clause->stats, sizeof(Stat) * (clause->nstats + 1)); + s = &clause->stats[clause->nstats++]; + memset(s, 0, sizeof(Stat)); + s->type = type; + va_start(va, type); + switch(type){ + case STATEXPR: + s->n = va_arg(va, Node *); + break; + case STATPRINT: + case STATPRINTF: + break; + default: + sysfatal("addstat: unknown type %d", type); + } + va_end(va); +} + +void +addarg(Node *n) +{ + Stat *s; + + assert(clause->nstats > 0); + s = &clause->stats[clause->nstats - 1]; + s->arg = erealloc(s->arg, sizeof(Node *) * (s->narg + 1)); + s->arg[s->narg++] = n; +} + +void +clauseend(void) +{ + clauses = erealloc(clauses, sizeof(Clause) * (nclauses + 1)); + clauses[nclauses++] = clause; +} + +void +actgradd(DTActGr *a, DTAct b) +{ + a->acts = erealloc(a->acts, sizeof(DTAct) * (a->nact + 1)); + a->acts[a->nact++] = b; +} + +void +addpred(DTExpr *e) +{ + clause->pred = e; +} + +static void +prepprintf(Node **arg, int narg, DTActGr *g, int *recoff) +{ + char *fmt; + int n; + Fmt f; + + if(narg <= 0) sysfatal("printf() needs an argument"); + if((*arg)->type != OSTR) sysfatal("printf() format string must be a literal"); + fmt = (*arg)->str; + fmtstrinit(&f); + n = 1; + for(; *fmt != 0; fmt++){ + fmtrune(&f, *fmt); + if(*fmt != '%') + continue; + fmt++; + again: + switch(*fmt){ + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + case 'u': case '+': case '-': case ',': case '#': case ' ': case '.': + fmtrune(&f, *fmt); + fmt++; + goto again; + case 'x': case 'X': case 'o': case 'b': case 'd': + if(n >= narg) sysfatal("printf() too few arguments"); + if(arg[n]->typ->type != TYPINT) sysfatal("print() %%%c with non-integer", *fmt); + arg[n] = tracegen(arg[n], g, recoff); + n++; + fmtrune(&f, 'l'); + fmtrune(&f, 'l'); + fmtrune(&f, *fmt); + break; + case 's': + if(n >= narg) sysfatal("printf() too few arguments"); + if(arg[n]->typ->type != TYPSTRING) sysfatal("print() %%s with non-string"); + arg[n] = tracegen(arg[n], g, recoff); + n++; + fmtrune(&f, *fmt); + break; + case 0: sysfatal("printf() missing verb"); + default: sysfatal("printf() unknown verb %%%c", *fmt); + } + } + if(n < narg) sysfatal("printf() too many arguments"); + (*arg)->str = fmtstrflush(&f); +} + +DTClause * +mkdtclause(Clause *c) +{ + DTClause *d; + Stat *s; + int recoff, i; + + d = emalloc(sizeof(DTClause)); + d->nprob = c->nprob; + d->probs = c->probs; + d->gr = emalloc(sizeof(DTActGr)); + d->gr->pred = c->pred; + d->gr->id = c->id; + recoff = 12; + for(s = c->stats; s < c->stats + c->nstats; s++) + switch(s->type){ + case STATEXPR: + actgradd(d->gr, (DTAct){ACTTRACE, codegen(s->n), 0}); + break; + case STATPRINT: + for(i = 0; i < s->narg; i++) + s->arg[i] = tracegen(s->arg[i], d->gr, &recoff); + break; + case STATPRINTF: + prepprintf(s->arg, s->narg, d->gr, &recoff); + break; + } + return d; +} + +void +packclauses(Fmt *f) +{ + int i; + DTClause *d; + + for(i = 0; i < nclauses; i++){ + d = mkdtclause(clauses[i]); + dtclpack(f, d); + } +} + +/* epid lookup table, filled with info from the kernel */ +Enab *enabtab[1024]; + +void +addepid(u32int epid, u32int cid, int reclen, char *p) +{ + Enab *e, **ep; + + assert(cid < nclauses); + assert((uint)reclen >= 12); + e = emalloc(sizeof(Enab)); + e->epid = epid; + e->cl = clauses[cid]; + e->reclen = reclen; + e->probe = strdup(p); + ep = &enabtab[epid % nelem(enabtab)]; + e->next = *ep; + *ep = e; +} + +Enab * +epidlookup(u32int epid) +{ + Enab *e; + + for(e = enabtab[epid % nelem(enabtab)]; e != nil; e = e->next) + if(e->epid == epid) + return e; + return nil; +} + +uchar * +unpack(uchar *p, uchar *e, char *fmt, ...) +{ + va_list va; + u64int vl; + + va_start(va, fmt); + for(;;) + switch(*fmt++){ + case 'c': + if(p + 1 > e) return nil; + *va_arg(va, u8int *) = p[0]; + p += 1; + break; + case 's': + if(p + 2 > e) return nil; + *va_arg(va, u16int *) = p[0] | p[1] << 8; + p += 2; + break; + case 'i': + if(p + 4 > e) return nil; + *va_arg(va, u32int *) = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; + p += 4; + break; + case 'v': + if(p + 8 > e) return nil; + vl = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; + vl |= (uvlong)p[4] << 32 | (uvlong)p[5] << 40 | (uvlong)p[6] << 48 | (uvlong)p[7] << 56; + *va_arg(va, u64int *) = vl; + p += 8; + break; + case 0: + return p; + default: + abort(); + } +} + +static Val +receval(Node *n, uchar *p, uchar *e, Enab *en) +{ + u8int c; + u16int s; + u32int i; + uvlong v; + char *sp; + uchar *q; + Val a, b; + + switch(n->type){ + case OSYM: + switch(n->sym->type){ + case SYMVAR: + switch(n->sym->idx){ + case DTV_TIME: + q = unpack(p + 4, e, "v", &v); + assert(q != nil); + return mkval(VALINT, v); + case DTV_PROBE: + return mkval(VALSTR, en->probe); + default: sysfatal("receval: unknown variable %d", n->type); return mkval(VALINT, 0LL); + } + break; + default: sysfatal("receval: unknown symbol type %d", n->type); return mkval(VALINT, 0LL); + } + case ONUM: return mkval(VALINT, n->num); + case OBIN: + a = receval(n->n1, p, e, en); + b = receval(n->n2, p, e, en); + assert(a.type == VALINT); + assert(b.type == VALINT); + return mkval(VALINT, evalop(n->op, n->typ->sign, a.v, b.v)); + case OLNOT: + a = receval(n->n1, p, e, en); + assert(a.type == VALINT); + return mkval(VALINT, (uvlong) !a.v); + case OTERN: + a = receval(n->n1, p, e, en); + assert(a.type == VALINT); + return a.v ? receval(n->n2, p, e, en) : receval(n->n3, p, e, en); + case ORECORD: + switch(n->typ->type){ + case TYPINT: + switch(n->typ->size){ + case 1: q = unpack(p + n->num, e, "c", &c); v = n->typ->sign ? (s8int)c : (u8int)c; break; + case 2: q = unpack(p + n->num, e, "s", &s); v = n->typ->sign ? (s16int)s : (u16int)s; break; + case 4: q = unpack(p + n->num, e, "i", &i); v = n->typ->sign ? (s32int)i : (u32int)i; break; + case 8: q = unpack(p + n->num, e, "v", &v); break; + default: q = nil; + } + assert(q != nil); + return mkval(VALINT, v); + case TYPSTRING: + assert(p + n->num + n->typ->size <= e); + sp = emalloc(n->typ->size + 1); + memcpy(sp, p + n->num, n->typ->size); + return mkval(VALSTR, sp); /* TODO: fix leak */ + default: + sysfatal("receval: don't know how to parse record for %τ", n->typ); + } + default: + sysfatal("receval: unknown type %α", n->type); + return mkval(VALINT, 0LL); + } +} + +static void +execprintf(Node **arg, int narg, uchar *p, uchar *e, Enab *en) +{ + char *x, *xp; + Val v; + int i; + + x = emalloc(sizeof(uvlong) * (narg - 1)); + xp = x; + for(i = 0; i < narg - 1; i++){ + v = receval(arg[i + 1], p, e, en); + switch(v.type){ + case VALINT: + *(uvlong*)xp = v.v; + xp += sizeof(uvlong); + break; + case VALSTR: + *(char**)xp = v.s; + xp += sizeof(char*); + break; + default: abort(); + } + } + vfprint(1, (*arg)->str, (va_list) x); + free(x); +} + +int +parseclause(Clause *cl, uchar *p, uchar *e, Enab *en, Biobuf *bp) +{ + Stat *s; + int i; + Val v; + + for(s = cl->stats; s < cl->stats + cl->nstats; s++) + switch(s->type){ + case STATEXPR: break; + case STATPRINT: + for(i = 0; i < s->narg; i++){ + v = receval(s->arg[i], p, e, en); + switch(v.type){ + case VALINT: + Bprint(bp, "%lld", v.v); + break; + case VALSTR: + Bprint(bp, "%s", v.s); + break; + default: sysfatal("parseclause: unknown val type %d", s->type); + } + Bprint(bp, "%c", i == s->narg - 1 ? '\n' : ' '); + } + break; + case STATPRINTF: + execprintf(s->arg, s->narg, p, e, en); + break; + default: + sysfatal("parseclause: unknown type %d", s->type); + } + return 0; +} + +int +parsebuf(uchar *p, int n, Biobuf *bp) +{ + uchar *e; + u32int epid; + u64int ts; + Enab *en; + + e = p + n; + while(p < e){ + p = unpack(p, e, "iv", &epid, &ts); + if(p == nil) goto err; + en = epidlookup(epid); + if(en == nil) goto err; + if(parseclause(en->cl, p - 12, p + en->reclen - 12, en, bp) < 0) return -1; + p += en->reclen - 12; + } + return 0; +err: + werrstr("buffer invalid"); + return -1; +} + +static void +dumpexpr(DTExpr *e, char *prefix) +{ + int i; + + for(i = 0; i < e->n; i++) + print("%s%.8ux %I\n", prefix, e->b[i], e->b[i]); +} + +#pragma varargck type "ε" Node* + +static void +fmtstring(Fmt *f, char *s) +{ + fmtrune(f, '"'); + for(; *s != 0; s++) + switch(*s){ + case '\n': fmtprint(f, "\\n"); break; + case '\r': fmtprint(f, "\\r"); break; + case '\t': fmtprint(f, "\\t"); break; + case '\v': fmtprint(f, "\\v"); break; + case '\b': fmtprint(f, "\\b"); break; + case '\a': fmtprint(f, "\\a"); break; + case '"': fmtprint(f, "\""); break; + case '\\': fmtprint(f, "\\"); break; + default: + if(*s < 0x20 || *s >= 0x7f) + fmtprint(f, "\\%.3o", (uchar)*s); + else + fmtrune(f, *s); + } + fmtrune(f, '"'); +} + +typedef struct Op Op; +struct Op { + char *name; + int pred; + enum { PRECRIGHT = 1 } flags; +}; +static Op optab[] = { + [OPLOR] {"||", 3, 0}, + [OPLAND] {"&&", 4, 0}, + [OPOR] {"|", 5, 0}, + [OPXNOR] {"~^", 6, 0}, + [OPXOR] {"^", 6, 0}, + [OPAND] {"&", 7, 0}, + [OPEQ] {"==", 8, }, + [OPNE] {"!=", 8, 0}, + [OPLE] {"<=", 9, 0}, + [OPLT] {"<", 9, 0}, + [OPLSH] {"<<", 10, 0}, + [OPRSH] {">>", 10, 0}, + [OPADD] {"+", 11, 0}, + [OPSUB] {"-", 11, 0}, + [OPDIV] {"/", 12, 0}, + [OPMOD] {"%", 12, 0}, + [OPMUL] {"*", 12, 0}, +}; +enum { PREDUNARY = 14 }; + +int +nodefmt(Fmt *f) +{ + Node *n; + Op *op; + int p; + + p = f->width; + n = va_arg(f->args, Node *); + switch(n->type){ + case OSYM: fmtprint(f, "%s", n->sym->name); break; + case ONUM: fmtprint(f, "%lld", n->num); break; + case OSTR: fmtstring(f, n->str); break; + case OBIN: + if(n->op >= nelem(optab) || optab[n->op].name == nil) + fmtprint(f, "(%*ε ??op%d %*ε)", PREDUNARY, n->n1, n->op, PREDUNARY, n->n2); + else{ + op = &optab[n->op]; + if(op->pred < p) fmtrune(f, '('); + fmtprint(f, "%*ε %s %*ε", op->pred + (op->flags & PRECRIGHT), n->n1, op->name, op->pred + (~op->flags & PRECRIGHT), n->n2); + if(op->pred < p) fmtrune(f, ')'); + } + break; + case OLNOT: fmtprint(f, "!%*ε", PREDUNARY, n->n1); break; + case OTERN: fmtprint(f, "%2ε ? %1ε : %1ε", n->n1, n->n2, n->n3); break; + case ORECORD: fmtprint(f, "record(%ε, %τ, %d)", n->n1, n->typ, (int)n->num); break; + case OCAST: fmtprint(f, "(%τ) %*ε", n->typ, PREDUNARY, n->n1); break; + default: fmtprint(f, "??? %α", n->type); + } + return 0; +} + +void +dump(void) +{ + int i, j; + Stat *s; + Clause *c; + DTClause *d; + DTAct *a; + + for(i = 0; i < nclauses; i++){ + c = clauses[i]; + d = mkdtclause(c); + print("clause %d:\n", c->id); + for(j = 0; j < c->nprob; j++) + print("\tprobe '%s'\n", c->probs[j]); + print("\tkernel code:\n"); + if(c->pred == nil) + print("\t\tno predicate\n"); + else{ + print("\t\tpredicate\n"); + dumpexpr(c->pred, "\t\t\t"); + } + for(a = d->gr->acts; a < d->gr->acts + d->gr->nact; a++) + switch(a->type){ + case ACTTRACE: + print("\t\ttrace (%d bytes)\n", a->size); + dumpexpr(a->p, "\t\t\t"); + break; + case ACTTRACESTR: + print("\t\ttrace string (%d bytes)\n", a->size); + dumpexpr(a->p, "\t\t\t"); + break; + default: + print("\t\t??? %d\n", a->type); + } + print("\trecord formatting:\n"); + for(s = c->stats; s < c->stats + c->nstats; s++) + switch(s->type){ + case STATEXPR: + break; + case STATPRINT: + print("\t\tprint\n"); + for(j = 0; j < s->narg; j++) + print("\t\t\targ %ε\n", s->arg[j]); + break; + case STATPRINTF: + print("\t\tprintf\n"); + for(j = 0; j < s->narg; j++) + print("\t\t\targ %ε\n", s->arg[j]); + break; + default: + print("\t\t??? %d\n", s->type); + } + } +} diff --git a/sys/src/cmd/dtracy/cgen.c b/sys/src/cmd/dtracy/cgen.c new file mode 100644 index 000000000..6b4f77d19 --- /dev/null +++ b/sys/src/cmd/dtracy/cgen.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +u16int regsused = 1; +u32int cbuf[256]; +int ncbuf; +int labtab[256]; +int nlab; + +static void +emit(u32int x) +{ + assert(ncbuf < nelem(cbuf)); + cbuf[ncbuf++] = x; +} + +static int +regalloc(void) +{ + u16int v; + int n; + + if(regsused == 0xffff){ + error("out of registers"); + return 0; + } + v = regsused + 1 & ~regsused; + regsused ^= v; + n = 0; + if((u8int)v == 0) {v >>= 8; n += 8;} + if((v & 0xf) == 0) {v >>= 4; n += 4;} + if((v & 3) == 0) {v >>= 2; n += 2;} + return n + (v >> 1); +} + +static void +regfree(int n) +{ + assert((regsused & 1<> 1 & 0x5555555555555555ULL); + s = (s & 0x3333333333333333ULL) + (s >> 2 & 0x3333333333333333ULL); + s = (s & 0x0F0F0F0F0F0F0F0FULL) + (s >> 4 & 0x0F0F0F0F0F0F0F0FULL); + s = (s & 0x00FF00FF00FF00FFULL) + (s >> 8 & 0x00FF00FF00FF00FFULL); + s = (s & 0x0000FFFF0000FFFFULL) + (s >> 16 & 0x0000FFFF0000FFFFULL); + return (u32int)s + (u32int)(s >> 32); +} + +static int +constenc(s64int val) +{ + int i, r; + s64int x; + + r = 0; + do{ + i = popcount(val ^ val - 1) - 1; + x = val << 54 - i >> 54; + if(r == 0){ + r = regalloc(); + emit(DTE_LDI << 24 | (x & 0x3ff) << 14 | i << 8 | r); + }else + emit(DTE_XORI << 24 | (x & 0x3ff) << 14 | i << 8 | r); + val ^= x << i; + }while(val != 0); + return r; +} + +static int egen(Node *); + +static void +condgen(Node *n, int invert, int truelab) +{ + int r1, r2, l1, op; + + if(n->type != OBIN) goto other; + switch(n->op){ + case OPEQ: op = DTE_SEQ; goto cmp; + case OPNE: op = DTE_SNE; goto cmp; + case OPLT: op = DTE_SLT; goto cmp; + case OPLE: op = DTE_SLE; + cmp: + r1 = egen(n->n1); + r2 = egen(n->n2); + if(invert) + emit(DTE(op ^ 1, r2, r1, truelab)); + else + emit(DTE(op, r1, r2, truelab)); + regfree(r1); + regfree(r2); + break; + case OPLOR: + case OPLAND: + if(invert ^ n->op == OPLOR){ + condgen(n->n1, invert, truelab); + condgen(n->n2, invert, truelab); + }else{ + l1 = nlab++; + condgen(n->n1, !invert, l1); + condgen(n->n2, invert, truelab); + labtab[l1] = ncbuf; + } + break; + default: + other: + r1 = egen(n); + emit(DTE(DTE_BNE ^ invert, r1, 0, truelab)); + regfree(r1); + break; + } +} + +static int +condvgen(Node *n, int invert) +{ + int r, l1, l2, op; + + if(n->type == OLNOT) + return condvgen(n->n1, !invert); + if(n->type != OBIN) goto other; + switch(n->op){ + case OPEQ: op = DTE_SEQ; goto cmp; + case OPNE: op = DTE_SNE; goto cmp; + case OPLT: op = DTE_SLT; goto cmp; + case OPLE: op = DTE_SLE; + cmp: + if(invert) + return egen(node(OBIN, op ^ 1, n->n2, n->n1)); + return egen(n); + case OPLOR: + case OPLAND: + if(invert ^ n->op == OPLOR){ + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, invert, l1); + r = condvgen(n->n2, invert); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + emit(DTE(DTE_LDI, 0, 1<<6, r)); + labtab[l2] = ncbuf; + return r; + }else{ + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, invert, l1); + r = condvgen(n->n2, invert); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + emit(DTE(DTE_LDI, 0, 0<<6, r)); + labtab[l2] = ncbuf; + return r; + } + default: + other: + r = egen(n); + emit(DTE(DTE_SNE ^ invert, r, 0, r)); + return r; + } +} + +static int +egen(Node *n) +{ + int r1, r2, rt, l1, l2, op; + + switch(/*nodetype*/n->type){ + case ONUM: + return constenc(n->num); + case OSYM: + switch(n->sym->type){ + case SYMVAR: + rt = regalloc(); + emit(DTE(DTE_LDV, n->sym->idx, rt, 0)); + return rt; + default: sysfatal("egen: unknown symbol type %d", n->sym->type); return 0; + } + case OBIN: + switch(/*oper*/n->op){ + case OPLAND: + case OPLOR: + return condvgen(n, 0); + case OPADD: op = DTE_ADD; break; + case OPSUB: op = DTE_SUB; break; + case OPMUL: op = DTE_MUL; break; + case OPDIV: op = n->typ->sign ? DTE_SDIV : DTE_UDIV; break; + case OPMOD: op = n->typ->sign ? DTE_SMOD : DTE_UMOD; break; + case OPAND: op = DTE_AND; break; + case OPOR: op = DTE_OR; break; + case OPXOR: op = DTE_XOR; break; + case OPLSH: op = DTE_LSL; break; + case OPRSH: op = n->typ->sign ? DTE_ASR : DTE_LSR; break; + case OPEQ: op = DTE_SEQ; break; + case OPNE: op = DTE_SNE; break; + case OPLT: op = DTE_SLT; break; + case OPLE: op = DTE_SLE; break; + case OPXNOR: op = DTE_XNOR; break; + default: sysfatal("egen: unknown op %d", n->op); return 0; + } + r1 = egen(n->n1); + r2 = egen(n->n2); + regfree(r1); + regfree(r2); + rt = regalloc(); + emit(DTE(op, r1, r2, rt)); + return rt; + case OTERN: + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, 1, l1); + r1 = egen(n->n2); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + r2 = egen(n->n3); + if(r1 != r2) + emit(DTE(DTE_OR, 0, r2, r1)); + labtab[l2] = ncbuf; + return r1; + case OLNOT: + return condvgen(n, 0); + case OCAST: + switch(n->typ->type){ + case TYPINT: + r1 = egen(n->n1); + emit(DTE(n->typ->sign ? DTE_SXT : DTE_ZXT, r1, n->typ->size * 8, r1)); + return r1; + case TYPSTRING: + return egen(n->n1); + default: + sysfatal("egen: don't know how to cast %τ to %τ", n->n1->typ, n->typ); + } + case ORECORD: + case OSTR: + default: sysfatal("egen: unknown type %α", n->type); return 0; + } +} + +DTExpr * +codegen(Node *n) +{ + int r, i, t; + DTExpr *ep; + + regsused = 1; + ncbuf = 0; + nlab = 0; + r = egen(n); + emit(DTE(DTE_RET, r, 0, 0)); + + for(i = 0; i < ncbuf; i++) + if((cbuf[i] >> 24 & 0xf0) == 0x30){ + t = labtab[cbuf[i] & 0xff]; + assert((uint)(t - i - 1) < 0x100); + cbuf[i] = cbuf[i] & 0xffffff00 | t - i - 1; + } + + ep = emalloc(sizeof(DTExpr) + ncbuf * sizeof(u32int)); + ep->n = ncbuf; + ep->b = (void *)(ep + 1); + memcpy(ep->b, cbuf, ncbuf * sizeof(u32int)); + return ep; +} + +Node * +tracegen(Node *n, DTActGr *g, int *recoff) +{ + switch(/*nodetype*/n->type){ + case OSYM: + case ONUM: + case OSTR: + break; + case OBIN: + n->n1 = tracegen(n->n1, g, recoff); + n->n2 = tracegen(n->n2, g, recoff); + break; + case OLNOT: + n->n1 = tracegen(n->n1, g, recoff); + break; + case OTERN: + n->n1 = tracegen(n->n1, g, recoff); + n->n2 = tracegen(n->n2, g, recoff); + n->n3 = tracegen(n->n3, g, recoff); + break; + case OCAST: + n->n1 = tracegen(n->n1, g, recoff); + break; + case ORECORD: + switch(n->typ->type){ + case TYPINT: + actgradd(g, (DTAct){ACTTRACE, codegen(n->n1), n->typ->size}); + break; + case TYPSTRING: + actgradd(g, (DTAct){ACTTRACESTR, codegen(n->n1), n->typ->size}); + break; + default: + sysfatal("tracegen: don't know how to record %τ", n->typ); + } + n->num = *recoff; + *recoff += n->typ->size; + return n; + default: sysfatal("tracegen: unknown type %α", n->type); return nil; + } + return n; +} diff --git a/sys/src/cmd/dtracy/dat.h b/sys/src/cmd/dtracy/dat.h new file mode 100644 index 000000000..fc6737b4c --- /dev/null +++ b/sys/src/cmd/dtracy/dat.h @@ -0,0 +1,123 @@ +typedef struct Node Node; +typedef struct Symbol Symbol; +typedef struct SymTab SymTab; +typedef struct Clause Clause; +typedef struct Enab Enab; +typedef struct Stat Stat; +typedef struct Type Type; + +enum { + SYMHASH = 256, +}; + +struct Type { + enum { + TYPINVAL, + TYPINT, + TYPPTR, + TYPSTRING, + } type; + int size; + uchar sign; + Type *ref; + Type *typenext; +}; + +struct Symbol { + enum { + SYMNONE, + SYMVAR, + } type; + char *name; + int idx; + Symbol *next; + Type *typ; +}; + +struct SymTab { + Symbol *sym[SYMHASH]; +}; + +struct Node { + enum { + OINVAL, + OSYM, + ONUM, + OSTR, + OBIN, + OLNOT, + OTERN, + ORECORD, + OCAST, + } type; + enum { + OPINVAL, + OPADD, + OPSUB, + OPMUL, + OPDIV, + OPMOD, + OPAND, + OPOR, + OPXOR, + OPLSH, + OPRSH, + OPEQ, + OPNE, + OPLT, + OPLE, + OPLAND, + OPLOR, + OPXNOR, + } op; + Node *n1, *n2, *n3; + Symbol *sym; + char *str; + s64int num; + + /* used by elidecasts() */ + char databits; + enum {UPZX, UPSX} upper; + + int recsize; + + Type *typ; +}; + +struct Stat { + enum { + STATEXPR, + STATPRINT, + STATPRINTF, + } type; + Node *n; + int narg; + Node **arg; +}; + +struct Clause { + int id; + Stat *stats; + int nstats; + char **probs; + int nprob; + DTExpr *pred; +}; + +struct Enab { + int epid; + int reclen; + char *probe; + Clause *cl; + Enab *next; +}; + +extern int errors; + +#pragma varargck type "α" int +#pragma varargck type "t" int +#pragma varargck type "τ" Type * +#pragma varargck type "ε" Node * +#pragma varargck argpos error 1 + +extern int dflag; diff --git a/sys/src/cmd/dtracy/dtracy.c b/sys/src/cmd/dtracy/dtracy.c new file mode 100644 index 000000000..8585180f2 --- /dev/null +++ b/sys/src/cmd/dtracy/dtracy.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +char *dtracyroot = "#Δ"; +int dtracyno; +int ctlfd, buffd; + +int +min(int a, int b) +{ + return a < b ? a : b; +} + +int +max(int a, int b) +{ + return a < b ? b : a; +} + +void * +emalloc(ulong n) +{ + void *v; + + v = malloc(n); + if(v == nil) sysfatal("malloc: %r"); + memset(v, 0, n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +void * +erealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(n != 0){ + if(v == nil) sysfatal("realloc: %r"); + setrealloctag(v, getcallerpc(&v)); + } + return v; +} + +void * +dtmalloc(ulong n) +{ + return emalloc(n); +} + +void +dtfree(void *v) +{ + free(v); +} + +void +defvar(char *name, int idx, Type *ty) +{ + Symbol *s; + + s = getsym(name); + s->type = SYMVAR; + s->idx = idx; + s->typ = ty; +} + +void +globvars(void) +{ + defvar("arg0", DTV_ARG0, type(TYPINT, 8, 1)); + defvar("arg1", DTV_ARG1, type(TYPINT, 8, 1)); + defvar("arg2", DTV_ARG2, type(TYPINT, 8, 1)); + defvar("arg3", DTV_ARG3, type(TYPINT, 8, 1)); + defvar("arg4", DTV_ARG4, type(TYPINT, 8, 1)); + defvar("arg5", DTV_ARG5, type(TYPINT, 8, 1)); + defvar("arg6", DTV_ARG6, type(TYPINT, 8, 1)); + defvar("arg7", DTV_ARG7, type(TYPINT, 8, 1)); + defvar("arg8", DTV_ARG8, type(TYPINT, 8, 1)); + defvar("arg9", DTV_ARG9, type(TYPINT, 8, 1)); + defvar("pid", DTV_PID, type(TYPINT, 4, 1)); + defvar("machno", DTV_MACHNO, type(TYPINT, 4, 1)); + defvar("time", DTV_TIME, type(TYPINT, 8, 0)); + defvar("probe", DTV_PROBE, type(TYPSTRING)); +} + +int +setup(void) +{ + char buf[512]; + char *p; + int n; + + snprint(buf, sizeof(buf), "%s/clone", dtracyroot); + ctlfd = open(buf, ORDWR); + if(ctlfd < 0) return -1; + n = read(ctlfd, buf, sizeof(buf) - 1); + if(n < 0) return -1; + buf[n] = 0; + dtracyno = strtol(buf, &p, 10); + if(p == buf || *p != 0){ + werrstr("expected number reading from ctl"); + return -1; + } + snprint(buf, sizeof(buf), "%s/%d/buf", dtracyroot, dtracyno); + buffd = open(buf, OREAD); + if(buffd < 0) return -1; + return 0; +} + +int +progcopy(void) +{ + char buf[512]; + int fd; + char *prog; + Fmt f; + + fmtstrinit(&f); + packclauses(&f); + prog = fmtstrflush(&f); + snprint(buf, sizeof(buf), "%s/%d/prog", dtracyroot, dtracyno); + fd = open(buf, OWRITE); + if(fd < 0) return -1; + if(write(fd, prog, strlen(prog)) < 0){ + close(fd); + return -1; + } + close(fd); + return 0; +} + +int +epidread(void) +{ + char buf[512]; + Biobuf *bp; + char *s; + char *f[5]; + int a, b, c; + + snprint(buf, sizeof(buf), "%s/%d/epid", dtracyroot, dtracyno); + bp = Bopen(buf, OREAD); + if(bp == nil) return -1; + for(; s = Brdstr(bp, '\n', 1), s != nil; free(s)){ + if(tokenize(s, f, nelem(f)) != 4) + goto err; + a = atoi(f[0]); + b = atoi(f[1]); + c = atoi(f[2]); + addepid(a, b, c, f[3]); + } + return 0; +err: + werrstr("epidread: invalid format"); + free(s); + return -1; + +} + +void +bufread(Biobuf *bp) +{ + static uchar buf[65536]; + int n; + + n = read(buffd, buf, sizeof(buf)); + if(n < 0) sysfatal("bufread: %r"); + if(parsebuf(buf, n, bp) < 0) + sysfatal("parsebuf: %r"); + Bflush(bp); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [ -cd ] script\n", argv0); + exits("usage"); +} + +int dflag; + +void +main(int argc, char **argv) +{ + Biobuf *out; + + dflag = 0; + ARGBEGIN { + case 'd': dflag = 1; break; + default: usage(); + } ARGEND; + if(argc != 1) usage(); + + fmtinstall(L'α', nodetfmt); + fmtinstall('t', typetfmt); + fmtinstall(L'I', dtefmt); + fmtinstall(L'τ', typefmt); + fmtinstall(L'ε', nodefmt); + lexinit(); + lexstring(argv[0]); + globvars(); + yyparse(); + if(errors != 0) + exits("errors"); + if(dflag) + dump(); + else{ + if(setup() < 0) + sysfatal("setup: %r"); + if(progcopy() < 0) + sysfatal("progcopy: %r"); + if(epidread() < 0) + sysfatal("epidread: %r"); + fprint(ctlfd, "go"); + out = Bfdopen(1, OWRITE); + if(out == nil) sysfatal("Bfdopen: %r"); + for(;;) + bufread(out); + } + exits(nil); +} diff --git a/sys/src/cmd/dtracy/fns.h b/sys/src/cmd/dtracy/fns.h new file mode 100644 index 000000000..f418e4f34 --- /dev/null +++ b/sys/src/cmd/dtracy/fns.h @@ -0,0 +1,35 @@ +void yyerror(char *); +int yylex(void); +int yyparse(void); +Node *node(int, ...); +int nodetfmt(Fmt *); +int typetfmt(Fmt *); +int typefmt(Fmt *); +int nodefmt(Fmt *); +void *emalloc(ulong); +void *erealloc(void *, ulong); +DTAct action(int, DTExpr *); +DTExpr *codegen(Node *); +void error(char *, ...); +Symbol *getsym(char *); +void lexinit(void); +void lexstring(char *); +void clausebegin(void); +void addstat(int, ...); +void addarg(Node *); +void addprobe(char *); +void addpred(DTExpr *); +void clauseend(void); +void packclauses(Fmt *); +void addepid(u32int, u32int, int, char*); +int parsebuf(uchar *, int, Biobuf*); +Node *tracegen(Node *, DTActGr *, int *); +void actgradd(DTActGr *, DTAct); +void needruntime(Node *); +void dump(void); +vlong evalop(int, int, vlong, vlong); +Node *exprcheck(Node *, int); +Type *type(int, ...); +int min(int, int); +int max(int, int); +Node *addtype(Type *, Node *); diff --git a/sys/src/cmd/dtracy/lex.c b/sys/src/cmd/dtracy/lex.c new file mode 100644 index 000000000..15a959ffa --- /dev/null +++ b/sys/src/cmd/dtracy/lex.c @@ -0,0 +1,390 @@ +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "y.tab.h" + +char *str, *strp; +int lineno = 1; +int errors; + +typedef struct Keyword Keyword; +struct Keyword { + char *name; + int tok; +}; +/* both tables must be sorted */ +Keyword kwtab[] = { + "if", TIF, + "print", TPRINT, + "printf", TPRINTF, + "s16", TS16, + "s32", TS32, + "s64", TS64, + "s8", TS8, + "string", TSTRING, + "u16", TU16, + "u32", TU32, + "u64", TU64, + "u8", TU8, +}; +Keyword optab[] = { + "!=", TNE, + "&&", TAND, + "<<", TLSL, + "<=", TLE, + "==", TEQ, + ">=", TGE, + ">>", TLSR, + "||", TOR, +}; +Keyword *kwchar[128], *opchar[128]; + +void +lexinit(void) +{ + Keyword *kw; + + for(kw = kwtab; kw < kwtab + nelem(kwtab); kw++) + if(kwchar[*kw->name] == nil) + kwchar[*kw->name] = kw; + for(kw = optab; kw < optab + nelem(optab); kw++) + if(opchar[*kw->name] == nil) + opchar[*kw->name] = kw; +} + +void +lexstring(char *s) +{ + str = strp = s; +} + +void +error(char *fmt, ...) +{ + Fmt f; + char buf[128]; + va_list va; + + fmtfdinit(&f, 2, buf, sizeof(buf)); + fmtprint(&f, "%d ", lineno); + va_start(va, fmt); + fmtvprint(&f, fmt, va); + fmtrune(&f, '\n'); + va_end(va); + fmtfdflush(&f); + errors++; +} + +void +yyerror(char *msg) +{ + error("%s", msg); +} + +static int +getch(void) +{ + if(*strp == 0) return -1; + return *strp++; +} + +static void +ungetch(void) +{ + assert(strp > str); + if(*strp != 0) + strp--; +} + +int +yylex(void) +{ + int ch; + static char buf[512]; + char *p; + Keyword *kw; + u64int v; + +again: + while(ch = getch(), ch >= 0 && isspace(ch)){ + if(ch == '\n') + lineno++; + } + if(ch < 0) + return -1; + if(ch == '/'){ + ch = getch(); + if(ch == '/'){ + while(ch = getch(), ch >= 0 && ch != '\n') + ; + if(ch == '\n') + lineno++; + goto again; + } + if(ch == '*'){ + s1: + ch = getch(); + if(ch < 0) return -1; + if(ch == '\n') lineno++; + if(ch != '*') goto s1; + s2: + ch = getch(); + if(ch < 0) return -1; + if(ch == '\n') lineno++; + if(ch == '*') goto s2; + if(ch != '/') goto s1; + goto again; + } + ungetch(); + return '/'; + } + if(isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':'){ + p = buf; + *p++ = ch; + while(ch = getch(), isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':') + if(p < buf + sizeof(buf) - 1) + *p++ = ch; + *p = 0; + ungetch(); + v = strtoull(buf, &p, 0); + if(p != buf && *p == 0){ + yylval.num = v; + return TNUM; + } + if(strcmp(buf, ":") == 0) + return ':'; + if((uchar)buf[0] < 0x80 && kwchar[buf[0]] != nil) + for(kw = kwchar[buf[0]]; kw < kwtab + nelem(kwtab) && kw->name[0] == buf[0]; kw++) + if(strcmp(kw->name, buf) == 0) + return kw->tok; + yylval.sym = getsym(buf); + return TSYM; + } + if(ch == '"'){ + p = buf; + while(ch = getch(), ch >= 0 && ch != '"'){ + if(ch == '\n') + error("unterminated string"); + if(ch == '\\') + switch(ch = getch()){ + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case 'b': ch = '\b'; break; + case 'a': ch = '\a'; break; + case '"': case '\\': break; + default: error("unknown escape code \\%c", ch); + } + if(p < buf + sizeof(buf) - 1) + *p++ = ch; + } + if(ch < 0) error("unterminated string"); + *p = 0; + yylval.str = strdup(buf); + return TSTR; + } + if(opchar[ch] != nil){ + buf[0] = ch; + buf[1] = getch(); + for(kw = opchar[buf[0]]; kw < optab + nelem(optab) && kw->name[0] == buf[0]; kw++) + if(buf[1] == kw->name[1]){ + buf[2] = getch(); + buf[3] = 0; + if(kw + 1 < optab + nelem(optab) && strcmp(kw[1].name, buf) == 0) + return kw[1].tok; + ungetch(); + return kw->tok; + } + ungetch(); + } + return ch; +} + +int +nodetfmt(Fmt *f) +{ + int t; + static char *nodestr[] = { + [OINVAL] "OINVAL", + [OBIN] "OBIN", + [OLNOT] "OLNOT", + [OSYM] "OSYM", + [ONUM] "ONUM", + [OSTR] "OSTR", + [OTERN] "OTERN", + [ORECORD] "ORECORD", + [OCAST] "OCAST", + }; + + t = va_arg(f->args, int); + if(t >= nelem(nodestr) || nodestr[t] == nil) + return fmtprint(f, "??? (%d)", t); + else + return fmtprint(f, "%s", nodestr[t]); +} + +Node * +node(int type, ...) +{ + va_list va; + Node *n; + + n = emalloc(sizeof(Node)); + n->type = type; + va_start(va, type); + switch(type){ + case OBIN: + n->op = va_arg(va, int); + n->n1 = va_arg(va, Node *); + n->n2 = va_arg(va, Node *); + break; + case OLNOT: + n->n1 = va_arg(va, Node *); + break; + case OSYM: + n->sym = va_arg(va, Symbol *); + break; + case ONUM: + n->num = va_arg(va, s64int); + break; + case OTERN: + n->n1 = va_arg(va, Node *); + n->n2 = va_arg(va, Node *); + n->n3 = va_arg(va, Node *); + break; + case ORECORD: + n->n1 = va_arg(va, Node *); + break; + case OCAST: + n->typ = va_arg(va, Type *); + n->n1 = va_arg(va, Node *); + break; + case OSTR: + n->str = va_arg(va, char *); + break; + default: + sysfatal("node: unknown type %α", type); + } + va_end(va); + return n; +} + +SymTab globals; + +static u64int +hash(char *s) +{ + u64int h; + + h = 0xcbf29ce484222325ULL; + for(; *s != 0; s++){ + h ^= *s; + h *= 0x100000001b3ULL; + } + return h; +} + +Symbol * +getsym(char *name) +{ + u64int h; + Symbol **sp, *s; + + h = hash(name); + for(sp = &globals.sym[h % SYMHASH]; s = *sp, s != nil; sp = &s->next) + if(strcmp(s->name, name) == 0) + return s; + *sp = s = emalloc(sizeof(Symbol)); + s->name = strdup(name); + return s; +} + +int +typetfmt(Fmt *f) +{ + int t; + static char *tstr[] = { + [TYPINVAL] "TYPINVAL", + [TYPINT] "TYPINT", + [TYPPTR] "TYPPTR", + [TYPSTRING] "TYPSTRING", + }; + + t = va_arg(f->args, int); + if(t >= nelem(tstr) || tstr[t] == nil) + return fmtprint(f, "??? (%d)", t); + else + return fmtprint(f, "%s", tstr[t]); +} + +int +typefmt(Fmt *f) +{ + Type *t; + + t = va_arg(f->args, Type *); + switch(t->type){ + case TYPINT: return fmtprint(f, "%c%d", t->sign ? 's' : 'u', t->size * 8); + case TYPSTRING: return fmtprint(f, "string"); + case TYPPTR: return fmtprint(f, "%τ*", t->ref); + default: return fmtprint(f, "%t", t->type); + } +} + +static Type typu8 = {.type TYPINT, .size 1, .sign 0}; +static Type typs8 = {.type TYPINT, .size 1, .sign 1}; +static Type typu16 = {.type TYPINT, .size 2, .sign 0}; +static Type typs16 = {.type TYPINT, .size 2, .sign 1}; +static Type typu32 = {.type TYPINT, .size 4, .sign 0}; +static Type typs32 = {.type TYPINT, .size 4, .sign 1}; +static Type typu64 = {.type TYPINT, .size 8, .sign 0}; +static Type typs64 = {.type TYPINT, .size 8, .sign 1}; +static Type typstr = {.type TYPSTRING, .size DTSTRMAX }; +static Type *typereg; + +static Type * +mkptr(Type *t) +{ + Type *s; + + for(s = typereg; s != nil; s = s->typenext) + if(s->type == TYPPTR && s->ref == t) + return s; + s = emalloc(sizeof(Type)); + s->type = TYPPTR; + s->ref = t; + return s; +} + +Type * +type(int typ, ...) +{ + int size, sign; + va_list va; + + va_start(va, typ); + switch(typ){ + case TYPINT: + size = va_arg(va, int); + sign = va_arg(va, int); + switch(size << 4 | sign){ + case 0x10: return &typu8; + case 0x11: return &typs8; + case 0x20: return &typu16; + case 0x21: return &typs16; + case 0x40: return &typu32; + case 0x41: return &typs32; + case 0x80: return &typu64; + case 0x81: return &typs64; + default: sysfatal("type: invalid (size,sign) = (%d,%d)\n", size, sign); return nil; + } + case TYPSTRING: return &typstr; + case TYPPTR: return mkptr(va_arg(va, Type *)); + default: sysfatal("type: unknown %t", typ); return nil; + } +} diff --git a/sys/src/cmd/dtracy/lint.rc b/sys/src/cmd/dtracy/lint.rc new file mode 100755 index 000000000..cafbdd835 --- /dev/null +++ b/sys/src/cmd/dtracy/lint.rc @@ -0,0 +1,20 @@ +#!/bin/rc +# check for full switch statements + +nl=' +' +nodetypes=`''{sed -n '/OINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OINVAL$'} +switches=`$nl{grep -n '/\*nodetype\*/' *.[ch]} +for(l in $switches){ + f=`:{echo $l} + a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)} + comm -23 <{echo $nodetypes} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p' +} + +oper=`''{sed -n '/OPINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OPINVAL$'} +switches=`$nl{grep -n '/\*oper\*/' *.[ch]} +for(l in $switches){ + f=`:{echo $l} + a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)} + comm -23 <{echo $oper} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p' +} diff --git a/sys/src/cmd/dtracy/mkfile b/sys/src/cmd/dtracy/mkfile new file mode 100644 index 000000000..7d1b4b45c --- /dev/null +++ b/sys/src/cmd/dtracy/mkfile @@ -0,0 +1,22 @@ + +#include +#include +#include +#include "dat.h" +#include "fns.h" +%} + +%union{ + Node *n; + Symbol *sym; + DTExpr *e; + s64int num; + char *str; + Type *t; +} + +%type expr +%type type + +%token TSYM +%token TNUM +%token TSTR +%token TPRINT TPRINTF +%token TIF +%token TU8 TU16 TU32 TU64 +%token TS8 TS16 TS32 TS64 +%token TSTRING + +%right '?' +%left TOR +%left TAND +%left '|' +%left '^' +%left '&' +%left TEQ TNE +%left '<' '>' TLE TGE +%left TLSL TLSR +%left '+' '-' +%left '*' '/' '%' +%left unary +%right castprec + +%% + +program: | program clause + +clause: { clausebegin(); } probes optpredicate optaction { clauseend(); } + +optpredicate: | TIF expr { addpred(codegen(exprcheck($2, 1))); } + +optaction: + { + addstat(STATPRINT); + addarg(node(OSYM, getsym("probe"))); + } + | action +action: '{' stats '}' +stats: | stats0 | stats0 ';' +stats0: stat | stats0 ';' stat + +stat: expr { addstat(STATEXPR, exprcheck($1, 0)); } +| TPRINT { addstat(STATPRINT); } pelist +| TPRINTF { addstat(STATPRINTF); } pelist + + +pelist: + '(' ')' + | '(' arg ',' ')' + | '(' elist2 optcomma ')' + | arg optcomma + | elist2 optcomma +elist2: arg ',' arg | elist2 ',' arg +arg: expr { addarg(exprcheck($1, 0)); } +optcomma: | ',' + +expr: + TSYM { $$ = node(OSYM, $1); } + | TNUM { $$ = node(ONUM, $1); } + | TSTR { $$ = node(OSTR, $1); } + | expr '+' expr { $$ = node(OBIN, OPADD, $1, $3); } + | expr '-' expr { $$ = node(OBIN, OPSUB, $1, $3); } + | expr '*' expr { $$ = node(OBIN, OPMUL, $1, $3); } + | expr '/' expr { $$ = node(OBIN, OPDIV, $1, $3); } + | expr '%' expr { $$ = node(OBIN, OPMOD, $1, $3); } + | expr '&' expr { $$ = node(OBIN, OPAND, $1, $3); } + | expr '|' expr { $$ = node(OBIN, OPOR, $1, $3); } + | expr '^' expr { $$ = node(OBIN, OPXOR, $1, $3); } + | expr TLSL expr { $$ = node(OBIN, OPLSH, $1, $3); } + | expr TLSR expr { $$ = node(OBIN, OPRSH, $1, $3); } + | expr TEQ expr { $$ = node(OBIN, OPEQ, $1, $3); } + | expr TNE expr { $$ = node(OBIN, OPNE, $1, $3); } + | expr '<' expr { $$ = node(OBIN, OPLT, $1, $3); } + | expr TLE expr { $$ = node(OBIN, OPLE, $1, $3); } + | expr '>' expr { $$ = node(OBIN, OPLT, $3, $1); } + | expr TGE expr { $$ = node(OBIN, OPLE, $3, $1); } + | expr TAND expr { $$ = node(OBIN, OPLAND, $1, $3); } + | expr TOR expr { $$ = node(OBIN, OPLOR, $1, $3); } + | '-' expr %prec unary { $$ = node(OBIN, OPSUB, node(ONUM, 0LL), $2); } + | '~' expr %prec unary { $$ = node(OBIN, OPXNOR, node(ONUM, 0LL), $2); } + | '!' expr %prec unary { $$ = node(OLNOT, $2); } + | '(' expr ')' { $$ = $2; } + | expr '?' expr ':' expr %prec '?' { $$ = node(OTERN, $1, $3, $5); } + | '(' type ')' expr %prec castprec { $$ = node(OCAST, $2, $4); } + +type: + TU8 { $$ = type(TYPINT, 1, 0); } + | TS8 { $$ = type(TYPINT, 1, 1); } + | TU16 { $$ = type(TYPINT, 2, 0); } + | TS16 { $$ = type(TYPINT, 2, 1); } + | TU32 { $$ = type(TYPINT, 4, 0); } + | TS32 { $$ = type(TYPINT, 4, 1); } + | TU64 { $$ = type(TYPINT, 8, 0); } + | TS64 { $$ = type(TYPINT, 8, 1); } + | TSTRING { $$ = type(TYPSTRING); } + +probes: + TSYM { addprobe($1->name); } + | probes ',' TSYM { addprobe($3->name); } + + +%% diff --git a/sys/src/cmd/dtracy/type.c b/sys/src/cmd/dtracy/type.c new file mode 100644 index 000000000..fdf7872b6 --- /dev/null +++ b/sys/src/cmd/dtracy/type.c @@ -0,0 +1,529 @@ +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Node * +icast(int sign, int size, Node *n) +{ + Type *t; + + t = type(TYPINT, sign, size); + return node(OCAST, t, n); +} + +/* + the type checker checks types. + the result is an expression that is correct if evaluated with 64-bit operands all the way. + to maintain c-like semantics, this means adding casts all over the place, which will get optimised later. + + note we use kencc, NOT ansi c, semantics for unsigned. +*/ + +Node * +typecheck(Node *n) +{ + int s1, s2, sign; + + switch(/*nodetype*/n->type){ + case OSYM: + switch(n->sym->type){ + case SYMNONE: error("undeclared '%s'", n->sym->name); break; + case SYMVAR: n->typ = n->sym->typ; break; + default: sysfatal("typecheck: unknown symbol type %d", n->sym->type); + } + break; + case ONUM: + if((vlong)n->num >= -0x80000000LL && (vlong)n->num <= 0x7fffffffLL) + n->typ = type(TYPINT, 4, 1); + else + n->typ = type(TYPINT, 8, 1); + break; + case OSTR: + n->typ = type(TYPSTRING); + break; + case OBIN: + n->n1 = typecheck(n->n1); + n->n2 = typecheck(n->n2); + if(n->n1->typ == nil || n->n2->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + if(n->n2->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n2->typ); + break; + } + s1 = n->n1->typ->size; + s2 = n->n2->typ->size; + sign = n->n1->typ->sign && n->n2->typ->sign; + switch(n->op){ + case OPADD: + case OPSUB: + case OPMUL: + case OPDIV: + case OPMOD: + case OPAND: + case OPOR: + case OPXOR: + case OPXNOR: + n->typ = type(TYPINT, 8, sign); + if(s1 > 4 || s2 > 4){ + n->n1 = icast(8, sign, n->n1); + n->n2 = icast(8, sign, n->n2); + return n; + }else{ + n->n1 = icast(4, sign, n->n1); + n->n2 = icast(4, sign, n->n2); + return icast(4, sign, n); + } + case OPEQ: + case OPNE: + case OPLT: + case OPLE: + n->typ = type(TYPINT, 4, sign); + if(s1 > 4 || s2 > 4){ + n->n1 = icast(8, sign, n->n1); + n->n2 = icast(8, sign, n->n2); + return n; + }else{ + n->n1 = icast(4, sign, n->n1); + n->n2 = icast(4, sign, n->n2); + return n; + } + case OPLAND: + case OPLOR: + n->typ = type(TYPINT, 4, sign); + return n; + case OPLSH: + case OPRSH: + if(n->n1->typ->size <= 4) + n->n1 = icast(4, n->n1->typ->sign, n->n1); + n->typ = n->n1->typ; + return icast(n->typ->size, n->typ->sign, n); + default: + sysfatal("typecheck: unknown op %d", n->op); + } + break; + case OCAST: + n->n1 = typecheck(n->n1); + if(n->n1->typ == nil) + break; + if(n->typ->type == TYPINT && n->n1->typ->type == TYPINT){ + }else if(n->typ == n->n1->typ){ + }else if(n->typ->type == TYPSTRING && n->n1->typ->type == TYPINT){ + }else + error("can't cast from %τ to %τ", n->n1->typ, n->typ); + break; + case OLNOT: + n->n1 = typecheck(n->n1); + if(n->n1->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + n->typ = type(TYPINT, 4, 1); + break; + case OTERN: + n->n1 = typecheck(n->n1); + n->n2 = typecheck(n->n2); + n->n3 = typecheck(n->n3); + if(n->n1->typ == nil || n->n2->typ == nil || n->n3->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + if(n->n2->typ->type == TYPINT || n->n3->typ->type == TYPINT){ + sign = n->n2->typ->sign && n->n3->typ->sign; + s1 = n->n2->typ->size; + s2 = n->n3->typ->size; + if(s1 > 4 || s2 > 4){ + n->n2 = icast(8, sign, n->n2); + n->n3 = icast(8, sign, n->n3); + n->typ = type(TYPINT, 8, sign); + return n; + }else{ + n->n2 = icast(4, sign, n->n2); + n->n3 = icast(4, sign, n->n3); + n->typ = type(TYPINT, 4, sign); + return n; + } + }else if(n->n2->typ == n->n3->typ){ + n->typ = n->n2->typ; + }else + error("don't know how to do ternary with %τ and %τ", n->n2->typ, n->n3->typ); + break; + case ORECORD: + default: sysfatal("typecheck: unknown node type %α", n->type); + } + return n; +} + +vlong +evalop(int op, int sign, vlong v1, vlong v2) +{ + switch(/*oper*/op){ + case OPADD: return v1 + v2; break; + case OPSUB: return v1 - v2; break; + case OPMUL: return v1 * v2; break; + case OPDIV: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 / v2 : (uvlong)v1 / (uvlong)v2; break; + case OPMOD: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 % v2 : (uvlong)v1 % (uvlong)v2; break; + case OPAND: return v1 & v2; break; + case OPOR: return v1 | v2; break; + case OPXOR: return v1 ^ v2; break; + case OPXNOR: return ~(v1 ^ v2); break; + case OPLSH: + if((u64int)v2 >= 64) + return 0; + else + return v1 << v2; + break; + case OPRSH: + if(sign){ + if((u64int)v2 >= 64) + return v1 >> 63; + else + return v1 >> v2; + }else{ + if((u64int)v2 >= 64) + return 0; + else + return (u64int)v1 >> v2; + } + break; + case OPEQ: return v1 == v2; break; + case OPNE: return v1 != v2; break; + case OPLT: return v1 < v2; break; + case OPLE: return v1 <= v2; break; + case OPLAND: return v1 && v2; break; + case OPLOR: return v1 || v2; break; + default: + sysfatal("cfold: unknown op %.2x", op); + return 0; + } + +} + +Node * +addtype(Type *t, Node *n) +{ + n->typ = t; + return n; +} + +/* fold constants */ + +static Node * +cfold(Node *n) +{ + switch(/*nodetype*/n->type){ + case ONUM: + case OSYM: + case OSTR: + return n; + case OBIN: + n->n1 = cfold(n->n1); + n->n2 = cfold(n->n2); + if(n->n1->type != ONUM || n->n2->type != ONUM) + return n; + return addtype(n->typ, node(ONUM, evalop(n->op, n->typ->sign, n->n1->num, n->n2->num))); + case OLNOT: + n->n1 = cfold(n->n1); + if(n->n1->type == ONUM) + return addtype(n->typ, node(ONUM, !n->n1->num)); + return n; + case OTERN: + n->n1 = cfold(n->n1); + n->n2 = cfold(n->n2); + n->n3 = cfold(n->n3); + if(n->n1->type == ONUM) + return n->n1->num ? n->n2 : n->n3; + return n; + case OCAST: + n->n1 = cfold(n->n1); + if(n->n1->type != ONUM || n->typ->type != TYPINT) + return n; + switch(n->typ->size << 4 | n->typ->sign){ + case 0x10: return addtype(n->typ, node(ONUM, (vlong)(u8int)n->n1->num)); + case 0x11: return addtype(n->typ, node(ONUM, (vlong)(s8int)n->n1->num)); + case 0x20: return addtype(n->typ, node(ONUM, (vlong)(u16int)n->n1->num)); + case 0x21: return addtype(n->typ, node(ONUM, (vlong)(s16int)n->n1->num)); + case 0x40: return addtype(n->typ, node(ONUM, (vlong)(u32int)n->n1->num)); + case 0x41: return addtype(n->typ, node(ONUM, (vlong)(s32int)n->n1->num)); + case 0x80: return addtype(n->typ, node(ONUM, n->n1->num)); + case 0x81: return addtype(n->typ, node(ONUM, n->n1->num)); + } + return n; + case ORECORD: + default: + fprint(2, "cfold: unknown type %α\n", n->type); + return n; + } +} + +/* calculate the minimum record size for each node of the expression */ +static Node * +calcrecsize(Node *n) +{ + switch(/*nodetype*/n->type){ + case ONUM: + case OSTR: + n->recsize = 0; + break; + case OSYM: + switch(n->sym->type){ + case SYMVAR: + switch(n->sym->idx){ + case DTV_TIME: + case DTV_PROBE: + n->recsize = 0; + break; + default: + n->recsize = n->typ->size; + break; + } + break; + default: sysfatal("calcrecsize: unknown symbol type %d", n->sym->type); return nil; + } + break; + case OBIN: + n->n1 = calcrecsize(n->n1); + n->n2 = calcrecsize(n->n2); + n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize); + break; + case OLNOT: + n->n1 = calcrecsize(n->n1); + n->recsize = min(n->typ->size, n->n1->recsize); + break; + case OCAST: + n->n1 = calcrecsize(n->n1); + if(n->typ->type == TYPSTRING) + n->recsize = n->typ->size; + else + n->recsize = min(n->typ->size, n->n1->recsize); + break; + case OTERN: + n->n1 = calcrecsize(n->n1); + n->n2 = calcrecsize(n->n2); + n->n3 = calcrecsize(n->n3); + n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize + n->n3->recsize); + break; + case ORECORD: + default: sysfatal("calcrecsize: unknown type %α", n->type); return nil; + } + return n; +} + +/* insert ORECORD nodes to mark the subexpression that we will pass to the kernel */ +static Node * +insrecord(Node *n) +{ + if(n->recsize == 0) + return n; + if(n->typ->size == n->recsize) + return addtype(n->typ, node(ORECORD, n)); + switch(/*nodetype*/n->type){ + case ONUM: + case OSTR: + case OSYM: + break; + case OBIN: + n->n1 = insrecord(n->n1); + n->n2 = insrecord(n->n2); + break; + case OLNOT: + case OCAST: + n->n1 = insrecord(n->n1); + break; + case OTERN: + n->n1 = insrecord(n->n1); + n->n2 = insrecord(n->n2); + n->n3 = insrecord(n->n3); + break; + case ORECORD: + default: sysfatal("insrecord: unknown type %α", n->type); return nil; + } + return n; +} + +/* + delete useless casts. + going down we determine the number of bits (m) needed to be correct at each stage. + going back up we determine the number of bits (n->databits) which can be either 0 or 1. + all other bits are either zero (n->upper == UPZX) or sign-extended (n->upper == UPSX). + note that by number of bits we always mean a consecutive block starting from the LSB. + + we can delete a cast if it either affects only bits not needed (according to m) or + if it's a no-op (according to databits, upper). +*/ +static Node * +elidecasts(Node *n, int m) +{ + switch(/*nodetype*/n->type){ + case OSTR: + return n; + case ONUM: + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + break; + case OSYM: + /* TODO: make less pessimistic */ + n->databits = 64; + break; + case OBIN: + switch(/*oper*/n->op){ + case OPADD: + case OPSUB: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + n->databits = min(64, max(n->n1->databits, n->n2->databits) + 1); + n->upper = n->n1->upper | n->n2->upper; + break; + case OPMUL: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + n->databits = min(64, n->n1->databits + n->n2->databits); + n->upper = n->n1->upper | n->n2->upper; + break; + case OPAND: + case OPOR: + case OPXOR: + case OPXNOR: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + if(n->op == OPAND && (n->n1->upper == UPZX || n->n2->upper == UPZX)){ + n->upper = UPZX; + if(n->n1->upper == UPZX && n->n2->upper == UPZX) + n->databits = min(n->n1->databits, n->n2->databits); + else if(n->n1->upper == UPZX) + n->databits = n->n1->databits; + else + n->databits = n->n2->databits; + }else{ + n->databits = max(n->n1->databits, n->n2->databits); + n->upper = n->n1->upper | n->n2->upper; + } + break; + case OPLSH: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, 64); + if(n->n2->type == ONUM && n->n2->num >= 0 && n->n1->databits + (uvlong)n->n2->num <= 64) + n->databits = n->n1->databits + n->n2->num; + else + n->databits = 64; + n->upper = n->n1->upper; + break; + case OPRSH: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + if(n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = 64; + n->upper = UPZX; + } + break; + case OPEQ: + case OPNE: + case OPLT: + case OPLE: + case OPLAND: + case OPLOR: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + n->databits = 1; + n->upper = UPZX; + break; + case OPDIV: + case OPMOD: + default: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + n->databits = 64; + n->upper = UPZX; + break; + } + break; + case OLNOT: + n->n1 = elidecasts(n->n1, 64); + n->databits = 1; + n->upper = UPZX; + break; + case OCAST: + switch(n->typ->type){ + case TYPINT: + n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m)); + if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + } + if(n->typ->size * 8 >= m) return n->n1; + if(n->typ->size * 8 >= n->n1->databits && n->typ->sign == n->n1->upper) return n->n1; + if(n->typ->size * 8 > n->n1->databits && n->typ->sign && !n->n1->upper) return n->n1; + break; + case TYPSTRING: + n->n1 = elidecasts(n->n1, 64); + break; + default: + sysfatal("elidecasts: don't know how to cast %τ to %τ", n->n1->typ, n->typ); + } + break; + case ORECORD: + n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m)); + if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + } + break; + case OTERN: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, m); + n->n3 = elidecasts(n->n3, m); + if(n->n2->upper == n->n3->upper){ + n->databits = max(n->n2->databits, n->n3->databits); + n->upper = n->n2->upper; + }else{ + if(n->n3->upper == UPSX) + n->databits = max(min(64, n->n2->databits + 1), n->n3->databits); + else + n->databits = max(min(64, n->n3->databits + 1), n->n2->databits); + n->upper = UPSX; + } + break; + default: sysfatal("elidecasts: unknown type %α", n->type); + } +// print("need %d got %d%c %ε\n", n->needbits, n->databits, "ZS"[n->upper], n); + return n; +} + + +Node * +exprcheck(Node *n, int pred) +{ + if(dflag) print("start %ε\n", n); + n = typecheck(n); + if(errors) return n; + if(dflag) print("typecheck %ε\n", n); + n = cfold(n); + if(dflag) print("cfold %ε\n", n); + if(!pred){ + n = insrecord(calcrecsize(n)); + if(dflag) print("insrecord %ε\n", n); + } + n = elidecasts(n, 64); + if(dflag) print("elidecasts %ε\n", n); + return n; +} diff --git a/sys/src/libdtracy/chan.c b/sys/src/libdtracy/chan.c new file mode 100644 index 000000000..16414834f --- /dev/null +++ b/sys/src/libdtracy/chan.c @@ -0,0 +1,222 @@ +#include +#include +#include + +int dtnmach; + +void +dtinit(int nmach) +{ + DTProvider **p; + + dtnmach = nmach; + + /* sanity */ + for(p = dtproviders; *p != nil; p++){ + assert((*p)->name != nil); + assert((*p)->provide != nil); + assert((*p)->enable != nil); + assert((*p)->disable != nil); + } +} + +void +dtsync(void) +{ + int i; + + for(i = 0; i < dtnmach; i++){ + dtmachlock(i); + dtmachunlock(i); + } +} + +DTChan * +dtcnew(void) +{ + DTChan *c; + int i; + + c = dtmalloc(sizeof(DTChan)); + c->rdbufs = dtmalloc(sizeof(DTBuf *) * dtnmach); + c->wrbufs = dtmalloc(sizeof(DTBuf *) * dtnmach); + for(i = 0; i < dtnmach; i++){ + c->rdbufs[i] = dtmalloc(sizeof(DTBuf)); + c->wrbufs[i] = dtmalloc(sizeof(DTBuf)); + } + return c; +} + +void +dtcfree(DTChan *ch) +{ + int i; + + if(ch == nil) return; + + dtcrun(ch, DTCSTOP); + dtcreset(ch); + dtsync(); + for(i = 0; i < dtnmach; i++){ + free(ch->rdbufs[i]); + free(ch->wrbufs[i]); + } + free(ch->rdbufs); + free(ch->wrbufs); + free(ch); +} + +int +dtcaddgr(DTChan *c, DTName name, DTActGr *gr) +{ + DTProbe **l, *p; + DTEnab *ep; + int i, nl, n; + + if(dtgverify(gr) < 0) + return -1; + gr->chan = c; + + nl = dtpmatch(name, &l); + n = 0; + for(i = 0; i < nl; i++){ + p = l[i]; + if(p->nenable == 0) + if(p->prov->enable(p) < 0) + continue; + ep = dtmalloc(sizeof(DTEnab)); + ep->epid = c->epidalloc++; + ep->gr = gr; + ep->prob = p; + ep->probnext = &p->enablist; + ep->probprev = p->enablist.probprev; + ep->probnext->probprev = ep; + ep->channext = c->enab; + c->enab = ep; + gr->ref++; + n++; + p->nenable++; + /* careful, has to be atomic for dtptrigger */ + dtcoherence(); + ep->probprev->probnext = ep; + } + dtfree(l); + return n; +} + +static int +dtnamesplit(char *s, DTName *rp) +{ + char *p; + + p = strchr(s, ':'); + if(p == nil) return -1; + rp->provider = dtmalloc(p - s + 1); + memcpy(rp->provider, s, p - s); + s = p + 1; + p = strchr(s, ':'); + if(p == nil){ + free(rp->provider); + rp->provider = nil; + return -1; + } + rp->function = dtmalloc(p - s + 1); + memcpy(rp->function, s, p - s); + s = p + 1; + if(strchr(s, ':') != nil){ + free(rp->provider); + rp->provider = nil; + free(rp->function); + rp->function = nil; + return -1; + } + rp->name = dtstrdup(s); + return 0; +} + +int +dtcaddcl(DTChan *c, DTClause *cl) +{ + DTName n; + int i, rc; + + rc = 0; + for(i = 0; i < cl->nprob; i++){ + if(dtnamesplit(cl->probs[i], &n) < 0){ + werrstr("invalid probe name '%s'", cl->probs[i]); + return -1; + } + rc += dtcaddgr(c, n, cl->gr); + dtfree(n.provider); + dtfree(n.function); + dtfree(n.name); + } + return rc; +} + +static void +dtcbufswap(DTChan *c, int n) +{ + DTBuf *z; + + dtmachlock(n); + z = c->rdbufs[n]; + c->rdbufs[n] = c->wrbufs[n]; + c->wrbufs[n] = z; + dtmachunlock(n); +} + +int +dtcread(DTChan *c, void *buf, int n) +{ + int i, swapped; + + if(c->state == DTCFAULT){ + werrstr("%s", c->errstr); + return -1; + } + for(i = 0; i < dtnmach; i++){ + if(swapped = c->rdbufs[i]->wr == 0) + dtcbufswap(c, i); + if(c->rdbufs[i]->wr != 0){ + if(c->rdbufs[i]->wr > n){ + werrstr("short read"); + return -1; + } + n = c->rdbufs[i]->wr; + memmove(buf, c->rdbufs[i]->data, n); + c->rdbufs[i]->wr = 0; + if(!swapped) + dtcbufswap(c, i); + return n; + } + } + return 0; +} + +void +dtcreset(DTChan *c) +{ + DTEnab *ep, *eq; + + for(ep = c->enab; ep != nil; ep = ep->channext){ + /* careful! has to look atomic for etptrigger */ + ep->probprev->probnext = ep->probnext; + ep->probnext->probprev = ep->probprev; + } + dtsync(); + for(ep = c->enab; ep != nil; eq = ep->channext, free(ep), ep = eq){ + if(--ep->gr->ref == 0) + dtgfree(ep->gr); + if(--ep->prob->nenable == 0) + ep->prob->prov->disable(ep->prob); + } + c->enab = nil; +} + +void +dtcrun(DTChan *c, int newstate) +{ + assert(newstate == DTCSTOP || newstate == DTCGO); + c->state = newstate; +} diff --git a/sys/src/libdtracy/dtefmt.c b/sys/src/libdtracy/dtefmt.c new file mode 100644 index 000000000..1f5efe4e4 --- /dev/null +++ b/sys/src/libdtracy/dtefmt.c @@ -0,0 +1,114 @@ +#include +#include +#include + +char *dtvarnames[DTNVARS] = { + [DTV_ARG0] "arg0", + [DTV_ARG1] "arg1", + [DTV_ARG2] "arg2", + [DTV_ARG3] "arg3", + [DTV_ARG4] "arg4", + [DTV_ARG5] "arg5", + [DTV_ARG6] "arg6", + [DTV_ARG7] "arg7", + [DTV_ARG8] "arg8", + [DTV_ARG9] "arg9", + [DTV_PID] "pid", + [DTV_TIME] "time", + [DTV_PROBE] "probe", + [DTV_MACHNO] "machno", +}; + +int +dtefmt(Fmt *f) +{ + u32int ins; + u8int op, a, b, c; + u64int x; + static char *opcodes[] = { + [DTE_ADD] "ADD", + [DTE_SUB] "SUB", + [DTE_MUL] "MUL", + [DTE_UDIV] "UDIV", + [DTE_UMOD] "UMOD", + [DTE_SDIV] "SDIV", + [DTE_SMOD] "SMOD", + [DTE_AND] "AND", + [DTE_OR] "OR", + [DTE_XOR] "XOR", + [DTE_XNOR] "XNOR", + [DTE_LSL] "LSL", + [DTE_LSR] "LSR", + [DTE_ASR] "ASR", + [DTE_SEQ] "SEQ", + [DTE_SNE] "SNE", + [DTE_SLT] "SLT", + [DTE_SLE] "SLE", + [DTE_LDI] "LDI", + [DTE_XORI] "XORI", + [DTE_BEQ] "BEQ", + [DTE_BNE] "BNE", + [DTE_BLT] "BLT", + [DTE_BLE] "BLE", + [DTE_LDV] "LDV", + [DTE_RET] "RET", + [DTE_ZXT] "ZXT", + [DTE_SXT] "SXT", + }; + + ins = va_arg(f->args, u32int); + op = ins >> 24; + a = ins >> 16; + b = ins >> 8; + c = ins; + switch(op){ + case DTE_ADD: + case DTE_SUB: + case DTE_MUL: + case DTE_UDIV: + case DTE_UMOD: + case DTE_SDIV: + case DTE_SMOD: + case DTE_AND: + case DTE_OR: + case DTE_XOR: + case DTE_XNOR: + case DTE_LSL: + case DTE_LSR: + case DTE_ASR: + case DTE_SEQ: + case DTE_SNE: + case DTE_SLT: + case DTE_SLE: + fmtprint(f, "%s R%d, R%d, R%d", opcodes[op], a, b, c); + break; + case DTE_LDI: + case DTE_XORI: + x = (s64int)ins << 40 >> 54 << (ins >> 8 & 63); + fmtprint(f, "%s $%#llx, R%d", opcodes[op], x, c); + break; + case DTE_BEQ: + case DTE_BNE: + case DTE_BLT: + case DTE_BLE: + fmtprint(f, "%s R%d, R%d, +%d", opcodes[op], a, b, c); + break; + case DTE_LDV: + if(a >= DTNVARS || dtvarnames[a] == nil) + fmtprint(f, "%s V%d, R%d", opcodes[op], a, b); + else + fmtprint(f, "%s %s, R%d", opcodes[op], dtvarnames[a], b); + break; + case DTE_ZXT: + case DTE_SXT: + fmtprint(f, "%s R%d, $%d, R%d", opcodes[op], a, b, c); + break; + case DTE_RET: + fmtprint(f, "RET R%d", a); + break; + default: + fmtprint(f, "??? (%#.8ux)", op); + break; + } + return 0; +} diff --git a/sys/src/libdtracy/mkfile b/sys/src/libdtracy/mkfile new file mode 100644 index 000000000..29f7d91bd --- /dev/null +++ b/sys/src/libdtracy/mkfile @@ -0,0 +1,20 @@ + +#include +#include + +static void +dtepack(Fmt *f, DTExpr *e) +{ + int i; + + fmtprint(f, "e%d\n", e->n); + for(i = 0; i < e->n; i++) + fmtprint(f, "%#.8ux\n", e->b[i]); +} + +void +dtgpack(Fmt *f, DTActGr *g) +{ + int i; + + fmtprint(f, "g%ud\n", g->id); + if(g->pred != nil){ + fmtprint(f, "p"); + dtepack(f, g->pred); + } + fmtprint(f, "a%d\n", g->nact); + for(i = 0; i < g->nact; i++){ + fmtprint(f, "t%d\n", g->acts[i].type); + fmtprint(f, "s%d\n", g->acts[i].size); + dtepack(f, g->acts[i].p); + } + fmtprint(f, "G"); +} + +void +dtclpack(Fmt *f, DTClause *c) +{ + int i; + + fmtprint(f, "c%d\n", c->nprob); + for(i = 0; i < c->nprob; i++) + fmtprint(f, "%s\n", c->probs[i]); + dtgpack(f, c->gr); +} + +static char * +u32unpack(char *s, u32int *np) +{ + char *r; + + *np = strtoul(s, &r, 0); + if(r == s || *r != '\n') return nil; + return r + 1; +} + +static char * +dteunpack(char *s, DTExpr **rp) +{ + int i; + u32int n; + DTExpr *e; + + *rp = nil; + if(*s++ != 'e') return nil; + s = u32unpack(s, &n); + if(s == nil) return nil; + e = dtmalloc(sizeof(DTExpr) + n * sizeof(u32int)); + e->n = n; + e->b = (void*)(e + 1); + for(i = 0; i < n; i++){ + s = u32unpack(s, &e->b[i]); + if(s == nil){ + dtfree(e); + return nil; + } + } + *rp = e; + return s; +} + +void +dtgfree(DTActGr *g) +{ + int i; + + if(g == nil) return; + for(i = 0; i < g->nact; i++) + dtfree(g->acts[i].p); + dtfree(g->acts); + dtfree(g->pred); + dtfree(g); + +} + +char * +dtgunpack(char *s, DTActGr **rp) +{ + DTActGr *g; + u32int n; + int i; + + *rp = nil; + g = dtmalloc(sizeof(DTActGr)); + g->reclen = 12; + g->ref = 1; + if(*s++ != 'g') goto fail; + s = u32unpack(s, &g->id); + if(s == nil) goto fail; + for(;;) + switch(*s++){ + case 'p': + s = dteunpack(s, &g->pred); + if(s == nil) goto fail; + break; + case 'a': + s = u32unpack(s, &n); + if(s == nil) goto fail; + g->acts = dtmalloc(n * sizeof(DTAct)); + g->nact = n; + for(i = 0; i < n; i++){ + if(*s++ != 't') goto fail; + s = u32unpack(s, (u32int *) &g->acts[i].type); + if(s == nil) goto fail; + if(*s++ != 's') goto fail; + s = u32unpack(s, (u32int *) &g->acts[i].size); + if(s == nil) goto fail; + s = dteunpack(s, &g->acts[i].p); + if(s == nil) goto fail; + switch(g->acts[i].type){ + case ACTTRACE: + g->reclen += g->acts[i].size; + break; + case ACTTRACESTR: + g->reclen += g->acts[i].size; + break; + default: + goto fail; + } + } + break; + case 'G': + *rp = g; + return s; + default: goto fail; + } +fail: + dtgfree(g); + return nil; +} + +char * +dtclunpack(char *s, DTClause **rp) +{ + DTClause *c; + char *e; + int i; + + *rp = nil; + c = dtmalloc(sizeof(DTClause)); + if(*s++ != 'c') goto fail; + s = u32unpack(s, (u32int*) &c->nprob); + if(s == nil) goto fail; + c->probs = dtmalloc(sizeof(char *) * c->nprob); + for(i = 0; i < c->nprob; i++){ + e = strchr(s, '\n'); + if(e == nil) goto fail; + c->probs[i] = dtmalloc(e - s + 1); + memmove(c->probs[i], s, e - s); + s = e + 1; + } + s = dtgunpack(s, &c->gr); + if(s == nil) goto fail; + *rp = c; + return s; +fail: + dtclfree(c); + return nil; +} + +void +dtclfree(DTClause *c) +{ + int i; + + if(c == nil) return; + if(--c->gr->ref == 0) + dtgfree(c->gr); + for(i = 0; i < c->nprob; i++) + free(c->probs[i]); + free(c->probs); + free(c); +} diff --git a/sys/src/libdtracy/prog.c b/sys/src/libdtracy/prog.c new file mode 100644 index 000000000..7aced0815 --- /dev/null +++ b/sys/src/libdtracy/prog.c @@ -0,0 +1,286 @@ +#include +#include +#include + +int +dteverify(DTExpr *p) +{ + int i, nregs; + u32int ins; + u8int a, b, c; + + nregs = 16; + for(i = 0; i < p->n; i++){ + ins = p->b[i]; + + a = ins >> 16; + b = ins >> 8; + c = ins; + switch(ins>>24){ + case DTE_ADD: + if(ins == 0) continue; + /* wet floor */ + case DTE_SUB: + case DTE_MUL: + case DTE_UDIV: + case DTE_UMOD: + case DTE_SDIV: + case DTE_SMOD: + case DTE_AND: + case DTE_OR: + case DTE_XOR: + case DTE_XNOR: + case DTE_LSL: + case DTE_LSR: + case DTE_ASR: + case DTE_SEQ: + case DTE_SNE: + case DTE_SLT: + case DTE_SLE: + if(a >= nregs || b >= nregs || c >= nregs || c == 0) + goto invalid; + break; + case DTE_LDI: + case DTE_XORI: + if(c >= nregs || c == 0) + goto invalid; + break; + case DTE_BEQ: + case DTE_BNE: + case DTE_BLT: + case DTE_BLE: + if(a >= nregs || b >= nregs || i + 1 + c >= p->n) + goto invalid; + break; + case DTE_RET: + if(a >= nregs || b != 0 || c != 0) + goto invalid; + break; + case DTE_LDV: + if(a >= DTNVARS || b >= nregs) + goto invalid; + break; + case DTE_ZXT: + case DTE_SXT: + if(a >= nregs || b == 0 || b > 64 || c >= nregs) + goto invalid; + default: goto invalid; + } + } + if(p->n == 0 || p->b[p->n - 1] >> 24 != DTE_RET){ + werrstr("must end with RET"); + return -1; + } + return 0; + +invalid: + werrstr("invalid instruction %#.8ux @ %#.4ux", ins, i); + return -1; +} + +int +dtgverify(DTActGr *g) +{ + int i; + + if(g->pred != nil && dteverify(g->pred) < 0) + return -1; + for(i = 0; i < g->nact; i++) + switch(g->acts[i].type){ + case ACTTRACE: + if(g->acts[i].p == nil || dteverify(g->acts[i].p) < 0 || (uint)g->acts[i].size > 8) + return -1; + break; + case ACTTRACESTR: + if(g->acts[i].p == nil || dteverify(g->acts[i].p) < 0 || (uint)g->acts[i].size > DTRECMAX) + return -1; + break; + default: + return -1; + } + return 0; +} + +typedef struct ExecInfo ExecInfo; +struct ExecInfo { + int machno; + int epid; + u64int ts; + u64int arg[10]; + DTChan *ch; +}; + +int +dteexec(DTExpr *p, ExecInfo *info, s64int *retv) +{ + s64int R[16]; + u32int ins; + u8int a, b, c; + int i; + + R[0] = 0; + for(i = 0;; i++){ + ins = p->b[i]; + a = ins >> 16; + b = ins >> 8; + c = ins; + switch(ins >> 24){ + case DTE_ADD: R[c] = R[a] + R[b]; break; + case DTE_SUB: R[c] = R[a] - R[b]; break; + case DTE_MUL: R[c] = R[a] * R[b]; break; + case DTE_SDIV: if(R[b] == 0) goto div0; R[c] = R[a] / R[b]; break; + case DTE_SMOD: if(R[b] == 0) goto div0; R[c] = R[a] % R[b]; break; + case DTE_UDIV: if(R[b] == 0) goto div0; R[c] = (uvlong)R[a] / (uvlong)R[b]; break; + case DTE_UMOD: if(R[b] == 0) goto div0; R[c] = (uvlong)R[a] % (uvlong)R[b]; break; + case DTE_AND: R[c] = R[a] & R[b]; break; + case DTE_OR: R[c] = R[a] | R[b]; break; + case DTE_XOR: R[c] = R[a] ^ R[b]; break; + case DTE_XNOR: R[c] = ~(R[a] ^ R[b]); break; + case DTE_LDI: R[c] = (s64int)ins << 40 >> 54 << (ins >> 8 & 63); break; + case DTE_XORI: R[c] |= (s64int)ins << 40 >> 54 << (ins >> 8 & 63); break; + case DTE_LSL: + if((u64int)R[b] >= 64) + R[c] = 0; + else + R[c] = R[a] << R[b]; + break; + case DTE_LSR: + if((u64int)R[b] >= 64) + R[c] = 0; + else + R[c] = (u64int)R[a] >> R[b]; + break; + case DTE_ASR: + if((u64int)R[b] >= 64) + R[c] = R[a] >> 63; + else + R[c] = R[a] >> R[b]; + break; + case DTE_SEQ: R[c] = R[a] == R[b]; break; + case DTE_SNE: R[c] = R[a] != R[b]; break; + case DTE_SLT: R[c] = R[a] < R[b]; break; + case DTE_SLE: R[c] = R[a] <= R[b]; break; + case DTE_BEQ: if(R[a] == R[b]) i += c; break; + case DTE_BNE: if(R[a] != R[b]) i += c; break; + case DTE_BLT: if(R[a] < R[b]) i += c; break; + case DTE_BLE: if(R[a] <= R[b]) i += c; break; + case DTE_LDV: + switch(a){ + case DTV_ARG0: + case DTV_ARG1: + case DTV_ARG2: + case DTV_ARG3: + case DTV_ARG4: + case DTV_ARG5: + case DTV_ARG6: + case DTV_ARG7: + case DTV_ARG8: + case DTV_ARG9: + R[b] = info->arg[a - DTV_ARG0]; + break; + case DTV_TIME: R[b] = info->ts; break; + case DTV_MACHNO: R[b] = info->machno; break; + default: + R[b] = dtgetvar(a); + break; + } + case DTE_ZXT: R[c] = (uvlong)R[a] << 64 - b >> 64 - b; break; + case DTE_SXT: R[c] = (vlong)R[a] << 64 - b >> 64 - b; break; + case DTE_RET: *retv = R[a]; return 0; + } + } + +div0: + snprint(info->ch->errstr, sizeof(info->ch->errstr), "division by zero"); + return -1; +} + +int +dtpeekstr(uvlong addr, u8int *v, int len) +{ + int i; + + for(i = 0; i < len; i++){ + if(addr + i < addr || dtpeek(addr + i, &v[i], 1) < 0){ + memset(v, 0, len); + return -1; + } + if(v[i] == 0) + break; + } + if(i < len) + memset(&v[i], 0, len - i); + return 0; +} + +#define PUT1(c) *bp++ = c; +#define PUT2(c) *bp++ = c; *bp++ = c >> 8; +#define PUT4(c) *bp++ = c; *bp++ = c >> 8; *bp++ = c >> 16; *bp++ = c >> 24; +#define PUT8(c) PUT4(c); PUT4(c>>32); + +static int +dtgexec(DTActGr *g, ExecInfo *info) +{ + DTBuf *b; + u8int *bp; + s64int v; + int i, j; + + b = g->chan->wrbufs[info->machno]; + if(b->wr + g->reclen > DTBUFSZ) + return 0; + if(g->pred != nil){ + if(dteexec(g->pred, info, &v) < 0) + return -1; + if(v == 0) + return 0; + } + bp = &b->data[b->wr]; + PUT4(info->epid); + PUT8(info->ts); + for(i = 0; i < g->nact; i++){ + if(dteexec(g->acts[i].p, info, &v) < 0) + return -1; + switch(g->acts[i].type){ + case ACTTRACE: + for(j = 0; j < g->acts[i].size; j++){ + *bp++ = v; + v >>= 8; + } + break; + case ACTTRACESTR: + if(dtpeekstr(v, bp, g->acts[i].size) < 0){ + snprint(info->ch->errstr, sizeof(info->ch->errstr), "fault @ %#llux", v); + return -1; + } + bp += g->acts[i].size; + break; + } + } + assert(bp - b->data - b->wr == g->reclen); + b->wr = bp - b->data; + return 0; +} + +void +dtptrigger(DTProbe *p, int machno, uvlong arg0, uvlong arg1, uvlong arg2, uvlong arg3) +{ + DTEnab *e; + ExecInfo info; + + info.ts = dttime(); + dtmachlock(machno); + info.machno = machno; + info.arg[0] = arg0; + info.arg[1] = arg1; + info.arg[2] = arg2; + info.arg[3] = arg3; + for(e = p->enablist.probnext; e != &p->enablist; e = e->probnext) + if(e->gr->chan->state == DTCGO){ + info.ch = e->gr->chan; + info.epid = e->epid; + if(dtgexec(e->gr, &info) < 0) + e->gr->chan->state = DTCFAULT; + } + dtmachunlock(machno); +} diff --git a/sys/src/libdtracy/prov.c b/sys/src/libdtracy/prov.c new file mode 100644 index 000000000..8becbc756 --- /dev/null +++ b/sys/src/libdtracy/prov.c @@ -0,0 +1,76 @@ +#include +#include +#include + +char * +dtstrdup(char *n) +{ + char *m; + + m = dtmalloc(strlen(n) + 1); + strcpy(m, n); + setmalloctag(m, getcallerpc(&n)); + return m; +} + +DTProbe * +dtpnew(DTName name, DTProvider *prov, void *aux) +{ + DTProbe *p, **pp; + + p = dtmalloc(sizeof(DTProbe)); + p->provider = dtstrdup(name.provider); + p->function = dtstrdup(name.function); + p->name = dtstrdup(name.name); + p->prov = prov; + p->aux = aux; + p->enablist.probnext = p->enablist.probprev = &p->enablist; + for(pp = &prov->probes; *pp != nil; pp = &(*pp)->provnext) + ; + *pp = p; + return p; +} + +int +dtstrmatch(char *a, char *b) +{ + if(a == nil || *a == 0) return 1; + if(b == nil) return 0; + return strcmp(a, b) == 0; +} + +int +dtpmatch(DTName name, DTProbe ***ret) +{ + DTProbe **l; + int nl; + DTProvider **provp, *prov; + DTProbe **pp, *p; + + l = nil; + nl = 0; + for(provp = dtproviders; prov = *provp, prov != nil; provp++){ + if(!dtstrmatch(name.provider, prov->name)) + continue; + for(pp = &prov->probes; p = *pp, p != nil; pp = &p->provnext) + if(dtstrmatch(name.function, p->function) && dtstrmatch(name.name, p->name)){ + if(ret != nil){ + l = dtrealloc(l, (nl + 1) * sizeof(DTProbe *)); + l[nl] = p; + } + nl++; + } + prov->provide(prov, name); + for(; p = *pp, p != nil; pp = &p->provnext) + if(dtstrmatch(name.function, p->function) && dtstrmatch(name.name, p->name)){ + if(ret != nil){ + l = dtrealloc(l, (nl + 1) * sizeof(DTProbe *)); + l[nl] = p; + } + nl++; + } + } + if(ret != nil) + *ret = l; + return nl; +}