adding dtracy (crude early version)
This commit is contained in:
parent
8c097ae84a
commit
e6d99771e5
18 changed files with 4038 additions and 0 deletions
530
sys/src/9/port/devdtracy.c
Normal file
530
sys/src/9/port/devdtracy.c
Normal 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
249
sys/src/9/port/dtracysys.c
Normal 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
571
sys/src/cmd/dtracy/act.c
Normal 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
313
sys/src/cmd/dtracy/cgen.c
Normal 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
123
sys/src/cmd/dtracy/dat.h
Normal 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
224
sys/src/cmd/dtracy/dtracy.c
Normal 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
35
sys/src/cmd/dtracy/fns.h
Normal 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
390
sys/src/cmd/dtracy/lex.c
Normal 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
20
sys/src/cmd/dtracy/lint.rc
Executable 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
22
sys/src/cmd/dtracy/mkfile
Normal 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
123
sys/src/cmd/dtracy/parse.y
Normal 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
529
sys/src/cmd/dtracy/type.c
Normal 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
222
sys/src/libdtracy/chan.c
Normal 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
114
sys/src/libdtracy/dtefmt.c
Normal 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
20
sys/src/libdtracy/mkfile
Normal 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
191
sys/src/libdtracy/pack.c
Normal 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
286
sys/src/libdtracy/prog.c
Normal 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
76
sys/src/libdtracy/prov.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue