]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/trace.c
Import sources from 2011-03-30 iso image - lib
[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 <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 }