mirror of
https://github.com/golang/go
synced 2024-11-18 23:24:39 -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:
parent
83267dce11
commit
eea21f8b85
28
src/cmd/prof/Makefile
Normal file
28
src/cmd/prof/Makefile
Normal 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
341
src/cmd/prof/main.c
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user