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