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