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\n", argv0, profdev); 573 574 if(newwin){ 575 if((wsys = getenv("wsys")) == nil) 576 sysfatal("%s: Cannot find windowing system: %r\n", 577 argv0); 578 579 if((wfd = open(wsys, ORDWR)) < 0) 580 sysfatal("%s: Cannot open windowing system: %r\n", 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\n", 590 argv0, line); 591 592 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0) 593 sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r\n", 594 argv0); 595 596 } 597 if((wctlfd = open("/dev/wctl", OWRITE)) < 0) 598 sysfatal("%s: Cannot open /dev/wctl: %r\n", argv0); 599 if(initdraw(nil, nil, "trace") < 0) 600 sysfatal("%s: initdraw failure: %r\n", 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\n", argv0); 607 608 if((keyboardctl = initkeyboard(nil)) == nil) 609 sysfatal("%s: cannot initialize keyboard: %r\n", 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\n"); 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 } 760