12 #pragma varargck type "t" vlong
13 #pragma varargck type "U" uvlong
15 #define NS(x) ((vlong)x)
16 #define US(x) (NS(x) * 1000ULL)
17 #define MS(x) (US(x) * 1000ULL)
18 #define S(x) (MS(x) * 1000ULL)
20 #define numblocks(a, b) (((a) + (b) - 1) / (b))
21 #define roundup(a, b) (numblocks((a), (b)) * (b))
25 MilliRound = US(1)/2LL,
28 typedef struct Event Event;
29 typedef struct Task Task;
32 vlong etime; /* length of block to draw */
46 ulong tevents[Nevent];
59 int Height = 100; // Per task
69 int schedparse(char*, char*, char*);
72 char *schedstatename[] = {
76 [SDeadline] = "Deadline",
80 [SRelease] = "Release",
95 { US(500), US(100), US(50), 0},
96 { US(1000), US(500), US(100), 0},
97 { US(2000), US(1000), US(200), 0},
98 { US(5000), US(1000), US(500), 0},
99 { MS(10), MS(5), MS(1), 20},
100 { MS(20), MS(10), MS(2), 20},
101 { MS(50), MS(10), MS(5), 20},
102 { MS(100), MS(50), MS(10), 20}, /* starting scaleno */
103 { MS(200), MS(100), MS(20), 20},
104 { MS(500), MS(100), MS(50), 50},
105 { MS(1000), MS(500), MS(100), 100},
106 { MS(2000), MS(1000), MS(200), 100},
107 { MS(5000), MS(1000), MS(500), 100},
108 { S(10), S(50), S(1), 100},
109 { S(20), S(10), S(2), 100},
110 { S(50), S(10), S(5), 100},
111 { S(100), S(50), S(10), 100},
112 { S(200), S(100), S(20), 100},
113 { S(500), S(100), S(50), 100},
114 { S(1000), S(500), S(100), 100},
117 int ntasks, verbose, triggerproc, paused;
119 Image *cols[Ncolor][4];
120 Font *mediumfont, *tinyfont;
121 Image *grey, *red, *green, *blue, *bg, *fg;
122 char*profdev = "/proc/trace";
127 fprint(2, "Usage: %s [-d profdev] [-w] [-v] [-t triggerproc] [processes]\n", argv0);
132 threadmain(int argc, char **argv)
137 fmtinstall('t', timeconv);
140 profdev = EARGF(usage());
149 triggerproc = (int)strtol(EARGF(usage()), nil, 0);
156 fname[sizeof fname - 1] = 0;
157 for(i = 0; i < argc; i++){
158 snprint(fname, sizeof fname - 2, "/proc/%s/ctl",
160 if((fd = open(fname, OWRITE)) < 0){
161 fprint(2, "%s: cannot open %s: %r\n",
166 if(fprint(fd, "trace 1") < 0)
167 fprint(2, "%s: cannot enable tracing on %s: %r\n",
176 mkcol(int i, int c0, int c1, int c2)
178 cols[i][0] = allocimagemix(display, c0, DWhite);
179 cols[i][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c1);
180 cols[i][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c2);
181 cols[i][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c0);
187 mediumfont = openfont(display, "/lib/font/bit/lucidasans/unicode.10.font");
188 if(mediumfont == nil)
190 tinyfont = openfont(display, "/lib/font/bit/lucidasans/unicode.7.font");
193 topmargin = mediumfont->height+2;
194 bottommargin = tinyfont->height+2;
197 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
199 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
201 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
203 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
205 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
207 cols[5][0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEEEEEEFF);
208 cols[5][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
209 cols[5][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x888888FF);
210 cols[5][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF);
212 red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFF0000FF);
213 green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x00FF00FF);
214 blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF);
227 vlong ts, oldestts, newestts, period, ppp, scale, s, ss;
229 # define time2x(t) ((int)(((t) - oldestts) / ppp))
231 scale = scales[scaleno].scale;
232 period = scale + scales[scaleno].littletics;
233 ppp = period / Width; // period per pixel.
235 /* Round `now' to a nice number */
236 newestts = now - (now % scales[scaleno].bigtics) +
237 (scales[scaleno].littletics>>1);
239 oldestts = newestts - period;
241 //print("newestts %t, period %t, %d-%d\n", newestts, period, time2x(oldestts), time2x(newestts));
242 if (prevts < oldestts){
243 oldestts = newestts - period;
246 draw(screen, screen->r, bg, nil, ZP);
248 /* just white out time */
250 rtime.min.x = rtime.max.x - stringwidth(mediumfont, "00000000000.000s");
251 rtime.max.y = rtime.min.y + mediumfont->height;
252 draw(screen, rtime, bg, nil, ZP);
255 for (n = 0; n != ntasks; n++) {
257 /* p is upper left corner for this task */
258 rtime = Rpt(p, addpt(p, Pt(500, mediumfont->height)));
259 draw(screen, rtime, bg, nil, ZP);
260 snprint(buf, sizeof(buf), "%d %s", t->pid, t->name);
261 q = string(screen, p, fg, ZP, mediumfont, buf);
263 if(t->tevents[SRelease])
264 snprint(buf, sizeof(buf), " per %t — avg: %t max: %t",
265 (vlong)(s/t->tevents[SRelease]),
266 (vlong)(t->runtime/t->tevents[SRelease]),
268 else if((s /=1000000000LL) != 0)
269 snprint(buf, sizeof(buf), " per 1s — avg: %t total: %t",
273 snprint(buf, sizeof(buf), " total: %t", t->total);
274 string(screen, q, fg, ZP, tinyfont, buf);
280 for (n = 0; n != ntasks; n++) {
283 /* p is upper left corner for this task */
285 /* Move part already drawn */
286 r = Rect(p.x, p.y + topmargin, p.x + x, p.y+Height);
287 draw(screen, r, screen, nil, Pt(p.x + Width - x, p.y + topmargin));
289 r.max.x = screen->r.max.x;
291 draw(screen, r, bg, nil, ZP);
293 line(screen, addpt(p, Pt(x, Height - lineht)), Pt(screen->r.max.x, p.y + Height - lineht),
294 Endsquare, Endsquare, 0, cols[n % Ncolor][1], ZP);
296 for (i = 0; i < t->nevents-1; i++)
297 if (prevts < t->events[i + 1].time)
301 memmove(t->events, t->events + i, (t->nevents - i) * sizeof(Event));
305 for (i = 0; i != t->nevents; i++) {
306 Event *e = &t->events[i], *_e;
309 switch (e->etype & 0xffff) {
311 if (e->time > prevts && e->time <= newestts) {
312 sx = time2x(e->time);
313 line(screen, addpt(p, Pt(sx, topmargin)),
314 addpt(p, Pt(sx, Height - bottommargin)),
315 Endarrow, Endsquare, 1, green, ZP);
319 if (e->time > prevts && e->time <= newestts) {
320 sx = time2x(e->time);
321 line(screen, addpt(p, Pt(sx, topmargin)),
322 addpt(p, Pt(sx, Height - bottommargin)),
323 Endsquare, Endarrow, 1, red, ZP);
327 if (e->time > prevts && e->time <= newestts) {
328 sx = time2x(e->time);
329 line(screen, addpt(p, Pt(sx, topmargin)),
330 addpt(p, Pt(sx, Height - bottommargin)),
331 Endarrow, Endsquare, 1, fg, ZP);
335 if (e->time > prevts && e->time <= newestts) {
336 sx = time2x(e->time);
337 line(screen, addpt(p, Pt(sx, topmargin)),
338 addpt(p, Pt(sx, Height - bottommargin)),
339 Endsquare, Endarrow, 1, fg, ZP);
345 if (e->time > prevts && e->time <= newestts) {
346 sx = time2x(e->time);
347 line(screen, addpt(p, Pt(sx, topmargin)),
348 addpt(p, Pt(sx, Height - bottommargin)),
349 Endsquare, Endarrow, 0,
350 (e->etype == SYield)? green: blue, ZP);
354 if (e->time > prevts && e->time <= newestts) {
355 sx = time2x(e->time);
356 line(screen, addpt(p, Pt(sx, topmargin)),
357 addpt(p, Pt(sx, Height - bottommargin)),
358 Endsquare, Endarrow, 0, red, ZP);
364 sx = time2x(e->time);
365 ex = time2x(e->etime);
369 r = Rect(sx, topmargin + 8, ex, Height - lineht);
372 draw(screen, r, cols[n % Ncolor][e->etype==SRun?1:3], nil, ZP);
374 if(t->pid == triggerproc && ex < Width)
377 for(j = 0; j < t->nevents; j++){
379 switch(_e->etype & 0xffff){
381 if (_e->time > prevts && _e->time <= newestts){
382 sx = time2x(_e->time);
383 line(screen, addpt(p, Pt(sx, topmargin)),
384 addpt(p, Pt(sx, Height / 2 - bottommargin)),
385 Endsquare, Endsquare, 0,
390 if (_e->time > prevts && _e->time <= newestts) {
391 sx = time2x(_e->time);
392 line(screen, addpt(p, Pt(sx, Height / 2 - bottommargin)),
393 addpt(p, Pt(sx, Height - bottommargin)),
394 Endsquare, Endsquare, 0,
406 ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics);
411 for(n = 0; n < ntasks; n++){
414 /* p is upper left corner for this task */
415 if ((ts % scales[scaleno].scale) == 0){
416 height = 10 * Height;
418 }else if ((ts % scales[scaleno].bigtics) == 0){
419 height = 12 * Height;
422 height = 13 * Height;
427 line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)),
428 Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP);
432 ts += scales[scaleno].littletics;
437 rtime.min.y = rtime.max.y - tinyfont->height + 2;
438 draw(screen, rtime, bg, nil, ZP);
439 ts = oldestts + scales[scaleno].bigtics - (oldestts % scales[scaleno].bigtics);
443 snprint(buf, sizeof(buf), "%t", ss);
444 string(screen, addpt(p, Pt(x - stringwidth(tinyfont, buf)/2, - tinyfont->height - 1)),
445 fg, ZP, tinyfont, buf);
446 ts += scales[scaleno].bigtics;
447 ss += scales[scaleno].bigtics;
451 snprint(buf, sizeof(buf), "%t", now);
452 string(screen, Pt(screen->r.max.x - stringwidth(mediumfont, buf), screen->r.min.y),
453 fg, ZP, mediumfont, buf);
455 flushimage(display, 1);
466 tasks = realloc(tasks, (ntasks + 1) * sizeof(Task));
469 t = &tasks[ntasks++];
470 memset(t, 0, sizeof(Task));
472 snprint(buf, sizeof buf, "/proc/%ld/status", pid);
474 fd = open(buf, OREAD);
476 n = read(fd, buf, sizeof buf);
478 p = buf + sizeof buf - 1;
480 p = strchr(buf, ' ');
482 t->name = strdup(buf);
484 print("%s: %r\n", buf);
487 print("%s: %r\n", buf);
491 fprint(wctlfd, "resize -dx %d -dy %d\n",
492 Width + 20, (ntasks * Height) + 5);
494 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
499 doevent(Task *t, Traceevent *ep)
505 t->tevents[ep->etype & 0xffff]++;
507 t->events = realloc(t->events, t->nevents*sizeof(Event));
509 event = &t->events[n];
510 memmove(event, ep, sizeof(Traceevent));
513 switch(event->etype & 0xffff){
515 if (t->runthis > t->runmax)
516 t->runmax = t->runthis;
524 for(i = n-1; i >= 0; i--)
525 if (t->events[i].etype == SRun ||
526 t->events[i].etype == SEdf)
528 if(i < 0 || t->events[i].etime != 0)
530 runt = event->time - t->events[i].time;
532 t->events[i].etime = event->time;
540 print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]);
544 memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t));
546 fprint(wctlfd, "resize -dx %d -dy %d\n",
547 Width + 20, (ntasks * Height) + 5);
549 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
557 char *wsys, line[256];
560 Keyboardctl *keyboardctl;
567 eventbuf = malloc(Nevents*sizeof(Traceevent));
570 if((logfd = open(profdev, OREAD)) < 0)
571 sysfatal("%s: Cannot open %s: %r", argv0, profdev);
574 if((wsys = getenv("wsys")) == nil)
575 sysfatal("%s: Cannot find windowing system: %r",
578 if((wfd = open(wsys, ORDWR)) < 0)
579 sysfatal("%s: Cannot open windowing system: %r",
582 snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d",
583 getpid(), Width + 20, Height + 5);
584 line[sizeof(line) - 1] = '\0';
587 if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0)
588 sysfatal("%s: Cannot mount %s under /mnt/wsys: %r",
591 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
592 sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r",
596 if((wctlfd = open("/dev/wctl", OWRITE)) < 0)
597 sysfatal("%s: Cannot open /dev/wctl: %r", argv0);
598 if(initdraw(nil, nil, "trace") < 0)
599 sysfatal("%s: initdraw failure: %r", argv0);
601 Width = Dx(screen->r);
602 Height = Dy(screen->r);
604 if((mousectl = initmouse(nil, screen)) == nil)
605 sysfatal("%s: cannot initialize mouse: %r", argv0);
607 if((keyboardctl = initkeyboard(nil)) == nil)
608 sysfatal("%s: cannot initialize keyboard: %r", argv0);
613 scaleno = 7; /* 100 milliseconds */
617 { mousectl->c, nil, CHANRCV },
618 { mousectl->resizec, nil, CHANRCV },
619 { keyboardctl->c, &r, CHANRCV },
620 { nil, nil, CHANNOBLK },
628 if(getwindow(display, Refnone) < 0)
629 sysfatal("drawrt: Cannot re-attach window");
631 if(Dx(screen->r) != Width ||
632 Dy(screen->r) != (ntasks * Height)){
633 fprint(2, "resize: x: have %d, need %d; y: have %d, need %d\n",
634 Dx(screen->r), Width + 8, Dy(screen->r), (ntasks * Height) + 8);
635 fprint(wctlfd, "resize -dx %d -dy %d\n",
636 Width + 8, (ntasks * Height) + 8);
640 Width = Dx(screen->r);
641 Height = ntasks? Dy(screen->r)/ntasks:
650 for(i = 0; i < ntasks; i++){
651 tasks[i].tstart = now;
653 tasks[i].runtime = 0;
655 tasks[i].runthis = 0;
657 memset(tasks[i].tevents, 0, Nevent*sizeof(ulong));
668 if (scaleno < nelem(scales) - 1)
692 while((n = read(logfd, eventbuf, Nevents*sizeof(Traceevent))) > 0){
693 assert((n % sizeof(Traceevent)) == 0);
694 nevents = n / sizeof(Traceevent);
695 for (ep = eventbuf; ep < eventbuf + nevents; ep++){
696 if ((ep->etype & 0xffff) >= Nevent){
697 print("%ld %t Illegal event %ld\n",
698 ep->pid, ep->time, ep->etype & 0xffff);
703 ep->pid, ep->time, schedstatename[ep->etype & 0xffff]);
705 for(i = 0; i < ntasks; i++)
706 if(tasks[i].pid == ep->pid)
710 t = newtask(ep->pid);
711 t->tstart = ep->time;
721 sleep(scales[scaleno].sleep);
728 char buf[128], *sign;
734 t = va_arg(f->args, vlong);
736 case 't': // vlong in nanoseconds
737 t = va_arg(f->args, vlong);
740 return fmtstrcpy(f, "(timeconv)");
749 sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000);
750 }else if (t > MS(1)){
752 sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000);
754 sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1)));
756 sprint(buf, "%s%dns", sign, (int)t);
757 return fmtstrcpy(f, buf);