diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile new file mode 100644 index 00000000000..99d292eb427 --- /dev/null +++ b/src/cmd/prof/Makefile @@ -0,0 +1,28 @@ +# 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 ../../Make.conf + +# The directory is db because the source is portable and general. +# We call the binary 6prof to avoid confusion and because this binary +# is linked only with amd64 and x86 support. + +TARG=6prof +OFILES=\ + main.$O\ + +#HFILES=\ +# defs.h\ +# fns.h\ + +$(TARG): $(OFILES) + $(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lmach_amd64 -lbio -l9 + +clean: + rm -f $(OFILES) $(TARG) + +install: $(TARG) + cp $(TARG) $(BIN)/$(TARG) + +$(OFILES): $(HFILES) diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c new file mode 100644 index 00000000000..cd708e96f7d --- /dev/null +++ b/src/cmd/prof/main.c @@ -0,0 +1,341 @@ +// 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 +#include +#include +#include +#include + +#include +#include + +int pid; +char* file = "6.out"; +static Fhdr fhdr; +int have_syms; +int fd; +Map *map; +Map *symmap; +struct Ureg ureg; +int total_sec = 10; +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() +{ + fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec] [6.out]\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() +{ + int i; + + ctlproc(pid, "stop"); + for(i = 0; i < sizeof ureg; i+=8) { + if(get8(map, (uvlong)i, &((uvlong*)&ureg)[i/8]) < 0) { + fprint(2, "prof: can't read registers at %d: %r\n", i); + return 0; + } + } + ctlproc(pid, "start"); + return 1; +} + +uvlong nextpc; + +void +ptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) +{ + char buf[1024]; + 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, ptrace) <= 0) + fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp); +} + +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() +{ + int msec; + struct timespec req; + + req.tv_sec = delta_msec/1000; + req.tv_nsec = 1000000*(delta_msec % 1000); + for(msec = 0; msec < 1000*total_sec; msec += delta_msec) { + if(!sample()) + break; + printpc(ureg.ip, ureg.sp); + 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[i-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 +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; + case 'l': + linenums = 1; + break; + case 'r': + registers = 1; + break; + case 's': + stacks = 1; + break; + }ARGEND + if(pid <= 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); + } + 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); + } + samples(); + detachproc(map); + dumphistogram(); + exit(0); +}