570 lines
9.1 KiB
C
570 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;
|
|
}
|