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