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