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