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