shithub: riscv

Download patch

ref: c7304ea03caf68cf035d14f50f6e24e0c16ccdfe
parent: dae5a44111dfb21a714824a5cec2abee4f9f9080
author: aiju <devnull@localhost>
date: Thu Dec 13 05:42:49 EST 2018

dtracy: get rid of DTName struct, support more than three parts in a probe name, wildcard matching

--- a/sys/include/dtracy.h
+++ b/sys/include/dtracy.h
@@ -18,7 +18,6 @@
 };
 #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 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 @@
 /* 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 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 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 @@
 /* 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);
--- a/sys/man/1/dtracy
+++ b/sys/man/1/dtracy
@@ -25,8 +25,9 @@
 \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
--- a/sys/src/9/port/devdtracy.c
+++ b/sys/src/9/port/devdtracy.c
@@ -46,6 +46,7 @@
 enum {
 	/* Qdir */
 	Qclone = 1,
+	Qprobes = 2,
 };
 
 enum {
@@ -156,6 +157,7 @@
 	}
 	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 @@
 		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 @@
 		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 @@
 	}
 	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);
@@ -316,6 +324,24 @@
 }
 
 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;
@@ -331,6 +357,9 @@
 		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);
--- a/sys/src/9/port/dtracysys.c
+++ b/sys/src/9/port/dtracysys.c
@@ -215,14 +215,11 @@
 };
 
 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 @@
 		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);
 	}
 }
 
--- a/sys/src/9/port/dtracytimer.c
+++ b/sys/src/9/port/dtracytimer.c
@@ -22,13 +22,9 @@
 }
 
 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
--- a/sys/src/cmd/dtracy/act.c
+++ b/sys/src/cmd/dtracy/act.c
@@ -41,6 +41,28 @@
 	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 @@
 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[] = {
--- a/sys/src/cmd/dtracy/parse.y
+++ b/sys/src/cmd/dtracy/parse.y
@@ -19,6 +19,7 @@
 %type <n> expr optexpr
 %type <sym> optsym
 %type <t> type
+%type <str> probe
 
 %token <sym> TSYM
 %token <num> TNUM
@@ -119,8 +120,11 @@
 	| 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; }
 
 %%
--- a/sys/src/libdtracy/chan.c
+++ b/sys/src/libdtracy/chan.c
@@ -81,7 +81,7 @@
 }
 
 int
-dtcaddgr(DTChan *c, DTName name, DTActGr *gr)
+dtcaddgr(DTChan *c, char *name, DTActGr *gr)
 {
 	DTProbe **l, *p;
 	DTEnab *ep;
@@ -92,6 +92,11 @@
 	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 @@
 	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;
 }
 
--- a/sys/src/libdtracy/prov.c
+++ b/sys/src/libdtracy/prov.c
@@ -14,14 +14,12 @@
 }
 
 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,46 +29,134 @@
 	return p;
 }
 
+/* does the pattern match at most one probe (or provider if provonly)? */
+static int
+patunique(char *pat, int provonly)
+{
+	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
-dtstrmatch(char *a, char *b)
+dtnamematch(char *pat, char *str, int provonly)
 {
-	if(a == nil || *a == 0) return 1;
-	if(b == nil) return 0;
-	return strcmp(a, b) == 0;
+	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(!dtstrmatch(name.provider, prov->name))
+		if(!dtnamematch(name, prov->name, 1))
 			continue;
-		for(pp = &prov->probes; p = *pp, p != nil; pp = &p->provnext)
-			if(dtstrmatch(name.function, p->function) && dtstrmatch(name.name, p->name)){
+		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;
 			}
-		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(uniqueprov) goto out;
+	}
+out:
+	if(ret != nil)
+		*ret = l;
+	return nl;
+}
+
+int
+dtplist(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(!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;
 }