ref: f88a55e79b5bf656e7f9578d1318a955b9a4963a
dir: /sys/src/cmd/flambe.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> #include <bio.h> #include <mach.h> typedef struct Data Data; struct Data { ushort down; ushort right; ulong pc; ulong count; uvlong time; uint rec; }; enum { Datasz = 2+2+4+4+8 }; Data* data; long ndata; uvlong cyclefreq; void syms(char *cout) { Fhdr f; int fd; if((fd = open(cout, 0)) < 0) sysfatal("%r"); if(!crackhdr(fd, &f)) sysfatal("can't read text file header: %r"); if(f.type == FNONE) sysfatal("text file not an a.out"); if(syminit(fd, &f) < 0) sysfatal("syminit: %r"); close(fd); } #define GET2(p) (u16int)(p)[1] | (u16int)(p)[0]<<8 #define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24 #define GET8(p) (u64int)(p)[7] | (u64int)(p)[6]<<8 | (u64int)(p)[5]<<16 | (u64int)(p)[4]<<24 | \ (u64int)(p)[3]<<32 | (u64int)(p)[2]<<40 | (u64int)(p)[1]<<48 | (u64int)(p)[0]<<56 void datas(char *dout) { int fd; Dir *d; int i; uchar hdr[3+1+8], *buf, *p; if((fd = open(dout, 0)) < 0){ perror(dout); exits("open"); } d = dirfstat(fd); if(d == nil){ perror(dout); exits("stat"); } d->length -= sizeof hdr; ndata = d->length/Datasz; data = malloc(ndata*sizeof(Data)); buf = malloc(d->length); if(buf == 0 || data == 0) sysfatal("malloc"); if(read(fd, hdr, sizeof hdr) != sizeof hdr) sysfatal("read data header: %r"); if(memcmp(hdr, "pr\x0f", 3) != 0) sysfatal("bad magic"); cyclefreq = GET8(hdr+4); if(readn(fd, buf, d->length) != d->length) sysfatal("data file read: %r"); for(p = buf, i = 0; i < ndata; i++){ data[i].down = GET2(p); p += 2; data[i].right = GET2(p); p += 2; data[i].pc = GET4(p); p += 4; data[i].count = GET4(p); p += 4; data[i].time = GET8(p); p += 8; } free(buf); free(d); close(fd); } char* name(ulong pc) { Symbol s; static char buf[16]; if (findsym(pc, CTEXT, &s)) return(s.name); snprint(buf, sizeof(buf), "#%lux", pc); return buf; } int rowh; Image **cols; int ncols; uvlong total; Rectangle *clicks; void gencols(void) { int i, h; ulong col, step; h = Dy(screen->r) / rowh + 1; if(ncols == h) return; for(i = 0; i < ncols; i++) freeimage(cols[i]); free(cols); ncols = h; cols = malloc(sizeof(Image*)*ncols); col = DRed; step = (0xFF/ncols)<<16; for(i = 0; i < ncols; i++){ cols[i] = allocimagemix(display, DWhite, col); col += step; } } Image* colfor(int h) { if(h % 2 == 0) h += (ncols/3); return cols[h % ncols]; } void onhover(int i) { Rectangle r; char buf[128]; r = screen->r; r.max.y = r.min.y + (font->height+2)*2; draw(screen, r, display->white, nil, ZP); string(screen, r.min, display->black, ZP, font, name(data[i].pc)); r.min.y += font->height + 1; snprint(buf, sizeof buf, "Time: %.8f(s), Calls: %lud", (double)data[i].time/cyclefreq, data[i].count); string(screen, r.min, display->black, ZP, font, buf); flushimage(display, 1); } void graph(int i, int x, int h) { Rectangle r, r2; if(i >= ndata) sysfatal("corrupted profile data"); r.min = (Point){x, screen->r.max.y - rowh*h}; r.max = (Point){ x + ((double)data[i].time * Dx(screen->r)) / total, r.min.y + rowh }; clicks[i] = r; if(Dx(r) > 6){ draw(screen, r, colfor(h), nil, ZP); r2 = r; r2.min.x = r2.max.x - 2; draw(screen, r2, display->black, nil, ZP); screen->clipr = r; r2.min = r.min; r2.min.x += 4; string(screen, r2.min, display->black, ZP, font, name(data[i].pc)); screen->clipr = screen->r; } if(data[i].right != 0xFFFF) graph(data[i].right, r.max.x, h); if(data[i].down != 0xFFFF) graph(data[i].down, x, h + 1); } void redraw(int i) { total = data[i].time; memset(clicks, 0, ndata * sizeof(Rectangle)); gencols(); draw(screen, screen->r, display->white, nil, ZP); graph(i, screen->r.min.x, 1); flushimage(display, 1); } enum { Ckey, Cmouse, Cresize, Numchan, }; void usage(void) { fprint(2, "%s: $O.out prof\n", argv0); exits("usage"); } /* syminit has a Biobuf on the stack */ mainstacksize = 64*1024; void threadmain(int argc, char **argv) { Mousectl *mctl; Keyboardctl *kctl; Mouse m; Rune r; vlong t; int i; Alt a[Numchan+1] = { [Ckey] = {nil, &r, CHANRCV}, [Cmouse] = {nil, &m, CHANRCV }, [Cresize] = {nil, nil, CHANRCV}, {nil, nil, CHANEND}, }; ARGBEGIN{ default: usage(); break; }ARGEND; if(argc != 2) usage(); syms(argv[0]); datas(argv[1]); for(int i = 1; i < ndata; i++){ if((t = data[i].time) < 0) data[i].time = t + data[0].time; } if(initdraw(nil, nil, "flambe") < 0) sysfatal("initdraw: %r"); if((kctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[Ckey].c = kctl->c; if((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); a[Cmouse].c = mctl->c; a[Cresize].c = mctl->resizec; rowh = font->height + 2; clicks = malloc(sizeof(Rectangle)*ndata); redraw(1); for(;;) switch(alt(a)){ case Ckey: switch(r){ case Kesc: redraw(1); break; case Kdel: case 'q': threadexitsall(nil); default: break; } break; case Cmouse: if(m.buttons == 4 || m.buttons == 16){ redraw(1); break; } for(i = 0; i < ndata; i++){ if(!ptinrect(m.xy, clicks[i])) continue; switch(m.buttons){ case 0: onhover(i); break; case 1: case 8: redraw(i); break; } break; } break; case Cresize: getwindow(display, Refnone); redraw(1); break; } }