xref: /plan9-contrib/sys/src/9k/port/devtrace.c (revision d46407a37b53d968b453d19c0e464c526df5e227)
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 #include	"netif.h"
8 
9 /*
10  * NB: To be used with 6l -e so tracein/out are called upon
11  * function entry and exit.
12  * There's no trace(3) man page, look at write source to see the
13  * commands.
14  */
15 
16 #pragma profile 0
17 
18 typedef struct Trace Trace;
19 /* This is a trace--a segment of memory to watch for entries and exits */
20 struct Trace {
21 	struct Trace *next;
22 	void *func;
23 	void *start;
24 	void *end;
25 	int enabled;
26 	char name[16];
27 };
28 
29 enum {
30 	Qdir,
31 	Qctl,
32 	Qdata,
33 };
34 
35 enum {
36 	TraceEntry = 1,
37 	TraceExit,
38 };
39 
40 /* fix me make this programmable */
41 enum {
42 	defaultlogsize = 8192,
43 };
44 
45 /* This represents a trace "hit" or event */
46 typedef struct Tracelog Tracelog;
47 struct Tracelog {
48 	uvlong ticks;
49 	int info;
50 	uintptr pc;
51 	/* these are different depending on type */
52 	uintptr dat[5];
53 	int machno;
54 };
55 
56 
57 static Rendez tracesleep;
58 static QLock traceslock;
59 /* this will contain as many entries as there are valid pc values */
60 static Trace **tracemap;
61 static Trace *traces; /* This stores all the traces */
62 static Lock loglk;
63 static Tracelog *tracelog = nil;
64 int traceactive = 0;
65 /* trace indices. These are just unsigned longs. You mask them
66  * to get an index. This makes fifo empty/full etc. trivial.
67  */
68 static uint pw = 0, pr = 0;
69 static int tracesactive = 0;
70 static int all = 0;
71 static int watching = 0;
72 static int slothits = 0;
73 static unsigned int traceinhits = 0;
74 static unsigned int newplfail = 0;
75 static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1;
76 
77 static int printsize = 0;	//The length of a line being printed
78 
79 /* These are for observing a single process */
80 static int *pidwatch = nil;
81 static int numpids = 0;
82 static const PIDWATCHSIZE = 32; /* The number of PIDS that can be watched. Pretty arbitrary. */
83 
84 int codesize = 0;
85 
86 static uvlong lastestamp; /* last entry timestamp */
87 static uvlong lastxstamp; /* last exit timestamp */
88 
89 /* Trace events can be either Entries or Exits */
90 static char eventname[] = {
91 	[TraceEntry] = 'E',
92 	[TraceExit] = 'X',
93 };
94 
95 static Dirtab tracedir[]={
96 	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
97 	"tracectl",	{Qctl},		0,		0664,
98 	"trace",	{Qdata},	0,		0440,
99 };
100 
101 char hex[] = {
102 	'0',
103 	'1',
104 	'2',
105 	'3',
106 	'4',
107 	'5',
108 	'6',
109 	'7',
110 	'8',
111 	'9',
112 	'a',
113 	'b',
114 	'c',
115 	'd',
116 	'e',
117 	'f',
118 };
119 
120 /* big-endian ... */
121 void
hex8(u32int l,char * c)122 hex8(u32int l, char *c)
123 {
124 	int i;
125 	for(i = 2; i; i--){
126 		c[i-1] = hex[l&0xf];
127 		l >>= 4;
128 	}
129 }
130 
131 void
hex16(u32int l,char * c)132 hex16(u32int l, char *c)
133 {
134 	int i;
135 	for(i = 4; i; i--){
136 		c[i-1] = hex[l&0xf];
137 		l >>= 4;
138 	}
139 }
140 
141 void
hex32(u32int l,char * c)142 hex32(u32int l, char *c)
143 {
144 	int i;
145 	for(i = 8; i; i--){
146 		c[i-1] = hex[l&0xf];
147 		l >>= 4;
148 	}
149 }
150 
151 void
hex64(u64int l,char * c)152 hex64(u64int l, char *c)
153 {
154 	hex32(l>>32, c);
155 	hex32(l, &c[8]);
156 }
157 
158 static int
lognonempty(void *)159 lognonempty(void *)
160 {
161 	return pw - pr;
162 }
163 
164 static int
logfull(void)165 logfull(void)
166 {
167 	return (pw - pr) >= logsize;
168 }
169 
170 static u64int
idx(u64int f)171 idx(u64int f)
172 {
173 	return f & logmask;
174 }
175 
176 /*
177  * Check if the given trace overlaps any others
178  * Returns 1 if there is overlap, 0 if clear.
179  */
180 int
overlapping(Trace * p)181 overlapping(Trace *p) {
182 	Trace *curr;
183 
184 	curr = traces;
185 
186 	if (!curr)
187 		return 0;
188 
189 	do {
190 		if ((curr->start < p->start && p->start < curr->end) ||
191 				(curr->start < p->end && p->end < curr->end))
192 			return 1;
193 		curr = curr->next;
194 	} while (curr != nil);
195 
196 	return 0;
197 }
198 
199 /* Make sure a PC is valid and traced; if so, return its Trace */
200 /* if dopanic == 1, the kernel will panic on an invalid PC */
201 struct Trace **
traceslot(void * pc,int dopanic)202 traceslot(void *pc, int dopanic)
203 {
204 	int index;
205 	struct Trace **p;
206 
207 	if (pc > etext) {
208 		if (dopanic)
209 			panic("Bad PC %p", pc);
210 
211 		print("Invalid PC %p\n", pc);
212 		return nil;
213 	}
214 	index = (int)((uintptr)pc - KTZERO);
215 	if (index > codesize){
216 		if (dopanic) {
217 			panic("Bad PC %p", pc);
218 			while(1);
219 		}
220 		print("Invalid PC %p\n", pc);
221 		return nil;
222 	}
223 	p = &tracemap[index];
224 	if (tracemap[index])
225 		ainc(&slothits);
226 	return p;
227 }
228 
229 /* Check if the given PC is traced and return a Trace if so */
230 struct Trace *
traced(void * pc,int dopanic)231 traced(void *pc, int dopanic)
232 {
233 	struct Trace **p;
234 
235 	p = traceslot(pc, dopanic);
236 
237 	if (p == nil)
238 		return nil;
239 
240 	return *p;
241 }
242 
243 /*
244  * Return 1 if pid is being watched or no pids are being watched.
245  * Return 0 if pids are being watched and the argument is not
246  * among them.
247  */
248 int
watchingpid(int pid)249 watchingpid(int pid) {
250 	int i;
251 
252 	if (pidwatch[0] == 0)
253 		return 1;
254 
255 	for (i = 0; i < numpids; i++) {
256 		if (pidwatch[i] == pid)
257 			return 1;
258 	}
259 	return 0;
260 }
261 
262 /*
263  * Remove a trace.
264  */
265 void
removetrace(Trace * p)266 removetrace(Trace *p) {
267 	unsigned char *cp;
268 	struct Trace *prev;
269 	struct Trace *curr;
270 	struct Trace **slot;
271 
272 	slot = traceslot(p->start, 0);
273 	for(cp = p->start; cp <= p->end; slot++, cp++)
274 		*slot = nil;
275 
276 	curr = traces;
277 
278 	if (curr == p) {
279 		if (curr->next) {
280 			traces = curr->next;
281 		} else {
282 			traces = nil;	//this seems to work fine
283 		}
284 		free(curr);
285 		return;
286 	}
287 
288 	prev = curr;
289 	curr = curr->next;
290 	do {
291 		if (curr == p) {
292 			prev->next = curr->next;
293 			return;
294 		}
295 		prev = curr;
296 		curr = curr->next;
297 	} while (curr != nil);
298 
299 }
300 
301 /* it is recommended that you call these with something sane. */
302 /* these next two functions assume you locked tracelock */
303 
304 /* Turn on a trace */
305 void
traceon(struct Trace * p)306 traceon(struct Trace *p)
307 {
308 	unsigned char *cp;
309 	struct Trace **slot;
310 	slot = traceslot(p->start, 0);
311 	for(cp = p->start; cp <= p->end; slot++, cp++)
312 		*slot = p;
313 	p->enabled = 1;
314 	tracesactive++;
315 }
316 
317 /* Turn off a trace */
318 void
traceoff(struct Trace * p)319 traceoff(struct Trace  *p)
320 {
321 	unsigned char *cp;
322 	struct Trace **slot;
323 	slot = traceslot(p->start, 0);
324 	for(cp = p->start; cp <= p->end; slot++, cp++)
325 		*slot = nil;
326 	p->enabled = 0;
327 	tracesactive--;
328 }
329 
330 /* Make a new tracelog (an event) */
331 /* can return NULL, meaning, no record for you */
332 static struct Tracelog *
newpl(void)333 newpl(void)
334 {
335 	uint index;
336 
337 	index = ainc((int *)&pw);
338 
339 	return &tracelog[idx(index)];
340 
341 }
342 
343 /* Called every time a (traced) function starts */
344 /* this is not really smp safe. FIX */
345 void
tracein(void * pc,uintptr a1,uintptr a2,uintptr a3,uintptr a4)346 tracein(void* pc, uintptr a1, uintptr a2, uintptr a3, uintptr a4)
347 {
348 	struct Tracelog *pl;
349 
350 	/* if we are here, tracing is active. Turn it off. */
351 	traceactive = 0;
352 	if (! traced(pc, 1)){
353 		traceactive = 1;
354 		return;
355 	}
356 
357 	ainc((int *)&traceinhits);
358 	/* Continue if we are watching this pid or we're not watching any */
359 	if (!all)
360 		if (!up || !watchingpid(up->pid)){
361 			traceactive = 1;
362 			return;
363 	}
364 
365 	pl = newpl();
366 
367 	if (! pl) {
368 		ainc((int *)&newplfail);
369 		traceactive = 1;
370 		return;
371 	}
372 
373 	cycles(&pl->ticks);
374 
375 	pl->pc = (uintptr)pc;
376 	if (up)
377 		pl->dat[0] = up->pid;
378 	else
379 		pl->dat[0] = (unsigned long)-1;
380 
381 	pl->dat[1] = a1;
382 	pl->dat[2] = a2;
383 	pl->dat[3] = a3;
384 	pl->dat[4] = a4;
385 
386 	pl->info = TraceEntry;
387 	pl->machno = m->machno;
388 	traceactive = 1;
389 }
390 
391 /* Called every time a traced function exits */
392 void
traceout(void * pc,uintptr retval)393 traceout(void* pc, uintptr retval)
394 {
395 	struct Tracelog *pl;
396 	/* if we are here, tracing is active. Turn it off. */
397 	traceactive = 0;
398 	if (! traced(pc, 1)){
399 		traceactive = 1;
400 		return;
401 	}
402 
403 	if (!all)
404 		if (!up || !watchingpid(up->pid)){
405 			traceactive = 1;
406 			return;
407 		}
408 
409 	pl = newpl();
410 	if (! pl){
411 		traceactive = 1;
412 		return;
413 	}
414 
415 	cycles(&pl->ticks);
416 
417 	pl->pc = (uintptr)pc;
418 	if (up)
419 		pl->dat[0] = up->pid;
420 	else
421 		pl->dat[0] = (unsigned long)-1;
422 
423 	pl->dat[1] = retval;
424 	pl->dat[2] = 0;
425 	pl->dat[3] = 0;
426 
427 	pl->info = TraceExit;
428 	pl->machno = m->machno;
429 	traceactive = 1;
430 }
431 
432 /* Create a new trace with the given range */
433 static Trace *
mktrace(void * func,void * start,void * end)434 mktrace(void *func, void *start, void *end)
435 {
436 	Trace *p;
437 	p = mallocz(sizeof p[0], 1);
438 	p->func = func;
439 	p->start = start;
440 	p->end = end;
441 	return p;
442 }
443 
444 /* Get rid of an old trace */
445 static void
freetrace(Trace * p)446 freetrace(Trace *p)
447 {
448 	free(p);
449 }
450 
451 
452 static Chan*
traceattach(char * spec)453 traceattach(char *spec)
454 {
455 	return devattach('T', spec);
456 }
457 
458 static Walkqid*
tracewalk(Chan * c,Chan * nc,char ** name,int nname)459 tracewalk(Chan *c, Chan *nc, char **name, int nname)
460 {
461 	return devwalk(c, nc, name, nname, tracedir, nelem(tracedir), devgen);
462 }
463 
464 static long
tracestat(Chan * c,uchar * db,long n)465 tracestat(Chan *c, uchar *db, long n)
466 {
467 	return devstat(c, db, n, tracedir, nelem(tracedir), devgen);
468 }
469 
470 static Chan*
traceopen(Chan * c,int omode)471 traceopen(Chan *c, int omode)
472 {
473 
474 	/* if there is no tracelog, allocate one. Open always fails
475 	 * if the basic alloc fails. You can resize it later.
476 	 */
477 
478 	codesize = (uintptr)etext - (uintptr)KTZERO;
479 	if (! tracemap)
480 		//tracemap = mallocz(sizeof(struct tracemap *)*codesize, 1);
481 		tracemap = mallocz(sizeof(struct Trace *)*codesize, 1);
482 	if (! tracemap)
483 		error("tracemap malloc failed");
484 	if (! tracelog)
485 		tracelog = mallocz(sizeof(*tracelog)*logsize, 1);
486 	/* I guess malloc doesn't toss an error */
487 	if (! tracelog)
488 		error("tracelog malloc failed");
489 	if (! pidwatch)
490 		pidwatch = mallocz(sizeof(int)*PIDWATCHSIZE, 1);
491 	if (! pidwatch)
492 		error("pidwatch malloc failed");
493 	c = devopen(c, omode, tracedir, nelem(tracedir), devgen);
494 	return c;
495 }
496 
497 static void
traceclose(Chan *)498 traceclose(Chan *)
499 {
500 }
501 
502 /*
503  * Reading from the device, either the data or control files.
504  * The data reading involves deep rminnich magic so we don't have
505  * to call print(), which is traced.
506  */
507 static long
traceread(Chan * c,void * a,long n,vlong offset)508 traceread(Chan *c, void *a, long n, vlong offset)
509 {
510 	char *buf;
511 	char *cp = a;
512 	struct Tracelog *pl;
513 	Trace *p;
514 	int i, j;
515 	int saveactive = traceactive;
516 	traceactive = 0;
517 	static QLock gate;
518 
519 	if (waserror()) {
520 		traceactive = saveactive;
521 		nexterror();
522 	}
523 
524 	if(c->qid.type == QTDIR) {
525 		long l = devdirread(c, a, n, tracedir, nelem(tracedir), devgen);
526 		poperror();
527 		traceactive = saveactive;
528 		return l;
529 	}
530 
531 	switch((int) c->qid.path){
532 	default:
533 		error("traceread: bad qid");
534 	case Qctl:
535 		i = 0;
536 		qlock(&traceslock);
537 		buf = malloc(READSTR);
538 		i += snprint(buf + i, READSTR - i, "logsize %lud\n", logsize);
539 		for(p = traces; p != nil; p = p->next)
540 			i += snprint(buf + i, READSTR - i, "trace %p %p new %s\n",
541 				p->start, p->end, p->name);
542 
543 		for(p = traces; p != nil; p = p->next)
544 			i += snprint(buf + i, READSTR - i, "#trace %p traced? %p\n",
545 				p->func, traced(p->func, 0));
546 
547 		for(p = traces; p != nil; p = p->next)
548 			if (p->enabled)
549 				i += snprint(buf + i, READSTR - i, "trace %s on\n",
550 				p->name);
551 		i += snprint(buf + i, READSTR - i, "#tracehits %d, in queue %d\n",
552 				pw, pw-pr);
553 		i += snprint(buf + i, READSTR - i, "#tracelog %p\n", tracelog);
554 		i += snprint(buf + i, READSTR - i, "#traceactive %d\n", saveactive);
555 		i += snprint(buf + i, READSTR - i, "#slothits %d\n", slothits);
556 		i += snprint(buf + i, READSTR - i, "#traceinhits %d\n", traceinhits);
557 		for (j = 0; j < numpids - 1; j++)
558 			i += snprint(buf + i, READSTR - i, "watch %d\n", pidwatch[j]);
559 		snprint(buf + i, READSTR - i, "watch %d\n", pidwatch[numpids - 1]);
560 		n = readstr(offset, a, n, buf);
561 		free(buf);
562 		qunlock(&traceslock);
563 		break;
564 	case Qdata:
565 
566 		// Set the printsize
567 		/* 32-bit E PCPCPCPC TIMETIMETIMETIME PID# CR XXARG1XX XXARG2XX XXARG3XX XXARG4XX\n */
568 		if (sizeof(uintptr) == 4) {
569 			printsize = 73;		// 32-bit format
570 		} else {
571 			printsize = 121;	// must be 64-bit
572 		}
573 
574 		i = 0;
575 		while(lognonempty((void *)0)){
576 			int j;
577 
578 			if ((pw - pr) > logsize)
579 				pr = pw - logsize;
580 
581 			pl = tracelog + idx(pr);
582 
583 			if ((i + printsize) > n)
584 				break;
585 			/* simple format */
586 			if (sizeof(uintptr) == 4) {
587 				cp[0] = eventname[pl->info];
588 				cp ++;
589 				*cp++ = ' ';
590 				hex32((uint)pl->pc, cp);
591 				cp[8] = ' ';
592 				cp += 9;
593 				hex64(pl->ticks, cp);
594 				cp[16] = ' ';
595 				cp += 17;
596 				hex16(pl->dat[0], cp);
597 				cp += 4;
598 				cp[0] = ' ';
599 				cp++;
600 				hex8(pl->machno, cp);
601 				cp += 2;
602 				cp[0] = ' ';
603 				cp++;
604 				for(j = 1; j < 4; j++){
605 					hex32(pl->dat[j], cp);
606 					cp[8] = ' ';
607 					cp += 9;
608 				}
609 				/* adjust for extra skip above */
610 				cp--;
611 				*cp++ = '\n';
612 				pr++;
613 				i += printsize;
614 			} else  {
615 				cp[0] = eventname[pl->info];
616 				cp ++;
617 				*cp++ = ' ';
618 				hex64((u64int)pl->pc, cp);
619 				cp[16] = ' ';
620 				cp += 17;
621 				hex64(pl->ticks, cp);
622 				cp[16] = ' ';
623 				cp += 17;
624 				hex32(pl->dat[0], cp);
625 				cp += 8;
626 				cp[0] = ' ';
627 				cp++;
628 				cp[0] = ' ';
629 				cp++;
630 				cp[0] = ' ';
631 				cp++;
632 				cp[0] = ' ';
633 				cp++;
634 				hex8(pl->machno, cp);
635 				cp += 4;
636 				for (j = 1; j < 5; j++) {
637 					hex64(pl->dat[j], cp);
638 					cp[16] = ' ';
639 					cp += 17;
640 				}
641 				cp--;
642 				*cp++ = '\n';
643 				pr++;
644 				i += printsize;
645 			}
646 		}
647 		n = i;
648 		break;
649 	}
650 	poperror();
651 	traceactive = saveactive;
652 	return n;
653 }
654 
655 /*
656  * Process commands sent to the ctl file.
657  */
658 static long
tracewrite(Chan * c,void * a,long n,vlong)659 tracewrite(Chan *c, void *a, long n, vlong)
660 {
661 	char *tok[6]; //changed this so "tracein" works with the new 4th arg
662 	char *ep, *s = nil;
663 	Trace *p, **pp, *foo;
664 	int ntok;
665 	int saveactive = traceactive;
666 	traceactive = 0;
667 
668 	qlock(&traceslock);
669 	if(waserror()){
670 		qunlock(&traceslock);
671 		if(s != nil) free(s);
672 		traceactive = saveactive;
673 		nexterror();
674 	}
675 	switch((uintptr)c->qid.path){
676 	default:
677 		error("tracewrite: bad qid");
678 	case Qctl:
679 		s = malloc(n + 1);
680 		memmove(s, a, n);
681 		s[n] = 0;
682 		ntok = tokenize(s, tok, nelem(tok));
683 		if(!strcmp(tok[0], "trace")){	/* 'trace' ktextaddr 'on'|'off'|'mk'|'del' [name] */
684 			if(ntok < 3) {
685 				error("devtrace: usage: 'trace' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");
686 			}
687 			for(pp = &traces; *pp != nil; pp = &(*pp)->next){
688 				if(!strcmp(tok[1], (*pp)->name))
689 					break;
690 }
691 			p = *pp;
692 			if((ntok > 3) && (!strcmp(tok[3], "new"))){
693 				uintptr addr;
694 				void *start, *end, *func;
695 				if (ntok != 5) {
696 					error("devtrace: usage: trace <ktextstart> <ktextend> new <name>");
697 				}
698 				addr = (uintptr)strtoul(tok[1], &ep, 16);
699 				if (addr < KTZERO)
700 					addr |= KTZERO;
701 				func = start = (void *)addr;
702 				if(*ep) {
703 					error("devtrace: start address not in recognized format");
704 				}
705 				addr = (uintptr)strtoul(tok[2], &ep, 16);
706 				if (addr < KTZERO)
707 					addr |= KTZERO;
708 				end = (void *)addr;
709 				if(*ep) {
710 					error("devtrace: end address not in recognized format");
711 				}
712 
713 				if (start > end || start > etext || end > etext)
714 					error("devtrace: invalid address range");
715 
716 				/* What do we do here? start and end are weird *
717 				if((addr < (uintptr)start) || (addr > (uintptr)end)
718 					error("devtrace: address out of bounds");
719 				 */
720 				if(p) {
721 					error("devtrace: trace already exists");
722 				}
723 				p = mktrace(func, start, end);
724 				for (foo = traces; foo != nil; foo = foo->next) {
725 					if (!strcmp(tok[4], foo->name))
726 						error("devtrace: trace with that name already exists");
727 				}
728 
729 				if (!overlapping(p)) {
730 					p->next = traces;
731 					if(ntok < 5)
732 						snprint(p->name, sizeof p->name, "%p", func);
733 					else
734 						strncpy(p->name, tok[4], sizeof p->name);
735 					traces = p;
736 				} else {
737 					error("devtrace: given range overlaps with existing trace");
738 				}
739 			} else if(!strcmp(tok[2], "remove")){
740 				if (ntok != 3)
741 					error("devtrace: usage: trace <name> remove");
742 				if (p == nil) {
743 					error("devtrace: trace not found");
744 				}
745 				removetrace(p);
746 			} else if(!strcmp(tok[2], "on")){
747 				if (ntok != 3)
748 					error("devtrace: usage: trace <name> on");
749 
750 				if(p == nil) {
751 					error("devtrace: trace not found");
752 				}
753 				if (! traced(p->func, 0)){
754 					traceon(p);
755 				}
756 			} else if(!strcmp(tok[2], "off")){
757 				if (ntok != 3)
758 					error("devtrace: usage: trace <name> off");
759 				if(p == nil) {
760 					error("devtrace: trace not found");
761 				}
762 				if(traced(p->func, 0)){
763 					traceoff(p);
764 				}
765 			}
766 		} else if(!strcmp(tok[0], "query")){
767 			/* See if addr is being traced */
768 			Trace* p;
769 			uintptr addr;
770 			if (ntok != 2) {
771 				error("devtrace: usage: query <addr>");
772 			}
773 			addr = (uintptr)strtoul(tok[1], &ep, 16);
774 			if (addr < KTZERO)
775 				addr |= KTZERO;
776 			p = traced((void *)addr, 0);
777 			if (p) {
778 				print("Probing is enabled\n");
779 			} else {
780 				print("Probing is disabled\n");
781 			}
782 		} else if(!strcmp(tok[0], "size")){
783 			int l, size;
784 			struct Tracelog *newtracelog;
785 
786 			if (ntok != 2)
787 				error("devtrace: usage: size <exponent>");
788 
789 			l = strtoul(tok[1], &ep, 0);
790 			if(*ep) {
791 				error("devtrace: size not in recognized format");
792 			}
793 			size = 1 << l;
794 			/* sort of foolish. Alloc new trace first, then free old. */
795 			/* and too bad if there are unread traces */
796 			newtracelog = mallocz(sizeof(*newtracelog)*size, 1);
797 			/* does malloc throw waserror? I don't know */
798 			if (newtracelog){
799 				free(tracelog);
800 				tracelog = newtracelog;
801 				logsize = size;
802 				logmask = size - 1;
803 				pr = pw = 0;
804 			} else  {
805 				error("devtrace:  can't allocate that much");
806 			}
807 		} else if (!strcmp(tok[0], "testtracein")) {
808 			/* Manually jump to a certain bit of traced code */
809 			uintptr pc, a1, a2, a3, a4;
810 			int x;
811 
812 			if (ntok != 6)
813 				error("devtrace: usage: testtracein <pc> <arg1> <arg2> <arg3> <arg4>");
814 
815 			pc = (uintptr)strtoul(tok[1], &ep, 16);
816 			if (pc < KTZERO)
817 				pc |= KTZERO;
818 			a1 = (uintptr)strtoul(tok[2], &ep, 16);
819 			a2 = (uintptr)strtoul(tok[3], &ep, 16);
820 			a3 = (uintptr)strtoul(tok[4], &ep, 16);
821 			a4 = (uintptr)strtoul(tok[5], &ep, 16);
822 
823 			if (traced((void *)pc, 0)) {
824 				x = splhi();
825 				watching = 1;
826 				tracein((void *)pc, a1, a2, a3, a4);
827 				watching = 0;
828 				splx(x);
829 			}
830 		} else if (!strcmp(tok[0], "watch")) {
831 			/* Watch a certain PID */
832 			int pid;
833 
834 			if (ntok != 2) {
835 				error("devtrace: usage: watch [0|<PID>]");
836 			}
837 
838 			pid = atoi(tok[1]);
839 			if (pid == 0) {
840 				pidwatch = mallocz(sizeof(int)*PIDWATCHSIZE, 1);
841 				numpids = 0;
842 			} else if (pid < 0) {
843 				error("PID must be greater than zero.");
844 			} else if (numpids < PIDWATCHSIZE) {
845 				pidwatch[numpids] = pid;
846 				ainc(&numpids);
847 			} else {
848 				error("pidwatch array full!");
849 			}
850 		} else if (!strcmp(tok[0], "start")) {
851 			if (ntok != 1)
852 				error("devtrace: usage: start");
853 			saveactive = 1;
854 		} else if (!strcmp(tok[0], "stop")) {
855 			if (ntok != 1)
856 				error("devtrace: usage: stop");
857 			saveactive = 0;
858 			all = 0;
859 		} else if (!strcmp(tok[0], "all")) {
860 			if (ntok != 1)
861 				error("devtrace: usage: all");
862 			saveactive = 1;
863 			all = 1;
864 		} else {
865 			error("devtrace:  usage: 'trace' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or:  'size' buffersize (power of 2)");
866 		}
867 		free(s);
868 		break;
869 	}
870 	poperror();
871 	qunlock(&traceslock);
872 	traceactive = saveactive;
873 	return n;
874 }
875 
876 Dev tracedevtab = {
877 	'T',
878 	"trace",
879 	devreset,
880 	devinit,
881 	devshutdown,
882 	traceattach,
883 	tracewalk,
884 	tracestat,
885 	traceopen,
886 	devcreate,
887 	traceclose,
888 	traceread,
889 	devbread,
890 	tracewrite,
891 	devbwrite,
892 	devremove,
893 	devwstat,
894 };
895