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