1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:05:02 -07:00

rudimentary real-time profiler.

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
This commit is contained in:
Rob Pike 2008-09-23 18:45:44 -07:00
parent 83267dce11
commit eea21f8b85
2 changed files with 369 additions and 0 deletions

28
src/cmd/prof/Makefile Normal file
View File

@ -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)

341
src/cmd/prof/main.c Normal file
View File

@ -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);
}