| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include <u.h> |
| #include <time.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <ctype.h> |
| |
| #include <ureg_amd64.h> |
| #include <mach_amd64.h> |
| |
| int pid; |
| char* file = "6.out"; |
| static Fhdr fhdr; |
| int have_syms; |
| int fd; |
| Map *map; |
| Map *symmap; |
| struct Ureg ureg; |
| int total_sec = 0; |
| int delta_msec = 100; |
| int collapse = 1; // collapse histogram trace points in same function |
| |
| // output formats |
| int functions; // print functions |
| int histograms; // print histograms |
| int linenums; // print file and line numbers rather than function names |
| int registers; // print registers |
| int stacks; // print stack traces |
| |
| void |
| Usage(void) |
| { |
| fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]\n"); |
| fprint(2, "\tformats (default -h):\n"); |
| fprint(2, "\t\t-h: histograms\n"); |
| fprint(2, "\t\t-f: dynamic functions\n"); |
| fprint(2, "\t\t-l: dynamic file and line numbers\n"); |
| fprint(2, "\t\t-r: dynamic registers\n"); |
| fprint(2, "\t\t-s: dynamic function stack traces\n"); |
| exit(2); |
| } |
| |
| typedef struct PC PC; |
| struct PC { |
| uvlong pc; |
| unsigned int count; |
| PC* next; |
| }; |
| |
| enum { |
| Ncounters = 256 |
| }; |
| |
| PC *counters[Ncounters]; |
| |
| void |
| regprint(void) |
| { |
| print("ax\t0x%llux\n", ureg.ax); |
| print("bx\t0x%llux\n", ureg.bx); |
| print("cx\t0x%llux\n", ureg.cx); |
| print("dx\t0x%llux\n", ureg.dx); |
| print("si\t0x%llux\n", ureg.si); |
| print("di\t0x%llux\n", ureg.di); |
| print("bp\t0x%llux\n", ureg.bp); |
| print("r8\t0x%llux\n", ureg.r8); |
| print("r9\t0x%llux\n", ureg.r9); |
| print("r10\t0x%llux\n", ureg.r10); |
| print("r11\t0x%llux\n", ureg.r11); |
| print("r12\t0x%llux\n", ureg.r12); |
| print("r13\t0x%llux\n", ureg.r13); |
| print("r14\t0x%llux\n", ureg.r14); |
| print("r15\t0x%llux\n", ureg.r15); |
| print("ds\t0x%llux\n", ureg.ds); |
| print("es\t0x%llux\n", ureg.es); |
| print("fs\t0x%llux\n", ureg.fs); |
| print("gs\t0x%llux\n", ureg.gs); |
| print("type\t0x%llux\n", ureg.type); |
| print("error\t0x%llux\n", ureg.error); |
| print("pc\t0x%llux\n", ureg.ip); |
| print("cs\t0x%llux\n", ureg.cs); |
| print("flags\t0x%llux\n", ureg.flags); |
| print("sp\t0x%llux\n", ureg.sp); |
| print("ss\t0x%llux\n", ureg.ss); |
| } |
| |
| int |
| sample(void) |
| { |
| int i; |
| static int n; |
| |
| n++; |
| for(i = 0; i < sizeof ureg; i+=8) { |
| if(get8(map, (uvlong)i, &((uvlong*)&ureg)[i/8]) < 0) { |
| if(n == 1) |
| fprint(2, "prof: can't read registers at %d: %r\n", i); |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| uvlong nextpc; |
| |
| void |
| xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) |
| { |
| char buf[1024]; |
| if(sym == nil){ |
| print("syms\n"); |
| return; |
| } |
| if(nextpc == 0) |
| nextpc = sym->value; |
| print("%s(", sym->name); |
| print(")"); |
| if(nextpc != sym->value) |
| print("+%#llux ", nextpc - sym->value); |
| if(have_syms && linenums && fileline(buf, sizeof buf, pc)) { |
| print(" %s", buf); |
| } |
| print("\n"); |
| nextpc = pc; |
| } |
| |
| void |
| stacktracepcsp(uvlong pc, uvlong sp) |
| { |
| nextpc = 0; |
| if(machdata->ctrace==nil) |
| fprint(2, "no machdata->ctrace\n"); |
| else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0) |
| fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp); |
| else |
| print("\n"); |
| } |
| |
| void |
| addtohistogram(uvlong pc, uvlong sp) |
| { |
| int h; |
| PC *x; |
| |
| h = pc % Ncounters; |
| for(x = counters[h]; x != NULL; x = x->next) { |
| if(x->pc == pc) { |
| x->count++; |
| return; |
| } |
| } |
| x = malloc(sizeof(PC)); |
| x->pc = pc; |
| x->count = 1; |
| x->next = counters[h]; |
| counters[h] = x; |
| } |
| |
| void |
| printpc(uvlong pc, uvlong sp) |
| { |
| char buf[1024]; |
| if(registers) |
| regprint(); |
| if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc)) |
| print("%s\n", buf); |
| if(have_syms > 0 && functions) { |
| symoff(buf, sizeof(buf), pc, CANY); |
| print("%s\n", buf); |
| } |
| if(stacks){ |
| stacktracepcsp(pc, sp); |
| } |
| if(histograms){ |
| addtohistogram(pc, sp); |
| } |
| } |
| |
| void |
| samples(void) |
| { |
| int msec; |
| struct timespec req; |
| |
| req.tv_sec = delta_msec/1000; |
| req.tv_nsec = 1000000*(delta_msec % 1000); |
| for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) { |
| ctlproc(pid, "stop"); |
| if(!sample()) { |
| ctlproc(pid, "start"); |
| break; |
| } |
| printpc(ureg.ip, ureg.sp); |
| ctlproc(pid, "start"); |
| nanosleep(&req, NULL); |
| } |
| } |
| |
| int |
| comparepc(const void *va, const void *vb) |
| { |
| const PC *const*a = va; |
| const PC *const*b = vb; |
| return (*a)->pc - (*b)->pc; |
| } |
| |
| int |
| comparecount(const void *va, const void *vb) |
| { |
| const PC *const*a = va; |
| const PC *const*b = vb; |
| return (*b)->count - (*a)->count; // sort downwards |
| } |
| |
| void |
| func(char *s, int n, uvlong pc) |
| { |
| char *p; |
| |
| symoff(s, n, pc, CANY); |
| p = strchr(s, '+'); |
| if(p != NULL) |
| *p = 0; |
| } |
| |
| void |
| dumphistogram() |
| { |
| int h; |
| PC *x; |
| PC **pcs; |
| uint i; |
| uint j; |
| uint npc; |
| uint ncount; |
| char b1[100]; |
| char b2[100]; |
| |
| if(!histograms) |
| return; |
| |
| // count samples |
| ncount = 0; |
| npc = 0; |
| for(h = 0; h < Ncounters; h++) |
| for(x = counters[h]; x != NULL; x = x->next) { |
| ncount += x->count; |
| npc++; |
| } |
| // build array |
| pcs = malloc(npc*sizeof(PC*)); |
| i = 0; |
| for(h = 0; h < Ncounters; h++) |
| for(x = counters[h]; x != NULL; x = x->next) |
| pcs[i++] = x; |
| if(collapse) { |
| // combine counts in same function |
| // sort by address |
| qsort(pcs, npc, sizeof(PC*), comparepc); |
| for(i = j = 0; i < npc; i++){ |
| x = pcs[i]; |
| func(b2, sizeof(b2), x->pc); |
| if(j > 0 && strcmp(b1, b2) == 0) { |
| pcs[j-1]->count += x->count; |
| } else { |
| strcpy(b1, b2); |
| pcs[j++] = x; |
| } |
| } |
| npc = j; |
| } |
| // sort by count |
| qsort(pcs, npc, sizeof(PC*), comparecount); |
| // print array |
| for(i = 0; i < npc; i++){ |
| x = pcs[i]; |
| print("%5.2f%%\t", 100.0*(double)x->count/(double)ncount); |
| if(collapse) |
| func(b2, sizeof b2, x->pc); |
| else |
| symoff(b2, sizeof(b2), x->pc, CANY); |
| print("%s\n", b2); |
| } |
| } |
| |
| int |
| startprocess(char **argv) |
| { |
| int pid; |
| |
| if((pid = fork()) == 0) { |
| pid = getpid(); |
| if(ctlproc(pid, "hang") < 0){ |
| fprint(2, "prof: child process could not hang\n"); |
| exits(0); |
| } |
| execv(argv[0], argv); |
| fprint(2, "prof: could not exec %s: %r\n", argv[0]); |
| exits(0); |
| } |
| |
| if(pid == -1) { |
| fprint(2, "prof: could not fork\n"); |
| exit(1); |
| } |
| if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) { |
| fprint(2, "prof: could not attach to child process: %r\n"); |
| exit(1); |
| } |
| return pid; |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| ARGBEGIN{ |
| case 'c': |
| collapse = 0; |
| break; |
| case 'd': |
| delta_msec = atoi(EARGF(Usage)); |
| break; |
| case 't': |
| total_sec = atoi(EARGF(Usage)); |
| break; |
| case 'p': |
| pid = atoi(EARGF(Usage)); |
| break; |
| case 'f': |
| functions = 1; |
| break; |
| case 'h': |
| histograms = 1; |
| break; |
| case 'l': |
| linenums = 1; |
| break; |
| case 'r': |
| registers = 1; |
| break; |
| case 's': |
| stacks = 1; |
| break; |
| }ARGEND |
| if(pid <= 0 && argc == 0) |
| Usage(); |
| if(functions+linenums+registers+stacks == 0) |
| histograms = 1; |
| if(!machbyname("amd64")) { |
| fprint(2, "prof: no amd64 support\n", pid); |
| exit(1); |
| } |
| if(argc > 0) |
| file = argv[0]; |
| fd = open(file, 0); |
| if(fd < 0) { |
| fprint(2, "prof: can't open %s: %r\n", file); |
| exit(1); |
| } |
| if(pid <= 0) |
| pid = startprocess(argv); |
| map = attachproc(pid, &fhdr); |
| if(map == nil) { |
| fprint(2, "prof: can't attach to %d: %r\n", pid); |
| exit(1); |
| } |
| if(crackhdr(fd, &fhdr)) { |
| have_syms = syminit(fd, &fhdr); |
| if(!have_syms) { |
| fprint(2, "prof: no symbols for %s: %r\n", file); |
| } |
| } else { |
| fprint(2, "prof: crack header for %s: %r\n", file); |
| exit(1); |
| } |
| ctlproc(pid, "start"); |
| samples(); |
| detachproc(map); |
| dumphistogram(); |
| exit(0); |
| } |