xref: /plan9-contrib/sys/src/9k/port/devprobe.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 #include	"probe.h"
9 
10 enum {
11 	Qdir,
12 	Qctl,
13 	Qdata,
14 };
15 
16 enum {
17 	ProbeEntry = 1,
18 	ProbeExit
19 };
20 
21 /* fix me make this programmable */
22 enum {
23 	defaultlogsize = 1024,
24 	printsize = 64,
25 };
26 
27 typedef struct Probelog Probelog;
28 struct Probelog {
29 	uvlong ticks;
30 	/* yeah, waste a whole int on something stupid but ... */
31 	int info;
32 	ulong pc;
33 	/* these are different depending on type */
34 	long dat[4];
35 };
36 
37 static Rendez probesleep;
38 static QLock probeslk;
39 static Probe *probes;
40 static Lock loglk;
41 static Probelog *probelog = nil;
42 /* probe indices. These are just unsigned longs. You mask them
43  * to get an index. This makes fifo empty/full etc. trivial.
44  */
45 static ulong pw = 0, pr = 0;
46 static int probesactive = 0;
47 static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1;
48 
49 static char eventname[] = {
50 	[ProbeEntry] = 'E',
51 	[ProbeExit] = 'X'
52 };
53 
54 static Dirtab probedir[]={
55 	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
56 	"probectl",	{Qctl},		0,		0664,
57 	"probe",	{Qdata},	0,		0440,
58 };
59 
60 char hex[] = {
61 	'0',
62 	'1',
63 	'2',
64 	'3',
65 	'4',
66 	'5',
67 	'6',
68 	'7',
69 	'8',
70 	'9',
71 	'A',
72 	'B',
73 	'C',
74 	'D',
75 	'E',
76 	'F',
77 };
78 
79 /* big-endian ... */
80 void
hex32(ulong l,char * c)81 hex32(ulong l, char *c)
82 {
83 	int i;
84 	for(i = 8; i; i--){
85 		c[i-1] = hex[l&0xf];
86 		l >>= 4;
87 	}
88 }
89 
90 void
hex64(uvlong l,char * c)91 hex64(uvlong l, char *c)
92 {
93 	hex32(l>>32, c);
94 	hex32(l, &c[8]);
95 }
96 static int
lognonempty(void *)97 lognonempty(void *)
98 {
99 	return pw - pr;
100 }
101 
102 static int
logfull(void)103 logfull(void)
104 {
105 	return (pw - pr) >= logsize;
106 }
107 
108 static ulong
idx(ulong f)109 idx(ulong f)
110 {
111 	return f & logmask;
112 }
113 
114 /* can return NULL, meaning, no record for you */
115 static struct Probelog *
newpl(void)116 newpl(void)
117 {
118 	ulong index;
119 
120 	if (logfull()){
121 		wakeup(&probesleep);
122 		return nil;
123 	}
124 
125 	ilock(&loglk);
126 	index = pw++;
127 	iunlock(&loglk);
128 
129 	return &probelog[idx(index)];
130 
131 }
132 
133 static void
probeentry(Probe * p)134 probeentry(Probe *p)
135 {
136 	struct Probelog *pl;
137 //print("probeentry %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
138 	pl = newpl();
139 	if (! pl)
140 		return;
141 	cycles(&pl->ticks);
142 	pl->pc = (ulong)p->func;
143 	pl->dat[0] = p->argp[0];
144 	pl->dat[1] = p->argp[1];
145 	pl->dat[2] = p->argp[2];
146 	pl->dat[3] = p->argp[3];
147 	pl->info = ProbeEntry;
148 }
149 
150 static void
probeexit(Probe * p)151 probeexit(Probe *p)
152 {
153 //print("probeexit %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
154 	struct Probelog *pl;
155 	pl = newpl();
156 	if (! pl)
157 		return;
158 	cycles(&pl->ticks);
159 	pl->pc = (ulong)p->func;
160 	pl->dat[0] = p->rval;
161 	pl->info = ProbeExit;
162 }
163 
164 static Chan*
probeattach(char * spec)165 probeattach(char *spec)
166 {
167 	return devattach('+', spec);
168 }
169 
170 static Walkqid*
probewalk(Chan * c,Chan * nc,char ** name,int nname)171 probewalk(Chan *c, Chan *nc, char **name, int nname)
172 {
173 	return devwalk(c, nc, name, nname, probedir, nelem(probedir), devgen);
174 }
175 
176 static long
probestat(Chan * c,uchar * db,long n)177 probestat(Chan *c, uchar *db, long n)
178 {
179 	return devstat(c, db, n, probedir, nelem(probedir), devgen);
180 }
181 
182 static Chan*
probeopen(Chan * c,int omode)183 probeopen(Chan *c, int omode)
184 {
185 	/* if there is no probelog, allocate one. Open always fails
186 	  * if the basic alloc fails. You can resize it later.
187 	  */
188 	if (! probelog)
189 		probelog = malloc(sizeof(*probelog)*logsize);
190 	/* I guess malloc doesn't toss an error */
191 	if (! probelog)
192 		error("probelog malloc failed");
193 
194 	c = devopen(c, omode, probedir, nelem(probedir), devgen);
195 	return c;
196 }
197 
198 static void
probeclose(Chan *)199 probeclose(Chan *)
200 {
201 }
202 
203 static long
proberead(Chan * c,void * a,long n,vlong offset)204 proberead(Chan *c, void *a, long n, vlong offset)
205 {
206 	char *buf;
207 	char *cp = a;
208 	struct Probelog *pl;
209 	Probe *p;
210 	int i;
211 	static QLock gate;
212 	if(c->qid.type == QTDIR)
213 		return devdirread(c, a, n, probedir, nelem(probedir), devgen);
214 	switch((ulong)c->qid.path){
215 	default:
216 		error("proberead: bad qid");
217 	case Qctl:
218 		buf = malloc(READSTR);
219 		i = 0;
220 		qlock(&probeslk);
221 		i += snprint(buf + i, READSTR - i, "logsize %lud\n", logsize);
222 		for(p = probes; p != nil; p = p->next)
223 			i += snprint(buf + i, READSTR - i, "probe %p new %s\n",
224 				p->func, p->name);
225 
226 		for(p = probes; p != nil; p = p->next)
227 			if (p->enabled)
228 				i += snprint(buf + i, READSTR - i, "probe %s on\n",
229 				p->name);
230 		i += snprint(buf + i, READSTR - i, "#probehits %lud, in queue %lud\n",
231 				pw, pw-pr);
232 		snprint(buf + i, READSTR - i, "#probelog %p\n", probelog);
233 		qunlock(&probeslk);
234 		n = readstr(offset, a, n, buf);
235 		free(buf);
236 		break;
237 	case Qdata:
238 		qlock(&gate);
239 		if(waserror()){
240 			qunlock(&gate);
241 			nexterror();
242 		}
243 		while(!lognonempty(nil))
244 			tsleep(&probesleep, lognonempty, nil, 5000);
245 		i = 0;
246 		while(lognonempty((void *)0)){
247 			int j;
248 			pl = probelog + idx(pr);
249 
250 			if ((i + printsize) >= n)
251 				break;
252 			/* simple format */
253 			cp[0] = eventname[pl->info];
254 			cp ++;
255 			*cp++ = ' ';
256 			hex32(pl->pc, cp);
257 			cp[8] = ' ';
258 			cp += 9;
259 			hex64(pl->ticks, cp);
260 			cp[16] = ' ';
261 			cp += 17;
262 			for(j = 0; j < 4; j++){
263 				hex32(pl->dat[j], cp);
264 				cp[8] = ' ';
265 				cp += 9;
266 			}
267 			/* adjust for extra skip above */
268 			cp--;
269 			*cp++ = '\n';
270 			pr++;
271 			i += printsize;
272 		}
273 		poperror();
274 		qunlock(&gate);
275 		n = i;
276 		break;
277 	}
278 	return n;
279 }
280 
281 static long
probewrite(Chan * c,void * a,long n,vlong)282 probewrite(Chan *c, void *a, long n, vlong)
283 {
284 	char *tok[5];
285 	char *ep, *s = nil;
286 	Probe *p, **pp;
287 	int ntok;
288 
289 	qlock(&probeslk);
290 	if(waserror()){
291 		qunlock(&probeslk);
292 		if(s != nil) free(s);
293 		nexterror();
294 	}
295 	switch((ulong)c->qid.path){
296 	default:
297 		error("proberead: bad qid");
298 	case Qctl:
299 		s = malloc(n + 1);
300 		memmove(s, a, n);
301 		s[n] = 0;
302 		ntok = tokenize(s, tok, nelem(tok));
303 		if(!strcmp(tok[0], "probe")){	/* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */
304 			if(ntok < 3)
305 				error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");
306 			for(pp = &probes; *pp != nil; pp = &(*pp)->next)
307 				if(!strcmp(tok[1], (*pp)->name))
308 					break;
309 			p = *pp;
310 			if(!strcmp(tok[2], "new")){
311 				ulong addr;
312 				void *func;
313 				addr = strtoul(tok[1], &ep, 0);
314 				func = (void*)addr;
315 				if(*ep)
316 					error("devprobe: address not in recognized format");
317 			//	if(addr < ((ulong) start) || addr > ((ulong) end))
318 			//		error("devprobe: address out of bounds");
319 				if(p != nil)
320 					error("devprobe: %#p already has probe");
321 				p = mkprobe(func, probeentry, probeexit);
322 				p->next = probes;
323 				if(ntok < 4)
324 					snprint(p->name, sizeof p->name, "%p", func);
325 				else
326 					strncpy(p->name, tok[3], sizeof p->name);
327 				probes = p;
328 			} else if(!strcmp(tok[2], "on")){
329 				if(p == nil)
330 					error("devprobe: probe not found");
331 				if(!p->enabled)
332 					probeinstall(p);
333 print("probeinstall in devprobe\n");
334 				probesactive++;
335 			} else if(!strcmp(tok[2], "off")){
336 				if(p == nil)
337 					error("devprobe: probe not found");
338 				if(p->enabled)
339 					probeuninstall(p);
340 				probesactive--;
341 			} else if(!strcmp(tok[2], "del")){
342 				if(p == nil)
343 					error("devprobe: probe not found");
344 				if(p->enabled)
345 					probeuninstall(p);
346 				probesactive--;
347 				*pp = p->next;
348 				freeprobe(p);
349 			} else if(!strcmp(tok[2], "mv")){
350 				if(p == nil)
351 					error("devprobe: probe not found");
352 				if(ntok < 4)
353 					error("devprobe: rename without new name?");
354 				strncpy(p->name, tok[3], sizeof p->name);
355 			}
356 		} else if(!strcmp(tok[0], "size")){
357 			int l, size;
358 			struct Probelog *newprobelog;
359 			l = strtoul(tok[1], &ep, 0);
360 			if(*ep)
361 				error("devprobe: size not in recognized format");
362 			size = 1 << l;
363 			/* sort of foolish. Alloc new probe first, then free old. */
364 			/* and too bad if there are unread probes */
365 			newprobelog = malloc(sizeof(*newprobelog)*size);
366 			/* does malloc throw waserror? I don't know */
367 			free(probelog);
368 			probelog = newprobelog;
369 			logsize = size;
370 			pr = pw = 0;
371 		} else {
372 			error("devprobe:  usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or:  'size' buffersize (power of 2)");
373 		}
374 		free(s);
375 		break;
376 	}
377 	poperror();
378 	qunlock(&probeslk);
379 	return n;
380 }
381 
382 Dev probedevtab = {
383 	'+',
384 	"probe",
385 	devreset,
386 	devinit,
387 	devshutdown,
388 	probeattach,
389 	probewalk,
390 	probestat,
391 	probeopen,
392 	devcreate,
393 	probeclose,
394 	proberead,
395 	devbread,
396 	probewrite,
397 	devbwrite,
398 	devremove,
399 	devwstat,
400 };
401