shithub: riscv

Download patch

ref: e6d99771e5c1eef3f69fc847253d4709ffaa84be
parent: 8c097ae84a500eae9c8e4ee21b7b3ea8f8d23259
author: aiju <devnull@localhost>
date: Sat Nov 10 08:46:16 EST 2018

adding dtracy (crude early version)

diff: cannot open b/sys/src/cmd/dtracy//null: file does not exist: 'b/sys/src/cmd/dtracy//null' diff: cannot open b/sys/src/libdtracy//null: file does not exist: 'b/sys/src/libdtracy//null'
--- /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;
+}