Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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*
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
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
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
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
}