]> Cypherpunks repositories - gostls13.git/commitdiff
rudimentary real-time profiler.
authorRob Pike <r@golang.org>
Wed, 24 Sep 2008 01:45:44 +0000 (18:45 -0700)
committerRob Pike <r@golang.org>
Wed, 24 Sep 2008 01:45:44 +0000 (18:45 -0700)
tested on mac only.
output like this:
tubenose=% sudo go/src/cmd/prof/6prof -p 71839 -c  -d 10
63.93% mach_semaphore_signal
 4.64% sys·chansend1
 3.93% chanrecv
 2.86% semrelease
 1.43% cas
 1.43% sendchan
 1.07% xadd
 0.71% main·f
 0.71% scheduler
 0.71% sys·gosched
 0.71% dequeue
 ...

R=rsc
DELTA=361  (361 added, 0 deleted, 0 changed)
OCL=15731
CL=15736

src/cmd/prof/Makefile [new file with mode: 0644]
src/cmd/prof/main.c [new file with mode: 0644]

diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile
new file mode 100644 (file)
index 0000000..99d292e
--- /dev/null
@@ -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 (file)
index 0000000..cd708e9
--- /dev/null
@@ -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 <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <time.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 = 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);
+}