plan9fox/sys/src/9/port/devdtracy.c
2019-06-14 10:30:50 +02:00

571 lines
9.1 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include <dtracy.h>
static 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(up->syserrstr);
}
}
}
enum {
/* Qdir */
Qclone = 1,
Qprobes = 2,
};
enum {
Qdir,
Qctl,
Qprog,
Qbuf,
Qepid,
Qaggbuf,
};
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,
"aggbuf", { Qaggbuf, 0, 0 }, 0, 0440,
};
enum {
CMstop,
CMgo,
};
static Cmdtab dtracyctlmsg[] = {
CMstop, "stop", 1,
CMgo, "go", 1,
};
static DTKChan **dtktab;
static 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 int dtracyen;
static void
dtracyinit(void)
{
dtracyen = getconf("*dtracy") != nil;
if(!dtracyen) return;
machlocks = smalloc(sizeof(Lock) * conf.nmach);
dtinit(conf.nmach);
}
static Chan*
dtracyattach(char *spec)
{
if(!dtracyen)
error("*dtracy= not set");
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-- == 0) goto probes;
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(c->qid.path == Qprobes){
probes:
strcpy(up->genbuf, "probes");
devdir(c, (Qid){Qprobes, 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);
}
if(c->qid.path == Qprobes){
p = nil;
}else{
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\n", e->epid, e->gr->id, e->gr->reclen, e->prob->name);
aux->str = fmtstrflush(&f);
}
return readstr(off, a, n, aux->str);
}
static long
lockedread(DTChan *c, void *a, long n, int(*readf)(DTChan *, void *, int))
{
long rc;
if(waserror()){
qunlock(&dtracylock);
nexterror();
}
eqlock(&dtracylock);
rc = readf(c, a, n);
qunlock(&dtracylock);
poperror();
return rc;
}
static long
handleread(DTChan *c, void *a, long n, int(*readf)(DTChan *, void *, int))
{
long rc, m;
int i;
for(;;){
rc = lockedread(c, a, n, readf);
if(rc < 0) return -1;
if(rc > 0) break;
tsleep(&up->sleep, return0, 0, 250);
}
m = rc;
for(i = 0; i < 3 && m < n/2; i++){
tsleep(&up->sleep, return0, 0, 50);
rc = lockedread(c, (uchar *)a + m, n - m, readf);
if(rc < 0) break;
m += rc;
}
return m;
}
static long
probesread(DTKAux *aux, char *a, long n, vlong off)
{
Fmt f;
DTProbe **l;
int i, nl;
if(aux->str == nil){
fmtstrinit(&f);
nl = dtplist(&l);
for(i = 0; i < nl; i++)
fmtprint(&f, "%s\n", l[i]->name);
dtfree(l);
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;
DTChan *ch;
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;
case Qprobes:
rc = probesread(c->aux, a, n, off);
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:
ch = p->ch;
qunlock(&dtracylock);
poperror();
return handleread(ch, a, n, dtcread);
case Qaggbuf:
ch = p->ch;
qunlock(&dtracylock);
poperror();
return handleread(ch, a, n, dtcaggread);
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;
}
int
dtmachlock(int i)
{
while(i < 0) {
i = dtmachlock(m->machno);
if(i == m->machno)
return i;
dtmachunlock(i);
i = -1;
}
ilock(&machlocks[i]);
return 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;
default:
return 0;
}
}
extern int peek(char *, char *, int);
int
dtpeek(uvlong addr, void *buf, int len)
{
uintptr a;
a = addr;
if(len == 0) return 0;
if(a != addr || a > -(uintptr)len || len < 0) return -1;
if(up == nil || up->privatemem || a >= KZERO) return -1;
return peek((void *)a, buf, len) > 0 ? -1 : 0;
}