ref: e6d99771e5c1eef3f69fc847253d4709ffaa84be
parent: 8c097ae84a500eae9c8e4ee21b7b3ea8f8d23259
author: aiju <devnull@localhost>
date: Sat Nov 10 08:46:16 EST 2018
adding dtracy (crude early version)
--- /dev/null
+++ b/sys/src/9/port/devdtracy.c
@@ -1,0 +1,530 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include <dtracy.h>
+
+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,
+};
+
--- /dev/null
+++ b/sys/src/9/port/dtracysys.c
@@ -1,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 <dtracy.h>
+#include <ctype.h>
+
+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,
+};
--- /dev/null
+++ b/sys/src/cmd/dtracy/act.c
@@ -1,0 +1,571 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#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);
+ }
+ }
+}
--- /dev/null
+++ b/sys/src/cmd/dtracy/cgen.c
@@ -1,0 +1,313 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#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<<n) != 0);
+ assert(n != 0);
+ regsused &= ~(1<<n);
+}
+
+static int
+popcount(u64int s)
+{
+ s = (s & 0x5555555555555555ULL) + (s >> 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;
+}
--- /dev/null
+++ b/sys/src/cmd/dtracy/dat.h
@@ -1,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;
--- /dev/null
+++ b/sys/src/cmd/dtracy/dtracy.c
@@ -1,0 +1,224 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/dtracy/fns.h
@@ -1,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 *);
--- /dev/null
+++ b/sys/src/cmd/dtracy/lex.c
@@ -1,0 +1,390 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#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;
+ }
+}
--- /dev/null
+++ b/sys/src/cmd/dtracy/lint.rc
@@ -1,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'
+}
--- /dev/null
+++ b/sys/src/cmd/dtracy/mkfile
@@ -1,0 +1,22 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=dtracy
+OFILES=\
+ dtracy.$O\
+ lex.$O\
+ y.tab.$O\
+ cgen.$O\
+ act.$O\
+ type.$O\
+
+YFILES=parse.y
+
+HFILES=\
+ dat.h\
+ fns.h\
+
+</sys/src/cmd/mkone
+
+YFLAGS=-v -d -D1
+lex.$O: y.tab.h
--- /dev/null
+++ b/sys/src/cmd/dtracy/parse.y
@@ -1,0 +1,123 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+%}
+
+%union{
+ Node *n;
+ Symbol *sym;
+ DTExpr *e;
+ s64int num;
+ char *str;
+ Type *t;
+}
+
+%type <n> expr
+%type <t> type
+
+%token <sym> TSYM
+%token <num> TNUM
+%token <str> 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); }
+
+
+%%
--- /dev/null
+++ b/sys/src/cmd/dtracy/type.c
@@ -1,0 +1,529 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/libdtracy/chan.c
@@ -1,0 +1,222 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+
+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;
+}
--- /dev/null
+++ b/sys/src/libdtracy/dtefmt.c
@@ -1,0 +1,114 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+
+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;
+}
--- /dev/null
+++ b/sys/src/libdtracy/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libdtracy.a
+
+OFILES=\
+ prov.$O\
+ prog.$O\
+ dtefmt.$O\
+ pack.$O\
+ chan.$O\
+
+HFILES=\
+ /sys/include/dtracy.h\
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/sys/src/libdtracy/pack.c
@@ -1,0 +1,191 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+
+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);
+}
--- /dev/null
+++ b/sys/src/libdtracy/prog.c
@@ -1,0 +1,286 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+
+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);
+}
--- /dev/null
+++ b/sys/src/libdtracy/prov.c
@@ -1,0 +1,76 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+
+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;
+}