13 #pragma varargck type "t" vlong
14 #pragma varargck type "U" uvlong
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)
21 #define numblocks(a, b) (((a) + (b) - 1) / (b))
22 #define roundup(a, b) (numblocks((a), (b)) * (b))
26 MilliRound = US(1)/2LL,
29 typedef struct Event Event;
30 typedef struct Task Task;
33 vlong etime; /* length of block to draw */
47 ulong tevents[Nevent];
60 int Height = 100; // Per task
70 int schedparse(char*, char*, char*);
73 char *schedstatename[] = {
77 [SDeadline] = "Deadline",
81 [SRelease] = "Release",
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},
118 int ntasks, verbose, triggerproc, paused;
120 Image *cols[Ncolor][4];
121 Font *mediumfont, *tinyfont;
122 Image *grey, *red, *green, *blue, *bg, *fg;
123 char*profdev = "/proc/trace";
128 fprint(2, "Usage: %s [-d profdev] [-w] [-v] [-t triggerproc] [processes]\n", argv0);
133 threadmain(int argc, char **argv)
138 fmtinstall('t', timeconv);
141 profdev = EARGF(usage());
150 triggerproc = (int)strtol(EARGF(usage()), nil, 0);
157 fname[sizeof fname - 1] = 0;
158 for(i = 0; i < argc; i++){
159 snprint(fname, sizeof fname - 2, "/proc/%s/ctl",
161 if((fd = open(fname, OWRITE)) < 0){
162 fprint(2, "%s: cannot open %s: %r\n",
167 if(fprint(fd, "trace 1") < 0)
168 fprint(2, "%s: cannot enable tracing on %s: %r\n",
177 mkcol(int i, int c0, int c1, int c2)
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);
188 mediumfont = openfont(display, "/lib/font/bit/lucidasans/unicode.10.font");
189 if(mediumfont == nil)
191 tinyfont = openfont(display, "/lib/font/bit/lucidasans/unicode.7.font");
194 topmargin = mediumfont->height+2;
195 bottommargin = tinyfont->height+2;
198 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
200 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
202 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
204 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
206 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
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);
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);
228 vlong ts, oldestts, newestts, period, ppp, scale, s, ss;
230 # define time2x(t) ((int)(((t) - oldestts) / ppp))
232 scale = scales[scaleno].scale;
233 period = scale + scales[scaleno].littletics;
234 ppp = period / Width; // period per pixel.
236 /* Round `now' to a nice number */
237 newestts = now - (now % scales[scaleno].bigtics) +
238 (scales[scaleno].littletics>>1);
240 oldestts = newestts - period;
242 //print("newestts %t, period %t, %d-%d\n", newestts, period, time2x(oldestts), time2x(newestts));
243 if (prevts < oldestts){
244 oldestts = newestts - period;
247 draw(screen, screen->r, bg, nil, ZP);
249 /* just white out time */
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);
256 for (n = 0; n != ntasks; 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);
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]),
269 else if((s /=1000000000LL) != 0)
270 snprint(buf, sizeof(buf), " per 1s — avg: %t total: %t",
274 snprint(buf, sizeof(buf), " total: %t", t->total);
275 string(screen, q, fg, ZP, tinyfont, buf);
281 for (n = 0; n != ntasks; n++) {
284 /* p is upper left corner for this task */
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));
290 r.max.x = screen->r.max.x;
292 draw(screen, r, bg, nil, ZP);
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);
297 for (i = 0; i < t->nevents-1; i++)
298 if (prevts < t->events[i + 1].time)
302 memmove(t->events, t->events + i, (t->nevents - i) * sizeof(Event));
306 for (i = 0; i != t->nevents; i++) {
307 Event *e = &t->events[i], *_e;
310 switch (e->etype & 0xffff) {
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);
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);
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);
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);
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);
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);
365 sx = time2x(e->time);
366 ex = time2x(e->etime);
370 r = Rect(sx, topmargin + 8, ex, Height - lineht);
373 draw(screen, r, cols[n % Ncolor][e->etype==SRun?1:3], nil, ZP);
375 if(t->pid == triggerproc && ex < Width)
378 for(j = 0; j < t->nevents; j++){
380 switch(_e->etype & 0xffff){
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,
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,
407 ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics);
412 for(n = 0; n < ntasks; n++){
415 /* p is upper left corner for this task */
416 if ((ts % scales[scaleno].scale) == 0){
417 height = 10 * Height;
419 }else if ((ts % scales[scaleno].bigtics) == 0){
420 height = 12 * Height;
423 height = 13 * Height;
428 line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)),
429 Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP);
433 ts += scales[scaleno].littletics;
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);
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;
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);
456 flushimage(display, 1);
467 tasks = realloc(tasks, (ntasks + 1) * sizeof(Task));
470 t = &tasks[ntasks++];
471 memset(t, 0, sizeof(Task));
473 snprint(buf, sizeof buf, "/proc/%ld/status", pid);
475 fd = open(buf, OREAD);
477 n = read(fd, buf, sizeof buf);
479 p = buf + sizeof buf - 1;
481 p = strchr(buf, ' ');
483 t->name = strdup(buf);
485 print("%s: %r\n", buf);
488 print("%s: %r\n", buf);
492 fprint(wctlfd, "resize -dx %d -dy %d\n",
493 Width + 20, (ntasks * Height) + 5);
495 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
500 doevent(Task *t, Traceevent *ep)
506 t->tevents[ep->etype & 0xffff]++;
508 t->events = realloc(t->events, t->nevents*sizeof(Event));
510 event = &t->events[n];
511 memmove(event, ep, sizeof(Traceevent));
514 switch(event->etype & 0xffff){
516 if (t->runthis > t->runmax)
517 t->runmax = t->runthis;
525 for(i = n-1; i >= 0; i--)
526 if (t->events[i].etype == SRun ||
527 t->events[i].etype == SEdf)
529 if(i < 0 || t->events[i].etime != 0)
531 runt = event->time - t->events[i].time;
533 t->events[i].etime = event->time;
541 print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]);
545 memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t));
547 fprint(wctlfd, "resize -dx %d -dy %d\n",
548 Width + 20, (ntasks * Height) + 5);
550 Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r);
558 char *wsys, line[256];
561 Keyboardctl *keyboardctl;
568 eventbuf = malloc(Nevents*sizeof(Traceevent));
571 if((logfd = open(profdev, OREAD)) < 0)
572 sysfatal("%s: Cannot open %s: %r", argv0, profdev);
575 if((wsys = getenv("wsys")) == nil)
576 sysfatal("%s: Cannot find windowing system: %r",
579 if((wfd = open(wsys, ORDWR)) < 0)
580 sysfatal("%s: Cannot open windowing system: %r",
583 snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d",
584 getpid(), Width + 20, Height + 5);
585 line[sizeof(line) - 1] = '\0';
588 if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0)
589 sysfatal("%s: Cannot mount %s under /mnt/wsys: %r",
592 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
593 sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r",
597 if((wctlfd = open("/dev/wctl", OWRITE)) < 0)
598 sysfatal("%s: Cannot open /dev/wctl: %r", argv0);
599 if(initdraw(nil, nil, "trace") < 0)
600 sysfatal("%s: initdraw failure: %r", argv0);
602 Width = Dx(screen->r);
603 Height = Dy(screen->r);
605 if((mousectl = initmouse(nil, screen)) == nil)
606 sysfatal("%s: cannot initialize mouse: %r", argv0);
608 if((keyboardctl = initkeyboard(nil)) == nil)
609 sysfatal("%s: cannot initialize keyboard: %r", argv0);
614 scaleno = 7; /* 100 milliseconds */
618 { mousectl->c, nil, CHANRCV },
619 { mousectl->resizec, nil, CHANRCV },
620 { keyboardctl->c, &r, CHANRCV },
621 { nil, nil, CHANNOBLK },
629 if(getwindow(display, Refnone) < 0)
630 sysfatal("drawrt: Cannot re-attach window");
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);
641 Width = Dx(screen->r);
642 Height = ntasks? Dy(screen->r)/ntasks:
651 for(i = 0; i < ntasks; i++){
652 tasks[i].tstart = now;
654 tasks[i].runtime = 0;
656 tasks[i].runthis = 0;
658 memset(tasks[i].tevents, 0, Nevent*sizeof(ulong));
669 if (scaleno < nelem(scales) - 1)
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);
704 ep->pid, ep->time, schedstatename[ep->etype & 0xffff]);
706 for(i = 0; i < ntasks; i++)
707 if(tasks[i].pid == ep->pid)
711 t = newtask(ep->pid);
712 t->tstart = ep->time;
722 sleep(scales[scaleno].sleep);
729 char buf[128], *sign;
735 t = va_arg(f->args, vlong);
737 case 't': // vlong in nanoseconds
738 t = va_arg(f->args, vlong);
741 return fmtstrcpy(f, "(timeconv)");
750 sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000);
751 }else if (t > MS(1)){
753 sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000);
755 sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1)));
757 sprint(buf, "%s%dns", sign, (int)t);
758 return fmtstrcpy(f, buf);