adding dtracy (crude early version)

front
aiju 2018-11-10 13:46:16 +00:00
parent 8c097ae84a
commit e6d99771e5
18 changed files with 4038 additions and 0 deletions

530
sys/src/9/port/devdtracy.c Normal file
View File

@ -0,0 +1,530 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include <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,
};

249
sys/src/9/port/dtracysys.c Normal file
View File

@ -0,0 +1,249 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "/sys/src/libc/9syscall/sys.h"
#include <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,
};

571
sys/src/cmd/dtracy/act.c Normal file
View File

@ -0,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);
}
}
}

313
sys/src/cmd/dtracy/cgen.c Normal file
View File

@ -0,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;
}

123
sys/src/cmd/dtracy/dat.h Normal file
View File

@ -0,0 +1,123 @@
typedef struct Node Node;
typedef struct Symbol Symbol;
typedef struct SymTab SymTab;
typedef struct Clause Clause;
typedef struct Enab Enab;
typedef struct Stat Stat;
typedef struct Type Type;
enum {
SYMHASH = 256,
};
struct Type {
enum {
TYPINVAL,
TYPINT,
TYPPTR,
TYPSTRING,
} type;
int size;
uchar sign;
Type *ref;
Type *typenext;
};
struct Symbol {
enum {
SYMNONE,
SYMVAR,
} type;
char *name;
int idx;
Symbol *next;
Type *typ;
};
struct SymTab {
Symbol *sym[SYMHASH];
};
struct Node {
enum {
OINVAL,
OSYM,
ONUM,
OSTR,
OBIN,
OLNOT,
OTERN,
ORECORD,
OCAST,
} type;
enum {
OPINVAL,
OPADD,
OPSUB,
OPMUL,
OPDIV,
OPMOD,
OPAND,
OPOR,
OPXOR,
OPLSH,
OPRSH,
OPEQ,
OPNE,
OPLT,
OPLE,
OPLAND,
OPLOR,
OPXNOR,
} op;
Node *n1, *n2, *n3;
Symbol *sym;
char *str;
s64int num;
/* used by elidecasts() */
char databits;
enum {UPZX, UPSX} upper;
int recsize;
Type *typ;
};
struct Stat {
enum {
STATEXPR,
STATPRINT,
STATPRINTF,
} type;
Node *n;
int narg;
Node **arg;
};
struct Clause {
int id;
Stat *stats;
int nstats;
char **probs;
int nprob;
DTExpr *pred;
};
struct Enab {
int epid;
int reclen;
char *probe;
Clause *cl;
Enab *next;
};
extern int errors;
#pragma varargck type "α" int
#pragma varargck type "t" int
#pragma varargck type "τ" Type *
#pragma varargck type "ε" Node *
#pragma varargck argpos error 1
extern int dflag;

224
sys/src/cmd/dtracy/dtracy.c Normal file
View File

@ -0,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);
}

35
sys/src/cmd/dtracy/fns.h Normal file
View File

@ -0,0 +1,35 @@
void yyerror(char *);
int yylex(void);
int yyparse(void);
Node *node(int, ...);
int nodetfmt(Fmt *);
int typetfmt(Fmt *);
int typefmt(Fmt *);
int nodefmt(Fmt *);
void *emalloc(ulong);
void *erealloc(void *, ulong);
DTAct action(int, DTExpr *);
DTExpr *codegen(Node *);
void error(char *, ...);
Symbol *getsym(char *);
void lexinit(void);
void lexstring(char *);
void clausebegin(void);
void addstat(int, ...);
void addarg(Node *);
void addprobe(char *);
void addpred(DTExpr *);
void clauseend(void);
void packclauses(Fmt *);
void addepid(u32int, u32int, int, char*);
int parsebuf(uchar *, int, Biobuf*);
Node *tracegen(Node *, DTActGr *, int *);
void actgradd(DTActGr *, DTAct);
void needruntime(Node *);
void dump(void);
vlong evalop(int, int, vlong, vlong);
Node *exprcheck(Node *, int);
Type *type(int, ...);
int min(int, int);
int max(int, int);
Node *addtype(Type *, Node *);

390
sys/src/cmd/dtracy/lex.c Normal file
View File

@ -0,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;
}
}

20
sys/src/cmd/dtracy/lint.rc Executable file
View File

@ -0,0 +1,20 @@
#!/bin/rc
# check for full switch statements
nl='
'
nodetypes=`''{sed -n '/OINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OINVAL$'}
switches=`$nl{grep -n '/\*nodetype\*/' *.[ch]}
for(l in $switches){
f=`:{echo $l}
a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)}
comm -23 <{echo $nodetypes} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p'
}
oper=`''{sed -n '/OPINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OPINVAL$'}
switches=`$nl{grep -n '/\*oper\*/' *.[ch]}
for(l in $switches){
f=`:{echo $l}
a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)}
comm -23 <{echo $oper} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p'
}

22
sys/src/cmd/dtracy/mkfile Normal file
View File

@ -0,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

123
sys/src/cmd/dtracy/parse.y Normal file
View File

@ -0,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); }
%%

529
sys/src/cmd/dtracy/type.c Normal file
View File

@ -0,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;
}

222
sys/src/libdtracy/chan.c Normal file
View File

@ -0,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;
}

114
sys/src/libdtracy/dtefmt.c Normal file
View File

@ -0,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;
}

20
sys/src/libdtracy/mkfile Normal file
View File

@ -0,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

191
sys/src/libdtracy/pack.c Normal file
View File

@ -0,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);
}

286
sys/src/libdtracy/prog.c Normal file
View File

@ -0,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);
}

76
sys/src/libdtracy/prov.c Normal file
View File

@ -0,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;
}