diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/trace.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/trace.c')
-rwxr-xr-x | sys/src/cmd/trace.c | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/sys/src/cmd/trace.c b/sys/src/cmd/trace.c new file mode 100755 index 000000000..72262da40 --- /dev/null +++ b/sys/src/cmd/trace.c @@ -0,0 +1,759 @@ +#include <u.h> +#include <tos.h> +#include <libc.h> +#include <thread.h> +#include <ip.h> +#include <bio.h> +#include <draw.h> +#include <mouse.h> +#include <cursor.h> +#include <keyboard.h> +#include "trace.h" + +#pragma varargck type "t" vlong +#pragma varargck type "U" uvlong + +#define NS(x) ((vlong)x) +#define US(x) (NS(x) * 1000ULL) +#define MS(x) (US(x) * 1000ULL) +#define S(x) (MS(x) * 1000ULL) + +#define numblocks(a, b) (((a) + (b) - 1) / (b)) +#define roundup(a, b) (numblocks((a), (b)) * (b)) + +enum { + OneRound = MS(1)/2LL, + MilliRound = US(1)/2LL, +}; + +typedef struct Event Event; +typedef struct Task Task; +struct Event { + Traceevent; + vlong etime; /* length of block to draw */ +}; + +struct Task { + int pid; + char *name; + int nevents; + Event *events; + vlong tstart; + vlong total; + vlong runtime; + vlong runmax; + vlong runthis; + long runs; + ulong tevents[Nevent]; +}; + +enum { + Nevents = 1024, + Ncolor = 6, + K = 1024, +}; + +vlong now, prevts; + +int newwin; +int Width = 1000; +int Height = 100; // Per task +int topmargin = 8; +int bottommargin = 4; +int lineht = 12; +int wctlfd; +int nevents; +Traceevent *eventbuf; +Event *event; + +void drawtrace(void); +int schedparse(char*, char*, char*); +int timeconv(Fmt*); + +char *schedstatename[] = { + [SAdmit] = "Admit", + [SSleep] = "Sleep", + [SDead] = "Dead", + [SDeadline] = "Deadline", + [SEdf] = "Edf", + [SExpel] = "Expel", + [SReady] = "Ready", + [SRelease] = "Release", + [SRun] = "Run", + [SSlice] = "Slice", + [SInts] = "Ints", + [SInte] = "Inte", + [SUser] = "User", + [SYield] = "Yield", +}; + +struct { + vlong scale; + vlong bigtics; + vlong littletics; + int sleep; +} scales[] = { + { US(500), US(100), US(50), 0}, + { US(1000), US(500), US(100), 0}, + { US(2000), US(1000), US(200), 0}, + { US(5000), US(1000), US(500), 0}, + { MS(10), MS(5), MS(1), 20}, + { MS(20), MS(10), MS(2), 20}, + { MS(50), MS(10), MS(5), 20}, + { MS(100), MS(50), MS(10), 20}, /* starting scaleno */ + { MS(200), MS(100), MS(20), 20}, + { MS(500), MS(100), MS(50), 50}, + { MS(1000), MS(500), MS(100), 100}, + { MS(2000), MS(1000), MS(200), 100}, + { MS(5000), MS(1000), MS(500), 100}, + { S(10), S(50), S(1), 100}, + { S(20), S(10), S(2), 100}, + { S(50), S(10), S(5), 100}, + { S(100), S(50), S(10), 100}, + { S(200), S(100), S(20), 100}, + { S(500), S(100), S(50), 100}, + { S(1000), S(500), S(100), 100}, +}; + +int ntasks, verbose, triggerproc, paused; +Task *tasks; +Image *cols[Ncolor][4]; +Font *mediumfont, *tinyfont; +Image *grey, *red, *green, *blue, *bg, *fg; +char*profdev = "/proc/trace"; + +static void +usage(void) +{ + fprint(2, "Usage: %s [-d profdev] [-w] [-v] [-t triggerproc] [processes]\n", argv0); + exits(nil); +} + +void +threadmain(int argc, char **argv) +{ + int fd, i; + char fname[80]; + + fmtinstall('t', timeconv); + ARGBEGIN { + case 'd': + profdev = EARGF(usage()); + break; + case 'v': + verbose = 1; + break; + case 'w': + newwin++; + break; + case 't': + triggerproc = (int)strtol(EARGF(usage()), nil, 0); + break; + default: + usage(); + } + ARGEND; + + fname[sizeof fname - 1] = 0; + for(i = 0; i < argc; i++){ + snprint(fname, sizeof fname - 2, "/proc/%s/ctl", + argv[i]); + if((fd = open(fname, OWRITE)) < 0){ + fprint(2, "%s: cannot open %s: %r\n", + argv[0], fname); + continue; + } + + if(fprint(fd, "trace 1") < 0) + fprint(2, "%s: cannot enable tracing on %s: %r\n", + argv[0], fname); + close(fd); + } + + drawtrace(); +} + +static void +mkcol(int i, int c0, int c1, int c2) +{ + cols[i][0] = allocimagemix(display, c0, DWhite); + cols[i][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c1); + cols[i][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c2); + cols[i][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c0); +} + +static void +colinit(void) +{ + mediumfont = openfont(display, "/lib/font/bit/lucidasans/unicode.10.font"); + if(mediumfont == nil) + mediumfont = font; + tinyfont = openfont(display, "/lib/font/bit/lucidasans/unicode.7.font"); + if(tinyfont == nil) + tinyfont = font; + topmargin = mediumfont->height+2; + bottommargin = tinyfont->height+2; + + /* Peach */ + mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF); + /* Aqua */ + mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue); + /* Yellow */ + mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen); + /* Green */ + mkcol(3, DPalegreen, DMedgreen, DDarkgreen); + /* Blue */ + mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF); + /* Grey */ + cols[5][0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEEEEEEFF); + cols[5][1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF); + cols[5][2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x888888FF); + cols[5][3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF); + grey = cols[5][2]; + red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFF0000FF); + green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x00FF00FF); + blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF); + bg = display->white; + fg = display->black; +} + +static void +redraw(int scaleno) +{ + int n, i, j, x; + char buf[256]; + Point p, q; + Rectangle r, rtime; + Task *t; + vlong ts, oldestts, newestts, period, ppp, scale, s, ss; + +# define time2x(t) ((int)(((t) - oldestts) / ppp)) + + scale = scales[scaleno].scale; + period = scale + scales[scaleno].littletics; + ppp = period / Width; // period per pixel. + + /* Round `now' to a nice number */ + newestts = now - (now % scales[scaleno].bigtics) + + (scales[scaleno].littletics>>1); + + oldestts = newestts - period; + +//print("newestts %t, period %t, %d-%d\n", newestts, period, time2x(oldestts), time2x(newestts)); + if (prevts < oldestts){ + oldestts = newestts - period; + + prevts = oldestts; + draw(screen, screen->r, bg, nil, ZP); + }else{ + /* just white out time */ + rtime = screen->r; + rtime.min.x = rtime.max.x - stringwidth(mediumfont, "00000000000.000s"); + rtime.max.y = rtime.min.y + mediumfont->height; + draw(screen, rtime, bg, nil, ZP); + } + p = screen->r.min; + for (n = 0; n != ntasks; n++) { + t = &tasks[n]; + /* p is upper left corner for this task */ + rtime = Rpt(p, addpt(p, Pt(500, mediumfont->height))); + draw(screen, rtime, bg, nil, ZP); + snprint(buf, sizeof(buf), "%d %s", t->pid, t->name); + q = string(screen, p, fg, ZP, mediumfont, buf); + s = now - t->tstart; + if(t->tevents[SRelease]) + snprint(buf, sizeof(buf), " per %t — avg: %t max: %t", + (vlong)(s/t->tevents[SRelease]), + (vlong)(t->runtime/t->tevents[SRelease]), + t->runmax); + else if((s /=1000000000LL) != 0) + snprint(buf, sizeof(buf), " per 1s — avg: %t total: %t", + t->total/s, + t->total); + else + snprint(buf, sizeof(buf), " total: %t", t->total); + string(screen, q, fg, ZP, tinyfont, buf); + p.y += Height; + } + x = time2x(prevts); + + p = screen->r.min; + for (n = 0; n != ntasks; n++) { + t = &tasks[n]; + + /* p is upper left corner for this task */ + + /* Move part already drawn */ + r = Rect(p.x, p.y + topmargin, p.x + x, p.y+Height); + draw(screen, r, screen, nil, Pt(p.x + Width - x, p.y + topmargin)); + + r.max.x = screen->r.max.x; + r.min.x += x; + draw(screen, r, bg, nil, ZP); + + line(screen, addpt(p, Pt(x, Height - lineht)), Pt(screen->r.max.x, p.y + Height - lineht), + Endsquare, Endsquare, 0, cols[n % Ncolor][1], ZP); + + for (i = 0; i < t->nevents-1; i++) + if (prevts < t->events[i + 1].time) + break; + + if (i > 0) { + memmove(t->events, t->events + i, (t->nevents - i) * sizeof(Event)); + t->nevents -= i; + } + + for (i = 0; i != t->nevents; i++) { + Event *e = &t->events[i], *_e; + int sx, ex; + + switch (e->etype & 0xffff) { + case SAdmit: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endarrow, Endsquare, 1, green, ZP); + } + break; + case SExpel: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endsquare, Endarrow, 1, red, ZP); + } + break; + case SRelease: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endarrow, Endsquare, 1, fg, ZP); + } + break; + case SDeadline: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endsquare, Endarrow, 1, fg, ZP); + } + break; + + case SYield: + case SUser: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endsquare, Endarrow, 0, + (e->etype == SYield)? green: blue, ZP); + } + break; + case SSlice: + if (e->time > prevts && e->time <= newestts) { + sx = time2x(e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endsquare, Endarrow, 0, red, ZP); + } + break; + + case SRun: + case SEdf: + sx = time2x(e->time); + ex = time2x(e->etime); + if(ex == sx) + ex++; + + r = Rect(sx, topmargin + 8, ex, Height - lineht); + r = rectaddpt(r, p); + + draw(screen, r, cols[n % Ncolor][e->etype==SRun?1:3], nil, ZP); + + if(t->pid == triggerproc && ex < Width) + paused ^= 1; + + for(j = 0; j < t->nevents; j++){ + _e = &t->events[j]; + switch(_e->etype & 0xffff){ + case SInts: + if (_e->time > prevts && _e->time <= newestts){ + sx = time2x(_e->time); + line(screen, addpt(p, Pt(sx, topmargin)), + addpt(p, Pt(sx, Height / 2 - bottommargin)), + Endsquare, Endsquare, 0, + green, ZP); + } + break; + case SInte: + if (_e->time > prevts && _e->time <= newestts) { + sx = time2x(_e->time); + line(screen, addpt(p, Pt(sx, Height / 2 - bottommargin)), + addpt(p, Pt(sx, Height - bottommargin)), + Endsquare, Endsquare, 0, + blue, ZP); + } + break; + } + } + break; + } + } + p.y += Height; + } + + ts = prevts + scales[scaleno].littletics - (prevts % scales[scaleno].littletics); + x = time2x(ts); + + while(x < Width){ + p = screen->r.min; + for(n = 0; n < ntasks; n++){ + int height, width; + + /* p is upper left corner for this task */ + if ((ts % scales[scaleno].scale) == 0){ + height = 10 * Height; + width = 1; + }else if ((ts % scales[scaleno].bigtics) == 0){ + height = 12 * Height; + width = 0; + }else{ + height = 13 * Height; + width = 0; + } + height >>= 4; + + line(screen, addpt(p, Pt(x, height)), addpt(p, Pt(x, Height - lineht)), + Endsquare, Endsquare, width, cols[n % Ncolor][2], ZP); + + p.y += Height; + } + ts += scales[scaleno].littletics; + x = time2x(ts); + } + + rtime = screen->r; + rtime.min.y = rtime.max.y - tinyfont->height + 2; + draw(screen, rtime, bg, nil, ZP); + ts = oldestts + scales[scaleno].bigtics - (oldestts % scales[scaleno].bigtics); + x = time2x(ts); + ss = 0; + while(x < Width){ + snprint(buf, sizeof(buf), "%t", ss); + string(screen, addpt(p, Pt(x - stringwidth(tinyfont, buf)/2, - tinyfont->height - 1)), + fg, ZP, tinyfont, buf); + ts += scales[scaleno].bigtics; + ss += scales[scaleno].bigtics; + x = time2x(ts); + } + + snprint(buf, sizeof(buf), "%t", now); + string(screen, Pt(screen->r.max.x - stringwidth(mediumfont, buf), screen->r.min.y), + fg, ZP, mediumfont, buf); + + flushimage(display, 1); + prevts = newestts; +} + +Task* +newtask(ulong pid) +{ + Task *t; + char buf[64], *p; + int fd,n; + + tasks = realloc(tasks, (ntasks + 1) * sizeof(Task)); + assert(tasks); + + t = &tasks[ntasks++]; + memset(t, 0, sizeof(Task)); + t->events = nil; + snprint(buf, sizeof buf, "/proc/%ld/status", pid); + t->name = nil; + fd = open(buf, OREAD); + if (fd >= 0){ + n = read(fd, buf, sizeof buf); + if(n > 0){ + p = buf + sizeof buf - 1; + *p = 0; + p = strchr(buf, ' '); + if (p) *p = 0; + t->name = strdup(buf); + }else + print("%s: %r\n", buf); + close(fd); + }else + print("%s: %r\n", buf); + t->pid = pid; + prevts = 0; + if (newwin){ + fprint(wctlfd, "resize -dx %d -dy %d\n", + Width + 20, (ntasks * Height) + 5); + }else + Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r); + return t; +} + +void +doevent(Task *t, Traceevent *ep) +{ + int i, n; + Event *event; + vlong runt; + + t->tevents[ep->etype & 0xffff]++; + n = t->nevents++; + t->events = realloc(t->events, t->nevents*sizeof(Event)); + assert(t->events); + event = &t->events[n]; + memmove(event, ep, sizeof(Traceevent)); + event->etime = 0; + + switch(event->etype & 0xffff){ + case SRelease: + if (t->runthis > t->runmax) + t->runmax = t->runthis; + t->runthis = 0; + break; + + case SSleep: + case SYield: + case SReady: + case SSlice: + for(i = n-1; i >= 0; i--) + if (t->events[i].etype == SRun || + t->events[i].etype == SEdf) + break; + if(i < 0 || t->events[i].etime != 0) + break; + runt = event->time - t->events[i].time; + if(runt > 0){ + t->events[i].etime = event->time; + t->runtime += runt; + t->total += runt; + t->runthis += runt; + t->runs++; + } + break; + case SDead: +print("task died %ld %t %s\n", event->pid, event->time, schedstatename[event->etype & 0xffff]); + free(t->events); + free(t->name); + ntasks--; + memmove(t, t+1, sizeof(Task)*(&tasks[ntasks]-t)); + if (newwin) + fprint(wctlfd, "resize -dx %d -dy %d\n", + Width + 20, (ntasks * Height) + 5); + else + Height = ntasks ? Dy(screen->r)/ntasks : Dy(screen->r); + prevts = 0; + } +} + +void +drawtrace(void) +{ + char *wsys, line[256]; + int wfd, logfd; + Mousectl *mousectl; + Keyboardctl *keyboardctl; + int scaleno; + Rune r; + int i, n; + Task *t; + Traceevent *ep; + + eventbuf = malloc(Nevents*sizeof(Traceevent)); + assert(eventbuf); + + if((logfd = open(profdev, OREAD)) < 0) + sysfatal("%s: Cannot open %s: %r", argv0, profdev); + + if(newwin){ + if((wsys = getenv("wsys")) == nil) + sysfatal("%s: Cannot find windowing system: %r", + argv0); + + if((wfd = open(wsys, ORDWR)) < 0) + sysfatal("%s: Cannot open windowing system: %r", + argv0); + + snprint(line, sizeof(line), "new -pid %d -dx %d -dy %d", + getpid(), Width + 20, Height + 5); + line[sizeof(line) - 1] = '\0'; + rfork(RFNAMEG); + + if(mount(wfd, -1, "/mnt/wsys", MREPL, line) < 0) + sysfatal("%s: Cannot mount %s under /mnt/wsys: %r", + argv0, line); + + if(bind("/mnt/wsys", "/dev", MBEFORE) < 0) + sysfatal("%s: Cannot bind /mnt/wsys in /dev: %r", + argv0); + + } + if((wctlfd = open("/dev/wctl", OWRITE)) < 0) + sysfatal("%s: Cannot open /dev/wctl: %r", argv0); + if(initdraw(nil, nil, "trace") < 0) + sysfatal("%s: initdraw failure: %r", argv0); + + Width = Dx(screen->r); + Height = Dy(screen->r); + + if((mousectl = initmouse(nil, screen)) == nil) + sysfatal("%s: cannot initialize mouse: %r", argv0); + + if((keyboardctl = initkeyboard(nil)) == nil) + sysfatal("%s: cannot initialize keyboard: %r", argv0); + + colinit(); + + paused = 0; + scaleno = 7; /* 100 milliseconds */ + now = nsec(); + for(;;) { + Alt a[] = { + { mousectl->c, nil, CHANRCV }, + { mousectl->resizec, nil, CHANRCV }, + { keyboardctl->c, &r, CHANRCV }, + { nil, nil, CHANNOBLK }, + }; + + switch (alt(a)) { + case 0: + continue; + + case 1: + if(getwindow(display, Refnone) < 0) + sysfatal("drawrt: Cannot re-attach window"); + if(newwin){ + if(Dx(screen->r) != Width || + Dy(screen->r) != (ntasks * Height)){ + fprint(2, "resize: x: have %d, need %d; y: have %d, need %d\n", + Dx(screen->r), Width + 8, Dy(screen->r), (ntasks * Height) + 8); + fprint(wctlfd, "resize -dx %d -dy %d\n", + Width + 8, (ntasks * Height) + 8); + } + } + else{ + Width = Dx(screen->r); + Height = ntasks? Dy(screen->r)/ntasks: + Dy(screen->r); + } + break; + + case 2: + + switch(r){ + case 'r': + for(i = 0; i < ntasks; i++){ + tasks[i].tstart = now; + tasks[i].total = 0; + tasks[i].runtime = 0; + tasks[i].runmax = 0; + tasks[i].runthis = 0; + tasks[i].runs = 0; + memset(tasks[i].tevents, 0, Nevent*sizeof(ulong)); + + } + break; + + case 'p': + paused ^= 1; + prevts = 0; + break; + + case '-': + if (scaleno < nelem(scales) - 1) + scaleno++; + prevts = 0; + break; + + case '+': + if (scaleno > 0) + scaleno--; + prevts = 0; + break; + + case 'q': + threadexitsall(nil); + + case 'v': + verbose ^= 1; + + default: + break; + } + break; + + case 3: + now = nsec(); + while((n = read(logfd, eventbuf, Nevents*sizeof(Traceevent))) > 0){ + assert((n % sizeof(Traceevent)) == 0); + nevents = n / sizeof(Traceevent); + for (ep = eventbuf; ep < eventbuf + nevents; ep++){ + if ((ep->etype & 0xffff) >= Nevent){ + print("%ld %t Illegal event %ld\n", + ep->pid, ep->time, ep->etype & 0xffff); + continue; + } + if (verbose) + print("%ld %t %s\n", + ep->pid, ep->time, schedstatename[ep->etype & 0xffff]); + + for(i = 0; i < ntasks; i++) + if(tasks[i].pid == ep->pid) + break; + + if(i == ntasks){ + t = newtask(ep->pid); + t->tstart = ep->time; + }else + t = &tasks[i]; + + doevent(t, ep); + } + } + if(!paused) + redraw(scaleno); + } + sleep(scales[scaleno].sleep); + } +} + +int +timeconv(Fmt *f) +{ + char buf[128], *sign; + vlong t; + + buf[0] = 0; + switch(f->r) { + case 'U': + t = va_arg(f->args, vlong); + break; + case 't': // vlong in nanoseconds + t = va_arg(f->args, vlong); + break; + default: + return fmtstrcpy(f, "(timeconv)"); + } + if (t < 0) { + sign = "-"; + t = -t; + }else + sign = ""; + if (t > S(1)){ + t += OneRound; + sprint(buf, "%s%d.%.3ds", sign, (int)(t / S(1)), (int)(t % S(1))/1000000); + }else if (t > MS(1)){ + t += MilliRound; + sprint(buf, "%s%d.%.3dms", sign, (int)(t / MS(1)), (int)(t % MS(1))/1000); + }else if (t > US(1)) + sprint(buf, "%s%d.%.3dµs", sign, (int)(t / US(1)), (int)(t % US(1))); + else + sprint(buf, "%s%dns", sign, (int)t); + return fmtstrcpy(f, buf); +} |