xref: /plan9-contrib/sys/src/cmd/trace.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
1 #include <u.h>
2 #include <tos.h>
3 #include <libc.h>
4 #include <thread.h>
5 #include <ip.h>
6 #include <bio.h>
7 #include <stdio.h>
8 #include <draw.h>
9 #include <mouse.h>
10 #include <cursor.h>
11 #include <keyboard.h>
12 #include "trace.h"
13 
14 #pragma	varargck	type	"t"		vlong
15 #pragma	varargck	type	"U"		uvlong
16 
17 #define NS(x)	((vlong)x)
18 #define US(x)	(NS(x) * 1000ULL)
19 #define MS(x)	(US(x) * 1000ULL)
20 #define S(x)	(MS(x) * 1000ULL)
21 
22 #define numblocks(a, b)	(((a) + (b) - 1) / (b))
23 #define roundup(a, b)	(numblocks((a), (b)) * (b))
24 
25 enum {
26 	OneRound = MS(1)/2LL,
27 	MilliRound = US(1)/2LL,
28 };
29 
30 typedef struct Event	Event;
31 typedef struct Task	Task;
32 struct Event {
33 	Traceevent;
34 	vlong	etime;	/* length of block to draw */
35 };
36 
37 struct Task {
38 	int	pid;
39 	char	*name;
40 	int	nevents;
41 	Event	*events;
42 	vlong	tstart;
43 	vlong	total;
44 	vlong	runtime;
45 	vlong	runmax;
46 	vlong	runthis;
47 	long	runs;
48 	ulong	tevents[Nevent];
49 };
50 
51 enum {
52 	Nevents = 1024,
53 	Ncolor = 6,
54 	K = 1024,
55 };
56 
57 vlong	now, prevts;
58 
59 int	newwin;
60 int	Width = 1000;
61 int	Height = 100;		// Per task
62 int	topmargin = 8;
63 int	bottommargin = 4;
64 int	lineht = 12;
65 int	wctlfd;
66 int	nevents;
67 Traceevent *eventbuf;
68 Event	*event;
69 
70 void drawtrace(void);
71 int schedparse(char*, char*, char*);
72 int timeconv(Fmt*);
73 
74 char *schedstatename[] = {
75 	[SAdmit] =	"Admit",
76 	[SSleep] =	"Sleep",
77 	[SDead] =	"Dead",
78 	[SDeadline] =	"Deadline",
79 	[SEdf] =	"Edf",
80 	[SExpel] =	"Expel",
81 	[SReady] =	"Ready",
82 	[SRelease] =	"Release",
83 	[SRun] =	"Run",
84 	[SSlice] =	"Slice",
85 	[SInts] =	"Ints",
86 	[SInte] =	"Inte",
87 	[SUser] = 	"User",
88 	[SYield] =	"Yield",
89 };
90 
91 struct {
92 	vlong	scale;
93 	vlong	bigtics;
94 	vlong	littletics;
95 	int	sleep;
96 } scales[] = {
97 	{	US(500),	US(100),	US(50),		  0},
98 	{	US(1000),	US(500),	US(100),	  0},
99 	{	US(2000),	US(1000),	US(200),	  0},
100 	{	US(5000),	US(1000),	US(500),	  0},
101 	{	MS(10),		MS(5),		MS(1),		 20},
102 	{	MS(20),		MS(10),		MS(2),		 20},
103 	{	MS(50),		MS(10),		MS(5),		 20},
104 	{	MS(100),	MS(50),		MS(10),		 20},	/* starting scaleno */
105 	{	MS(200),	MS(100),	MS(20),		 20},
106 	{	MS(500),	MS(100),	MS(50),		 50},
107 	{	MS(1000),	MS(500),	MS(100),	100},
108 	{	MS(2000),	MS(1000),	MS(200),	100},
109 	{	MS(5000),	MS(1000),	MS(500),	100},
110 	{	S(10),		S(50),		S(1),		100},
111 	{	S(20),		S(10),		S(2),		100},
112 	{	S(50),		S(10),		S(5),		100},
113 	{	S(100),		S(50),		S(10),		100},
114 	{	S(200),		S(100),		S(20),		100},
115 	{	S(500),		S(100),		S(50),		100},
116 	{	S(1000),	S(500),		S(100),		100},
117 };
118 
119 int ntasks, verbose, triggerproc, paused;
120 Task *tasks;
121 Image *cols[Ncolor][4];
122 Font *mediumfont, *tinyfont;
123 Image *grey, *red, *green, *blue, *bg, *fg;
124 char*profdev = "/proc/trace";
125 
126 static void
127 usage(void)
128 {
129 	fprint(2, "Usage: %s [-d profdev] [-w] [-v] [-t triggerproc] [processes]\n", argv0);
130 	exits(nil);
131 }
132 
133 void
134 threadmain(int argc, char **argv)
135 {
136 	int fd, i;
137 	char fname[80];
138 
139 	fmtinstall('t', timeconv);
140 	ARGBEGIN {
141 	case 'd':
142 		profdev = EARGF(usage());
143 		break;
144 	case 'v':
145 		verbose = 1;
146 		break;
147 	case 'w':
148 		newwin++;
149 		break;
150 	case 't':
151 		triggerproc = (int)strtol(EARGF(usage()), nil, 0);
152 		break;
153 	default:
154 		usage();
155 	}
156 	ARGEND;
157 
158 	fname[sizeof fname - 1] = 0;
159 	for(i = 0; i < argc; i++){
160 		snprint(fname, sizeof fname - 2, "/proc/%s/ctl",
161 					argv[i]);
162 		if((fd = open(fname, OWRITE)) < 0){
163 			fprint(2, "%s: cannot open %s: %r\n",
164 						argv[0], fname);
165 			continue;
166 		}
167 
168 		if(fprint(fd, "trace 1") < 0)
169 			fprint(2, "%s: cannot enable tracing on %s: %r\n",
170 						argv[0], fname);
171 		close(fd);
172 	}
173 
174 	drawtrace();
175 }
176 
177 static void
178 mkcol(int i, int c0, int c1, int c2)
179 {
180 	cols[i][0] = allocimagemix(display, c0, DWhite);
181 	cols[i][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c1);
182 	cols[i][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c2);
183 	cols[i][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c0);
184 }
185 
186 static void
187 colinit(void)
188 {
189 	mediumfont = openfont(display, "/lib/font/bit/lucidasans/unicode.10.font");
190 	if(mediumfont == nil)
191 		mediumfont = font;
192 	tinyfont = openfont(display, "/lib/font/bit/lucidasans/unicode.7.font");
193 	if(tinyfont == nil)
194 		tinyfont = font;
195 	topmargin = mediumfont->height+2;
196 	bottommargin = tinyfont->height+2;
197 
198 	/* Peach */
199 	mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
200 	/* Aqua */
201 	mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
202 	/* Yellow */
203 	mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
204 	/* Green */
205 	mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
206 	/* Blue */
207 	mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
208 	/* Grey */
209 	cols[5][0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEEEEEEFF);
210 	cols[5][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
211 	cols[5][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x888888FF);
212 	cols[5][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF);
213 	grey = cols[5][2];
214 	red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFF0000FF);
215 	green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x00FF00FF);
216 	blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF);
217 	bg = display->white;
218 	fg = display->black;
219 }
220 
221 static void
222 redraw(int scaleno)
223 {
224 	int n, i, j, x;
225 	char buf[256];
226 	Point p, q;
227 	Rectangle r, rtime;
228 	Task *t;
229 	vlong ts, oldestts, newestts, period, ppp, scale, s, ss;
230 
231 #	define time2x(t)	((int)(((t) - oldestts) / ppp))
232 
233 	scale = scales[scaleno].scale;
234 	period = scale + scales[scaleno].littletics;
235 	ppp = period / Width;	// period per pixel.
236 
237 	/* Round `now' to a nice number */
238 	newestts = now - (now % scales[scaleno].bigtics) +
239 			(scales[scaleno].littletics>>1);
240 
241 	oldestts = newestts - period;
242 
243 //print("newestts %t, period %t, %d-%d\n", newestts, period, time2x(oldestts), time2x(newestts));
244 	if (prevts < oldestts){
245 		oldestts = newestts - period;
246 
247 		prevts = oldestts;
248 		draw(screen, screen->r, bg, nil, ZP);
249 	}else{
250 		/* just white out time */
251 		rtime = screen->r;
252 		rtime.min.x = rtime.max.x - stringwidth(mediumfont, "00000000000.000s");
253 		rtime.max.y = rtime.min.y + mediumfont->height;
254 		draw(screen, rtime, bg, nil, ZP);
255 	}
256 	p = screen->r.min;
257 	for (n = 0; n != ntasks; n++) {
258 		t = &tasks[n];
259 		/* p is upper left corner for this task */
260 		rtime = Rpt(p, addpt(p, Pt(500, mediumfont->height)));
261 		draw(screen, rtime, bg, nil, ZP);
262 		snprint(buf, sizeof(buf), "%d %s", t->pid, t->name);
263 		q = string(screen, p, fg, ZP, mediumfont, buf);
264 		s = now - t->tstart;
265 		if(t->tevents[SRelease])
266 			snprint(buf, sizeof(buf), " per %t — avg: %t max: %t",
267 				(vlong)(s/t->tevents[SRelease]),
268 				(vlong)(t->runtime/t->tevents[SRelease]),
269 				t->runmax);
270 		else if((s /=1000000000LL) != 0)
271 			snprint(buf, sizeof(buf), " per 1s — avg: %t total: %t",
272 				t->total/s,
273 				t->total);
274 		else
275 			snprint(buf, sizeof(buf), " total: %t", t->total);
276 		string(screen, q, fg, ZP, tinyfont, buf);
277 		p.y += Height;
278 	}
279 	x = time2x(prevts);
280 
281 	p = screen->r.min;
282 	for (n = 0; n != ntasks; n++) {
283 		t = &tasks[n];
284 
285 		/* p is upper left corner for this task */
286 
287 		/* Move part already drawn */
288 		r = Rect(p.x, p.y + topmargin, p.x + x, p.y+Height);
289 		draw(screen, r, screen, nil, Pt(p.x + Width - x, p.y + topmargin));
290 
291 		r.max.x = screen->r.max.x;
292 		r.min.x += x;
293 		draw(screen, r, bg, nil, ZP);
294 
295 		line(screen, addpt(p, Pt(x, Height - lineht)), Pt(screen->r.max.x, p.y + Height - lineht),
296 			Endsquare, Endsquare, 0, cols[n % Ncolor][1], ZP);
297 
298 		for (i = 0; i < t->nevents-1; i++)
299 			if (prevts < t->events[i + 1].time)
300 				break;
301 
302 		if (i > 0) {
303 			memmove(t->events, t->events + i, (t->nevents - i) * sizeof(Event));
304 			t->nevents -= i;
305 		}
306 
307 		for (i = 0; i != t->nevents; i++) {
308 			Event *e = &t->events[i], *_e;
309 			int sx, ex;
310 
311 			switch (e->etype & 0xffff) {
312 			case SAdmit:
313 				if (e->time > prevts && e->time <= newestts) {
314 					sx = time2x(e->time);
315 					line(screen, addpt(p, Pt(sx, topmargin)),
316 						addpt(p, Pt(sx, Height - bottommargin)),
317 						Endarrow, Endsquare, 1, green, ZP);
318 				}
319 				break;
320 			case SExpel:
321 				if (e->time > prevts && e->time <= newestts) {
322 					sx = time2x(e->time);
323 					line(screen, addpt(p, Pt(sx, topmargin)),
324 						addpt(p, Pt(sx, Height - bottommargin)),
325 						Endsquare, Endarrow, 1, red, ZP);
326 				}
327 				break;
328 			case SRelease:
329 				if (e->time > prevts && e->time <= newestts) {
330 					sx = time2x(e->time);
331 					line(screen, addpt(p, Pt(sx, topmargin)),
332 						addpt(p, Pt(sx, Height - bottommargin)),
333 						Endarrow, Endsquare, 1, fg, ZP);
334 				}
335 				break;
336 			case SDeadline:
337 				if (e->time > prevts && e->time <= newestts) {
338 					sx = time2x(e->time);
339 					line(screen, addpt(p, Pt(sx, topmargin)),
340 						addpt(p, Pt(sx, Height - bottommargin)),
341 						Endsquare, Endarrow, 1, fg, ZP);
342 				}
343 				break;
344 
345 			case SYield:
346 			case SUser:
347 				if (e->time > prevts && e->time <= newestts) {
348 					sx = time2x(e->time);
349 					line(screen, addpt(p, Pt(sx, topmargin)),
350 						addpt(p, Pt(sx, Height - bottommargin)),
351 						Endsquare, Endarrow, 0,
352 						(e->etype == SYield)? green: blue, ZP);
353 				}
354 				break;
355 			case SSlice:
356 				if (e->time > prevts && e->time <= newestts) {
357 					sx = time2x(e->time);
358 					line(screen, addpt(p, Pt(sx, topmargin)),
359 						addpt(p, Pt(sx, Height - bottommargin)),
360 						Endsquare, Endarrow, 0, red, ZP);
361 				}
362 				break;
363 
364 			case SRun:
365 			case SEdf:
366 				sx = time2x(e->time);
367 				ex = time2x(e->etime);
368 				if(ex == sx)
369 					ex++;
370 
371 				r = Rect(sx, topmargin + 8, ex, Height - lineht);
372 				r = rectaddpt(r, p);
373 
374 				draw(screen, r, cols[n % Ncolor][e->etype==SRun?1:3], nil, ZP);
375 
376 				if(t->pid == triggerproc && ex < Width)
377 					paused ^= 1;
378 
379 				for(j = 0; j < t->nevents; j++){
380 					_e = &t->events[j];
381 					switch(_e->etype & 0xffff){
382 					case SInts:
383 						if (_e->time > prevts && _e->time <= newestts){
384 							sx = time2x(_e->time);
385 							line(screen, addpt(p, Pt(sx, topmargin)),
386 												addpt(p, Pt(sx, Height / 2 - bottommargin)),
387 												Endsquare, Endsquare, 0,
388 												green, ZP);
389 						}
390 						break;
391 					case SInte:
392 						if (_e->time > prevts && _e->time <= newestts) {
393 							sx = time2x(_e->time);
394 							line(screen, addpt(p, Pt(sx, Height / 2 - bottommargin)),
395 												addpt(p, Pt(sx, Height - bottommargin)),
396 												Endsquare, Endsquare, 0,
397 												blue, ZP);
398 						}
399 						break;
400 					}
401 				}
402 				break;
403 			}
404 		}
405 		p.y += Height;
406 	}
407 
408 	ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics);
409 	x = time2x(ts);
410 
411 	while(x < Width){
412 		p = screen->r.min;
413 		for(n = 0; n < ntasks; n++){
414 			int height, width;
415 
416 			/* p is upper left corner for this task */
417 			if ((ts % scales[scaleno].scale) == 0){
418 				height = 10 * Height;
419 				width = 1;
420 			}else if ((ts % scales[scaleno].bigtics) == 0){
421 				height = 12 * Height;
422 				width = 0;
423 			}else{
424 				height = 13 * Height;
425 				width = 0;
426 			}
427 			height >>= 4;
428 
429 			line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)),
430 				Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP);
431 
432 			p.y += Height;
433 		}
434 		ts += scales[scaleno].littletics;
435 		x = time2x(ts);
436 	}
437 
438 	rtime = screen->r;
439 	rtime.min.y = rtime.max.y - tinyfont->height + 2;
440 	draw(screen, rtime, bg, nil, ZP);
441 	ts = oldestts + scales[scaleno].bigtics - (oldestts % scales[scaleno].bigtics);
442 	x = time2x(ts);
443 	ss = 0;
444 	while(x < Width){
445 		snprint(buf, sizeof(buf), "%t", ss);
446 		string(screen, addpt(p, Pt(x - stringwidth(tinyfont, buf)/2, - tinyfont->height - 1)),
447 			fg, ZP, tinyfont, buf);
448 		ts += scales[scaleno].bigtics;
449 		ss += scales[scaleno].bigtics;
450 		x = time2x(ts);
451 	}
452 
453 	snprint(buf, sizeof(buf), "%t", now);
454 	string(screen, Pt(screen->r.max.x - stringwidth(mediumfont, buf), screen->r.min.y),
455 		fg, ZP, mediumfont, buf);
456 
457 	flushimage(display, 1);
458 	prevts = newestts;
459 }
460 
461 Task*
462 newtask(ulong pid)
463 {
464 	Task *t;
465 	char buf[64], *p;
466 	int fd,n;
467 
468 	tasks = realloc(tasks, (ntasks + 1) * sizeof(Task));
469 	assert(tasks);
470 
471 	t = &tasks[ntasks++];
472 	memset(t, 0, sizeof(Task));
473 	t->events = nil;
474 	snprint(buf, sizeof buf, "/proc/%ld/status", pid);
475 	t->name = nil;
476 	fd = open(buf, OREAD);
477 	if (fd >= 0){
478 		n = read(fd, buf, sizeof buf);
479 		if(n > 0){
480 			p = buf + sizeof buf - 1;
481 			*p = 0;
482 			p = strchr(buf, ' ');
483 			if (p) *p = 0;
484 			t->name = strdup(buf);
485 		}else
486 			print("%s: %r\n", buf);
487 		close(fd);
488 	}else
489 		print("%s: %r\n", buf);
490 	t->pid = pid;
491 	prevts = 0;
492 	if (newwin){
493 		fprint(wctlfd, "resize -dx %d -dy %d\n",
494 			Width + 20, (ntasks * Height) + 5);
495 	}else
496 		Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
497 	return t;
498 }
499 
500 void
501 doevent(Task *t, Traceevent *ep)
502 {
503 	int i, n;
504 	Event *event;
505 	vlong runt;
506 
507 	t->tevents[ep->etype & 0xffff]++;
508 	n = t->nevents++;
509 	t->events = realloc(t->events, t->nevents*sizeof(Event));
510 	assert(t->events);
511 	event = &t->events[n];
512 	memmove(event, ep, sizeof(Traceevent));
513 	event->etime = 0;
514 
515 	switch(event->etype & 0xffff){
516 	case SRelease:
517 		if (t->runthis > t->runmax)
518 			t->runmax = t->runthis;
519 		t->runthis = 0;
520 		break;
521 
522 	case SSleep:
523 	case SYield:
524 	case SReady:
525 	case SSlice:
526 		for(i = n-1; i >= 0; i--)
527 			if (t->events[i].etype == SRun ||
528 				t->events[i].etype == SEdf)
529 				break;
530 		if(i < 0 || t->events[i].etime != 0)
531 			break;
532 		runt = event->time - t->events[i].time;
533 		if(runt > 0){
534 			t->events[i].etime = event->time;
535 			t->runtime += runt;
536 			t->total += runt;
537 			t->runthis += runt;
538 			t->runs++;
539 		}
540 		break;
541 	case SDead:
542 print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]);
543 		free(t->events);
544 		free(t->name);
545 		ntasks--;
546 		memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t));
547 		if (newwin)
548 			fprint(wctlfd, "resize -dx %d -dy %d\n",
549 				Width + 20, (ntasks * Height) + 5);
550 		else
551 			Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
552 		prevts = 0;
553 	}
554 }
555 
556 void
557 drawtrace(void)
558 {
559 	char *wsys, line[256];
560 	int wfd, logfd;
561 	Mousectl *mousectl;
562 	Keyboardctl *keyboardctl;
563 	int scaleno;
564 	Rune r;
565 	int i, n;
566 	Task *t;
567 	Traceevent *ep;
568 
569 	eventbuf = malloc(Nevents*sizeof(Traceevent));
570 	assert(eventbuf);
571 
572 	if((logfd = open(profdev, OREAD)) < 0)
573 		sysfatal("%s: Cannot open %s: %r\n", argv0, profdev);
574 
575 	if(newwin){
576 		if((wsys = getenv("wsys")) == nil)
577 			sysfatal("%s: Cannot find windowing system: %r\n",
578 						argv0);
579 
580 		if((wfd = open(wsys, ORDWR)) < 0)
581 			sysfatal("%s: Cannot open windowing system: %r\n",
582 						argv0);
583 
584 		snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d",
585 				getpid(), Width + 20, Height + 5);
586 		line[sizeof(line) - 1] = '\0';
587 		rfork(RFNAMEG);
588 
589 		if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0)
590 			sysfatal("%s: Cannot mount %s under /mnt/wsys: %r\n",
591 						argv0, line);
592 
593 		if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
594 			sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r\n",
595 						argv0);
596 
597 	}
598 	if((wctlfd = open("/dev/wctl", OWRITE)) < 0)
599 		sysfatal("%s: Cannot open /dev/wctl: %r\n", argv0);
600 	if(initdraw(nil, nil, "trace") < 0)
601 		sysfatal("%s: initdraw failure: %r\n", argv0);
602 
603 	Width = Dx(screen->r);
604 	Height = Dy(screen->r);
605 
606 	if((mousectl = initmouse(nil, screen)) == nil)
607 		sysfatal("%s: cannot initialize mouse: %r\n", argv0);
608 
609 	if((keyboardctl = initkeyboard(nil)) == nil)
610 		sysfatal("%s: cannot initialize keyboard: %r\n", argv0);
611 
612 	colinit();
613 
614 	paused = 0;
615 	scaleno = 7;	/* 100 milliseconds */
616 	now = nsec();
617 	while(1) {
618 		Alt a[] = {
619 			{ mousectl->c,			nil,		CHANRCV		},
620 			{ mousectl->resizec,	nil,		CHANRCV		},
621 			{ keyboardctl->c,		&r,			CHANRCV		},
622 			{ nil,					nil,		CHANNOBLK	},
623 		};
624 
625 		switch (alt(a)) {
626 		case 0:
627 			continue;
628 
629 		case 1:
630 			if(getwindow(display, Refnone) < 0)
631 				sysfatal("drawrt: Cannot re-attach window\n");
632 			if(newwin){
633 				if(Dx(screen->r) != Width ||
634 					Dy(screen->r) != (ntasks * Height)){
635 					fprint(2, "resize: x: have %d, need %d; y: have %d, need %d\n",
636 							Dx(screen->r), Width + 8, Dy(screen->r), (ntasks * Height) + 8);
637 					fprint(wctlfd, "resize -dx %d -dy %d\n",
638 							Width + 8, (ntasks * Height) + 8);
639 				}
640 			}
641 			else{
642 				Width = Dx(screen->r);
643 				Height = ntasks? Dy(screen->r)/ntasks:
644 							Dy(screen->r);
645 			}
646 			break;
647 
648 		case 2:
649 
650 			switch(r){
651 			case 'r':
652 				for(i = 0; i < ntasks; i++){
653 					tasks[i].tstart = now;
654 					tasks[i].total = 0;
655 					tasks[i].runtime = 0;
656 					tasks[i].runmax = 0;
657 					tasks[i].runthis = 0;
658 					tasks[i].runs = 0;
659 					memset(tasks[i].tevents, 0, Nevent*sizeof(ulong));
660 
661 				}
662 				break;
663 
664 			case 'p':
665 				paused ^= 1;
666 				prevts = 0;
667 				break;
668 
669 			case '-':
670 				if (scaleno < nelem(scales) - 1)
671 					scaleno++;
672 				prevts = 0;
673 				break;
674 
675 			case '+':
676 				if (scaleno > 0)
677 					scaleno--;
678 				prevts = 0;
679 				break;
680 
681 			case 'q':
682 				threadexitsall(nil);
683 
684 			case 'v':
685 				verbose ^= 1;
686 
687 			default:
688 				break;
689 			}
690 			break;
691 
692 		case 3:
693 			now = nsec();
694 			while((n = read(logfd, eventbuf, Nevents*sizeof(Traceevent))) > 0){
695 				assert((n % sizeof(Traceevent)) == 0);
696 				nevents = n / sizeof(Traceevent);
697 				for (ep = eventbuf; ep < eventbuf + nevents; ep++){
698 					if ((ep->etype & 0xffff) >= Nevent){
699 						print("%ld %t Illegal event %ld\n",
700 							ep->pid, ep->time, ep->etype & 0xffff);
701 						continue;
702 					}
703 					if (verbose)
704 						print("%ld %t %s\n",
705 							ep->pid, ep->time, schedstatename[ep->etype & 0xffff]);
706 
707 					for(i = 0; i < ntasks; i++)
708 						if(tasks[i].pid == ep->pid)
709 							break;
710 
711 					if(i == ntasks){
712 						t = newtask(ep->pid);
713 						t->tstart = ep->time;
714 					}else
715 						t = &tasks[i];
716 
717 					doevent(t, ep);
718 				}
719 			}
720 			if(!paused)
721 				redraw(scaleno);
722 		}
723 		sleep(scales[scaleno].sleep);
724 	}
725 }
726 
727 int
728 timeconv(Fmt *f)
729 {
730 	char buf[128], *sign;
731 	vlong t;
732 
733 	buf[0] = 0;
734 	switch(f->r) {
735 	case 'U':
736 		t = va_arg(f->args, vlong);
737 		break;
738 	case 't':		// vlong in nanoseconds
739 		t = va_arg(f->args, vlong);
740 		break;
741 	default:
742 		return fmtstrcpy(f, "(timeconv)");
743 	}
744 	if (t < 0) {
745 		sign = "-";
746 		t = -t;
747 	}else
748 		sign = "";
749 	if (t > S(1)){
750 		t += OneRound;
751 		sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000);
752 	}else if (t > MS(1)){
753 		t += MilliRound;
754 		sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000);
755 	}else if (t > US(1))
756 		sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1)));
757 	else
758 		sprint(buf, "%s%dns", sign, (int)t);
759 	return fmtstrcpy(f, buf);
760 }
761