diff --git a/sys/include/dtracy.h b/sys/include/dtracy.h index cd8d577b3..438a8d1e7 100644 --- a/sys/include/dtracy.h +++ b/sys/include/dtracy.h @@ -18,7 +18,6 @@ enum { }; #define DTANIL ((u32int)-1) -typedef struct DTName DTName; typedef struct DTProbe DTProbe; typedef struct DTExprState DTExprState; typedef struct DTAct DTAct; @@ -32,12 +31,6 @@ typedef struct DTAgg DTAgg; typedef struct DTBuf DTBuf; typedef struct DTTrigInfo DTTrigInfo; -struct DTName { - char *provider; - char *function; - char *name; -}; - /* we assign all pairs (probe,action-group) (called an enabling or DTEnab) a unique ID called EPID. we could also use probe IDs and action group IDs but using a single 32-bit ID for both is more flexible/efficient. @@ -55,7 +48,7 @@ struct DTEnab { /* probes are never freed */ struct DTProbe { int nenable; - DTName; + char *name; DTEnab enablist; DTProvider *prov; void *aux; /* for the provider */ @@ -65,17 +58,16 @@ struct DTProbe { struct DTProvider { char *name; /* - provide() is called when the user asks for a probe that doesn't exist. + provide() is called when the user first uses a provider. provide() should call dtpnew() to create probes. - it can use the DTName as a hint or just create all probes that it knows about. - the provider has to ensure not to create the same probe multiple times. */ - void (*provide)(DTProvider *, DTName); + void (*provide)(DTProvider *); int (*enable)(DTProbe *); /* enable the probe. return >= 0 for success and < 0 for failure */ void (*disable)(DTProbe *); /* disable the probe */ /* for the library, not the provider */ DTProbe *probes; + int provided; }; /* @@ -246,8 +238,9 @@ void dtinit(int); void dtsync(void); /* probe functions */ -DTProbe *dtpnew(DTName, DTProvider *, void *aux); -int dtpmatch(DTName, DTProbe ***); +DTProbe *dtpnew(char *, DTProvider *, void *aux); +int dtpmatch(char *, DTProbe ***); +int dtplist(DTProbe ***); void dtptrigger(DTProbe *, int, DTTrigInfo *); /* expression functions */ @@ -269,7 +262,7 @@ void dtclfree(DTClause *); /* chan functions */ DTChan *dtcnew(void); void dtcfree(DTChan *); -int dtcaddgr(DTChan *, DTName, DTActGr *); +int dtcaddgr(DTChan *, char *, DTActGr *); int dtcaddcl(DTChan *, DTClause *); int dtcread(DTChan *, void *, int); int dtcaggread(DTChan *, void *, int); diff --git a/sys/man/1/dtracy b/sys/man/1/dtracy index af2d43af4..3830ca453 100644 --- a/sys/man/1/dtracy +++ b/sys/man/1/dtracy @@ -25,8 +25,9 @@ program is a series of statements of one of the following forms \fIprobes\fR \fLif\fR \fIpredicate \fL{\fR \fIactions\fL } \fR .PP \fIProbes\fR is a comma-separated list of probes, such as \fLsys:pwrite:entry\fR. -Each probe consists of three parts separated by \fL:\fR. +Each probe name consists of any number of parts separated by \fL:\fR. If a part is omitted (e.g. \fLqsys::entry\fR), it matches all probes that match the remaining parts. +If the probe name is enclosed in quotation marks, the wildcards \fL*\fR and \fL?\fR are available, e.g. \fL"sys:*stat:entry"\fR. .PP \fIPredicate\fR, if specified, is an expression that must evaluate to a non-zero value for the actions to be executed. .PP diff --git a/sys/src/9/port/devdtracy.c b/sys/src/9/port/devdtracy.c index 4bd47ca0e..018bad624 100644 --- a/sys/src/9/port/devdtracy.c +++ b/sys/src/9/port/devdtracy.c @@ -46,6 +46,7 @@ prog(DTKChan *p, char *s) enum { /* Qdir */ Qclone = 1, + Qprobes = 2, }; enum { @@ -156,6 +157,7 @@ dtracygen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) } 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); @@ -168,6 +170,12 @@ dtracygen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) 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]; @@ -224,9 +232,13 @@ dtracyopen(Chan *c, int omode) 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); + 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); @@ -266,12 +278,8 @@ epidread(DTKAux *aux, DTChan *c, char *a, long n, vlong off) } 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); - } + 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); @@ -315,6 +323,24 @@ handleread(DTChan *c, void *a, long n, int(*readf)(DTChan *, void *, int)) 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) { @@ -332,6 +358,9 @@ dtracyread(Chan *c, void *a, long n, vlong off) 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); } diff --git a/sys/src/9/port/dtracysys.c b/sys/src/9/port/dtracysys.c index 859dd9f84..1e525052d 100644 --- a/sys/src/9/port/dtracysys.c +++ b/sys/src/9/port/dtracysys.c @@ -215,14 +215,11 @@ static Syscall *wraptab[]={ }; static void -sysprovide(DTProvider *prov, DTName) +sysprovide(DTProvider *prov) { - static int provided; - char buf[32]; + char buf[32], pname[32]; int i; - if(provided) return; - provided = 1; dtpsysentry = smalloc(sizeof(Syscall *) * nsyscall); dtpsysreturn = smalloc(sizeof(Syscall *) * nsyscall); for(i = 0; i < nsyscall; i++){ @@ -230,8 +227,10 @@ sysprovide(DTProvider *prov, DTName) 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); + snprint(pname, sizeof(pname), "sys:%s:entry", buf); + dtpsysentry[i] = dtpnew(pname, prov, (void *) i); + snprint(pname, sizeof(pname), "sys:%s:return", buf); + dtpsysreturn[i] = dtpnew(pname, prov, (void *) i); } } diff --git a/sys/src/9/port/dtracytimer.c b/sys/src/9/port/dtracytimer.c index cb3b636f6..0c39a1042 100644 --- a/sys/src/9/port/dtracytimer.c +++ b/sys/src/9/port/dtracytimer.c @@ -22,13 +22,9 @@ dtracytimer(void *) } static void -timerprovide(DTProvider *prov, DTName) +timerprovide(DTProvider *prov) { - static int provided; - - if(provided) return; - provided = 1; - timerprobe = dtpnew((DTName){"timer", "", "1s"}, prov, nil); + timerprobe = dtpnew("timer::1s", prov, nil); } static int diff --git a/sys/src/cmd/dtracy/act.c b/sys/src/cmd/dtracy/act.c index 3412382c3..dde47ae57 100644 --- a/sys/src/cmd/dtracy/act.c +++ b/sys/src/cmd/dtracy/act.c @@ -41,6 +41,28 @@ mkval(int type, ...) return r; } +static char * +insertstars(char *n) +{ + Fmt f; + int partlen; + + fmtstrinit(&f); + partlen = 0; + for(; *n != 0; n++){ + if(*n == ':'){ + if(partlen == 0) + fmtrune(&f, '*'); + partlen = 0; + }else + partlen++; + fmtrune(&f, *n); + } + if(partlen == 0) + fmtrune(&f, '*'); + return fmtstrflush(&f); +} + void clausebegin(void) { @@ -52,7 +74,7 @@ void addprobe(char *s) { clause->probs = erealloc(clause->probs, sizeof(char *) * (clause->nprob + 1)); - clause->probs[clause->nprob++] = strdup(s); + clause->probs[clause->nprob++] = insertstars(s); } static char *aggtypes[] = { diff --git a/sys/src/cmd/dtracy/parse.y b/sys/src/cmd/dtracy/parse.y index 81a57f79b..893d6dac3 100644 --- a/sys/src/cmd/dtracy/parse.y +++ b/sys/src/cmd/dtracy/parse.y @@ -19,6 +19,7 @@ %type expr optexpr %type optsym %type type +%type probe %token TSYM %token TNUM @@ -119,8 +120,11 @@ type: | TSTRING { $$ = type(TYPSTRING); } probes: - TSYM { addprobe($1->name); } - | probes ',' TSYM { addprobe($3->name); } + probe { addprobe($1); } + | probes ',' probe { addprobe($3); } +probe: + TSYM { $$ = $1->name; } + | TSTR { $$ = $1; } %% diff --git a/sys/src/libdtracy/chan.c b/sys/src/libdtracy/chan.c index c29f24eb4..b926c965e 100644 --- a/sys/src/libdtracy/chan.c +++ b/sys/src/libdtracy/chan.c @@ -81,7 +81,7 @@ dtcfree(DTChan *ch) } int -dtcaddgr(DTChan *c, DTName name, DTActGr *gr) +dtcaddgr(DTChan *c, char *name, DTActGr *gr) { DTProbe **l, *p; DTEnab *ep; @@ -92,6 +92,11 @@ dtcaddgr(DTChan *c, DTName name, DTActGr *gr) gr->chan = c; nl = dtpmatch(name, &l); + if(nl == 0){ + dtfree(l); + werrstr("no match for %s", name); + return -1; + } n = 0; for(i = 0; i < nl; i++){ p = l[i]; @@ -118,53 +123,14 @@ dtcaddgr(DTChan *c, DTName name, DTActGr *gr) 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); - } + for(i = 0; i < cl->nprob; i++) + rc += dtcaddgr(c, cl->probs[i], cl->gr); return rc; } diff --git a/sys/src/libdtracy/prov.c b/sys/src/libdtracy/prov.c index 8becbc756..c9e4f822c 100644 --- a/sys/src/libdtracy/prov.c +++ b/sys/src/libdtracy/prov.c @@ -14,14 +14,12 @@ dtstrdup(char *n) } DTProbe * -dtpnew(DTName name, DTProvider *prov, void *aux) +dtpnew(char *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->name = dtstrdup(name); p->prov = prov; p->aux = aux; p->enablist.probnext = p->enablist.probprev = &p->enablist; @@ -31,16 +29,110 @@ dtpnew(DTName name, DTProvider *prov, void *aux) return p; } -int -dtstrmatch(char *a, char *b) +/* does the pattern match at most one probe (or provider if provonly)? */ +static int +patunique(char *pat, int provonly) { - if(a == nil || *a == 0) return 1; - if(b == nil) return 0; - return strcmp(a, b) == 0; + for(;; pat++) + switch(*pat){ + case ':': + if(provonly){ + case 0: + return 1; + } + break; + case '?': + case '*': + return 0; + } +} + +static char * +partmatch(char *pat, char *str, int provonly) +{ + for(;; pat++, str++){ + if(*pat == '*') + return pat; + if(*pat != *str && (*pat != '?' || *str == ':') && (!provonly || *pat != ':' || *str != 0)) + return nil; + if(*pat == 0 || *pat == ':' && provonly) + return (void*)-1; + } +} + +/* + do a wildcard match with * and ?, but don't match : against a wildcard + if provonly, stop at the first : + + replacing empty parts with * is done in user space +*/ +int +dtnamematch(char *pat, char *str, int provonly) +{ + char *patp, *strp, *p; + + patp = partmatch(pat, str, provonly); + if(patp == nil) return 0; + if(patp == (void*)-1) return 1; + /* reached a * */ + strp = str + (patp - pat); + patp++; + for(;;){ + /* try the rest of the pattern against each position */ + p = partmatch(patp, strp, provonly); + if(p == nil){ + if(*strp == 0 || *strp == ':') return 0; + strp++; + continue; + } + if(p == (void*)-1) + return 1; + /* reached another * */ + strp += p - patp; + patp = p + 1; + } } int -dtpmatch(DTName name, DTProbe ***ret) +dtpmatch(char *name, DTProbe ***ret) +{ + DTProbe **l; + int nl; + DTProvider **provp, *prov; + DTProbe **pp, *p; + int unique, uniqueprov; + + l = nil; + nl = 0; + unique = patunique(name, 0); + uniqueprov = patunique(name, 1); + for(provp = dtproviders; prov = *provp, prov != nil; provp++){ + if(!dtnamematch(name, prov->name, 1)) + continue; + if(!prov->provided){ + prov->provided = 1; + prov->provide(prov); + } + for(pp = &prov->probes; p = *pp, p != nil; pp = &p->provnext){ + if(dtnamematch(name, p->name, 0)){ + if(ret != nil){ + l = dtrealloc(l, (nl + 1) * sizeof(DTProbe *)); + l[nl] = p; + } + nl++; + if(unique) goto out; + } + } + if(uniqueprov) goto out; + } +out: + if(ret != nil) + *ret = l; + return nl; +} + +int +dtplist(DTProbe ***ret) { DTProbe **l; int nl; @@ -50,27 +142,21 @@ dtpmatch(DTName name, DTProbe ***ret) 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(!prov->provided){ + prov->provided = 1; + prov->provide(prov); + } + for(pp = &prov->probes; p = *pp, p != nil; pp = &p->provnext){ + if(ret != nil){ + l = dtrealloc(l, (nl + 1) * sizeof(DTProbe *)); + l[nl] = p; } + nl++; + } } if(ret != nil) *ret = l; + else + dtfree(l); return nl; }