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] [-b] [-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") < 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; 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, 287 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)), 294 Pt(screen->r.max.x, p.y + Height - lineht), 295 Endsquare, Endsquare, 0, 296 cols[n % Ncolor][1], ZP); 297 298 for (i = 0; i < t->nevents-1; i++) 299 if (prevts < t->events[i + 1].time) 300 break; 301 302 if (i > 0) { 303 memmove(t->events, t->events + i, 304 (t->nevents - i) * sizeof(Event)); 305 t->nevents -= i; 306 } 307 308 for (i = 0; i != t->nevents; i++) { 309 Event *e = &t->events[i], *_e; 310 int sx, ex; 311 312 switch (e->etype & 0xffff) { 313 case SAdmit: 314 if (e->time > prevts && e->time <= newestts) { 315 sx = time2x(e->time); 316 line(screen, addpt(p, Pt(sx, topmargin)), 317 addpt(p, Pt(sx, Height - bottommargin)), 318 Endarrow, Endsquare, 1, green, ZP); 319 } 320 break; 321 case SExpel: 322 if (e->time > prevts && e->time <= newestts) { 323 sx = time2x(e->time); 324 line(screen, addpt(p, Pt(sx, topmargin)), 325 addpt(p, Pt(sx, Height - bottommargin)), 326 Endsquare, Endarrow, 1, red, ZP); 327 } 328 break; 329 case SRelease: 330 if (e->time > prevts && e->time <= newestts) { 331 sx = time2x(e->time); 332 line(screen, addpt(p, Pt(sx, topmargin)), 333 addpt(p, Pt(sx, Height - bottommargin)), 334 Endarrow, Endsquare, 1, fg, ZP); 335 } 336 break; 337 case SDeadline: 338 if (e->time > prevts && e->time <= newestts) { 339 sx = time2x(e->time); 340 line(screen, addpt(p, Pt(sx, topmargin)), 341 addpt(p, Pt(sx, Height - bottommargin)), 342 Endsquare, Endarrow, 1, fg, ZP); 343 } 344 break; 345 346 case SYield: 347 case SUser: 348 if (e->time > prevts && e->time <= newestts) { 349 sx = time2x(e->time); 350 line(screen, addpt(p, Pt(sx, topmargin)), 351 addpt(p, Pt(sx, Height - bottommargin)), 352 Endsquare, Endarrow, 0, 353 (e->etype == SYield)? green: blue, ZP); 354 } 355 break; 356 case SSlice: 357 if (e->time > prevts && e->time <= newestts) { 358 sx = time2x(e->time); 359 line(screen, addpt(p, Pt(sx, topmargin)), 360 addpt(p, Pt(sx, Height - bottommargin)), 361 Endsquare, Endarrow, 0, red, ZP); 362 } 363 break; 364 365 case SRun: 366 case SEdf: 367 sx = time2x(e->time); 368 ex = time2x(e->etime); 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 for(j = 0; j < t->nevents; j++){ 376 _e = &t->events[j]; 377 switch(_e->etype & 0xffff){ 378 case SInts: 379 if (_e->time > prevts && _e->time <= newestts){ 380 sx = time2x(_e->time); 381 line(screen, addpt(p, Pt(sx, topmargin)), 382 addpt(p, Pt(sx, Height / 2 - bottommargin)), 383 Endsquare, Endsquare, 0, 384 green, ZP); 385 } 386 break; 387 case SInte: 388 if (_e->time > prevts && _e->time <= newestts) { 389 sx = time2x(_e->time); 390 line(screen, addpt(p, Pt(sx, Height / 2 - bottommargin)), 391 addpt(p, Pt(sx, Height - bottommargin)), 392 Endsquare, Endsquare, 0, 393 blue, ZP); 394 } 395 break; 396 } 397 } 398 break; 399 } 400 } 401 p.y += Height; 402 } 403 404 ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics); 405 x = time2x(ts); 406 407 while(x < Width){ 408 p = screen->r.min; 409 for(n = 0; n < ntasks; n++){ 410 int height, width; 411 412 /* p is upper left corner for this task */ 413 if ((ts % scales[scaleno].scale) == 0){ 414 height = 10 * Height; 415 width = 1; 416 }else if ((ts % scales[scaleno].bigtics) == 0){ 417 height = 12 * Height; 418 width = 0; 419 }else{ 420 height = 13 * Height; 421 width = 0; 422 } 423 height >>= 4; 424 425 line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)), 426 Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP); 427 428 p.y += Height; 429 } 430 ts += scales[scaleno].littletics; 431 x = time2x(ts); 432 } 433 434 rtime = screen->r; 435 rtime.min.y = rtime.max.y - tinyfont->height + 2; 436 draw(screen, rtime, bg, nil, ZP); 437 ts = oldestts + scales[scaleno].bigtics - (oldestts % scales[scaleno].bigtics); 438 x = time2x(ts); 439 while(x < Width){ 440 snprint(buf, sizeof(buf), "%t", ts); 441 string(screen, addpt(p, Pt(x - stringwidth(tinyfont, buf)/2, - tinyfont->height - 1)), 442 fg, ZP, tinyfont, buf); 443 ts += scales[scaleno].bigtics; 444 x = time2x(ts); 445 } 446 447 snprint(buf, sizeof(buf), "%t", now); 448 string(screen, Pt(screen->r.max.x - stringwidth(mediumfont, buf), screen->r.min.y), 449 fg, ZP, mediumfont, buf); 450 451 flushimage(display, 1); 452 prevts = newestts; 453 } 454 455 Task* 456 newtask(ulong pid) 457 { 458 Task *t; 459 char buf[64], *p; 460 int fd,n; 461 462 tasks = realloc(tasks, (ntasks + 1) * sizeof(Task)); 463 assert(tasks); 464 465 t = &tasks[ntasks++]; 466 memset(t, 0, sizeof(Task)); 467 t->events = nil; 468 snprint(buf, sizeof buf, "/proc/%ld/status", pid); 469 t->name = nil; 470 fd = open(buf, OREAD); 471 if (fd >= 0){ 472 n = read(fd, buf, sizeof buf); 473 if(n > 0){ 474 p = buf + sizeof buf - 1; 475 *p = 0; 476 p = strchr(buf, ' '); 477 if (p) *p = 0; 478 t->name = strdup(buf); 479 }else 480 print("%s: %r\n", buf); 481 close(fd); 482 }else 483 print("%s: %r\n", buf); 484 t->pid = pid; 485 prevts = 0; 486 if (newwin){ 487 fprint(wctlfd, "resize -dx %d -dy %d\n", 488 Width + 20, (ntasks * Height) + 5); 489 }else 490 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r); 491 return t; 492 } 493 494 void 495 doevent(Task *t, Traceevent *ep) 496 { 497 int i, n; 498 Event *event; 499 vlong runt; 500 501 t->tevents[ep->etype & 0xffff]++; 502 n = t->nevents++; 503 t->events = realloc(t->events, t->nevents*sizeof(Event)); 504 assert(t->events); 505 event = &t->events[n]; 506 memmove(event, ep, sizeof(Traceevent)); 507 event->etime = 0; 508 509 switch(event->etype & 0xffff){ 510 case SRelease: 511 if (t->runthis > t->runmax) 512 t->runmax = t->runthis; 513 t->runthis = 0; 514 break; 515 516 case SSleep: 517 case SYield: 518 case SReady: 519 case SSlice: 520 for(i = n-1; i >= 0; i--) 521 if (t->events[i].etype == SRun || 522 t->events[i].etype == SEdf) 523 break; 524 if(i < 0 || t->events[i].etime != 0) 525 break; 526 runt = event->time - t->events[i].time; 527 if(runt > 0){ 528 t->events[i].etime = event->time; 529 t->runtime += runt; 530 t->total += runt; 531 t->runthis += runt; 532 t->runs++; 533 } 534 break; 535 case SDead: 536 print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]); 537 free(t->events); 538 free(t->name); 539 ntasks--; 540 memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t)); 541 if (newwin) 542 fprint(wctlfd, "resize -dx %d -dy %d\n", 543 Width + 20, (ntasks * Height) + 5); 544 else 545 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r); 546 prevts = 0; 547 } 548 } 549 550 void 551 drawtrace(void) 552 { 553 char *wsys, line[256]; 554 int wfd, logfd; 555 Mousectl *mousectl; 556 Keyboardctl *keyboardctl; 557 int paused; 558 int scaleno; 559 Rune r; 560 int i, n; 561 Task *t; 562 Traceevent *ep; 563 564 eventbuf = malloc(Nevents*sizeof(Traceevent)); 565 assert(eventbuf); 566 567 if((logfd = open(profdev, OREAD)) < 0) 568 sysfatal("%s: Cannot open %s: %r\n", argv0, profdev); 569 570 if(newwin){ 571 if((wsys = getenv("wsys")) == nil) 572 sysfatal("%s: Cannot find windowing system: %r\n", 573 argv0); 574 575 if((wfd = open(wsys, ORDWR)) < 0) 576 sysfatal("%s: Cannot open windowing system: %r\n", 577 argv0); 578 579 snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d", 580 getpid(), Width + 20, Height + 5); 581 line[sizeof(line) - 1] = '\0'; 582 rfork(RFNAMEG); 583 584 if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0) 585 sysfatal("%s: Cannot mount %s under /mnt/wsys: %r\n", 586 argv0, line); 587 588 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0) 589 sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r\n", 590 argv0); 591 592 } 593 if((wctlfd = open("/dev/wctl", OWRITE)) < 0) 594 sysfatal("%s: Cannot open /dev/wctl: %r\n", argv0); 595 if(initdraw(nil, nil, "trace") < 0) 596 sysfatal("%s: initdraw failure: %r\n", argv0); 597 598 Width = Dx(screen->r); 599 Height = Dy(screen->r); 600 601 if((mousectl = initmouse(nil, screen)) == nil) 602 sysfatal("%s: cannot initialize mouse: %r\n", argv0); 603 604 if((keyboardctl = initkeyboard(nil)) == nil) 605 sysfatal("%s: cannot initialize keyboard: %r\n", argv0); 606 607 colinit(); 608 609 paused = 0; 610 scaleno = 7; /* 100 milliseconds */ 611 now = nsec(); 612 while(1) { 613 Alt a[] = { 614 { mousectl->c, nil, CHANRCV }, 615 { mousectl->resizec, nil, CHANRCV }, 616 { keyboardctl->c, &r, CHANRCV }, 617 { nil, nil, CHANNOBLK }, 618 }; 619 620 switch (alt(a)) { 621 case 0: 622 continue; 623 624 case 1: 625 if(getwindow(display, Refnone) < 0) 626 sysfatal("drawrt: Cannot re-attach window\n"); 627 if(newwin){ 628 if(Dx(screen->r) != Width || 629 Dy(screen->r) != (ntasks * Height)){ 630 fprint(2, "resize: x: have %d, need %d; y: have %d, need %d\n", 631 Dx(screen->r), Width + 8, Dy(screen->r), (ntasks * Height) + 8); 632 fprint(wctlfd, "resize -dx %d -dy %d\n", 633 Width + 8, (ntasks * Height) + 8); 634 } 635 } 636 else{ 637 Width = Dx(screen->r); 638 Height = ntasks? Dy(screen->r)/ntasks: 639 Dy(screen->r); 640 } 641 break; 642 643 case 2: 644 645 switch(r){ 646 case 'r': 647 for(i = 0; i < ntasks; i++){ 648 tasks[i].tstart = now; 649 tasks[i].total = 0; 650 tasks[i].runtime = 0; 651 tasks[i].runmax = 0; 652 tasks[i].runthis = 0; 653 tasks[i].runs = 0; 654 memset(tasks[i].tevents, 0, Nevent*sizeof(ulong)); 655 656 } 657 break; 658 659 case 'p': 660 paused = (paused + 1) & 1; 661 prevts = 0; 662 break; 663 664 case '-': 665 if (scaleno < nelem(scales) - 1) 666 scaleno++; 667 prevts = 0; 668 break; 669 670 case '+': 671 if (scaleno > 0) 672 scaleno--; 673 prevts = 0; 674 break; 675 676 case 'q': 677 threadexitsall(nil); 678 679 case 'v': 680 verbose ^= 1; 681 682 default: 683 break; 684 } 685 break; 686 687 case 3: 688 now = nsec(); 689 while((n = read(logfd, eventbuf, Nevents*sizeof(Traceevent))) > 0){ 690 assert((n % sizeof(Traceevent)) == 0); 691 nevents = n / sizeof(Traceevent); 692 for (ep = eventbuf; ep < eventbuf + nevents; ep++){ 693 if ((ep->etype & 0xffff) >= Nevent){ 694 print("%ld %t Illegal event %ld\n", 695 ep->pid, ep->time, ep->etype & 0xffff); 696 continue; 697 } 698 if (verbose) 699 print("%ld %t %s\n", 700 ep->pid, ep->time, schedstatename[ep->etype & 0xffff]); 701 702 for(i = 0; i < ntasks; i++) 703 if(tasks[i].pid == ep->pid) 704 break; 705 706 if(i == ntasks){ 707 t = newtask(ep->pid); 708 t->tstart = ep->time; 709 }else 710 t = &tasks[i]; 711 712 doevent(t, ep); 713 } 714 } 715 if(!paused) 716 redraw(scaleno); 717 } 718 sleep(scales[scaleno].sleep); 719 } 720 } 721 722 int 723 timeconv(Fmt *f) 724 { 725 char buf[128], *sign; 726 vlong t; 727 728 buf[0] = 0; 729 switch(f->r) { 730 case 'U': 731 t = va_arg(f->args, vlong); 732 break; 733 case 't': // vlong in nanoseconds 734 t = va_arg(f->args, vlong); 735 break; 736 default: 737 return fmtstrcpy(f, "(timeconv)"); 738 } 739 if (t < 0) { 740 sign = "-"; 741 t = -t; 742 }else 743 sign = ""; 744 if (t > S(1)){ 745 t += OneRound; 746 sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000); 747 }else if (t > MS(1)){ 748 t += MilliRound; 749 sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000); 750 }else if (t > US(1)) 751 sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1))); 752 else 753 sprint(buf, "%s%dns", sign, (int)t); 754 return fmtstrcpy(f, buf); 755 } 756