summaryrefslogtreecommitdiff
path: root/src/cmd/prof/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/prof/main.c')
-rw-r--r--src/cmd/prof/main.c364
1 files changed, 237 insertions, 127 deletions
diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c
index c4380b9b3..20ea7f28e 100644
--- a/src/cmd/prof/main.c
+++ b/src/cmd/prof/main.c
@@ -11,17 +11,16 @@
#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
+int nsample;
+int nsamplethread;
// output formats
int functions; // print functions
@@ -30,6 +29,12 @@ int linenums; // print file and line numbers rather than function names
int registers; // print registers
int stacks; // print stack traces
+int pid; // main process pid
+
+int nthread; // number of threads
+int thread[32]; // thread pids
+Map *map[32]; // thread maps
+
void
Usage(void)
{
@@ -40,12 +45,14 @@ Usage(void)
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");
+ fprint(2, "\t\t-hs: include stack info in histograms\n");
exit(2);
}
typedef struct PC PC;
struct PC {
uvlong pc;
+ uvlong callerpc;
unsigned int count;
PC* next;
};
@@ -88,20 +95,105 @@ regprint(void)
}
int
-sample(void)
+getthreads(void)
+{
+ int i, j, curn, found;
+ Map *curmap[nelem(map)];
+ int curthread[nelem(map)];
+ static int complained = 0;
+
+ curn = procthreadpids(pid, curthread, nelem(curthread));
+ if(curn <= 0)
+ return curn;
+
+ if(curn > nelem(map)) {
+ if(complained == 0) {
+ fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
+ complained = 1;
+ }
+ curn = nelem(map);
+ }
+ if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
+ return curn; // no changes
+
+ // Number of threads has changed (might be the init case).
+ // A bit expensive but rare enough not to bother being clever.
+ for(i = 0; i < curn; i++) {
+ found = 0;
+ for(j = 0; j < nthread; j++) {
+ if(curthread[i] == thread[j]) {
+ found = 1;
+ curmap[i] = map[j];
+ map[j] = nil;
+ break;
+ }
+ }
+ if(found)
+ continue;
+
+ // map new thread
+ curmap[i] = attachproc(curthread[i], &fhdr);
+ if(curmap[i] == nil) {
+ fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
+ return -1;
+ }
+ }
+
+ for(j = 0; j < nthread; j++)
+ if(map[j] != nil)
+ detachproc(map[j]);
+
+ nthread = curn;
+ memmove(thread, curthread, nthread*sizeof thread[0]);
+ memmove(map, curmap, sizeof map);
+ return nthread;
+}
+
+int
+sample(Map *map)
{
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;
+ if(registers) {
+ for(i = 0; i < sizeof ureg; i+=8) {
+ if(get8(map, (uvlong)i, &((uvlong*)&ureg)[i/8]) < 0)
+ goto bad;
}
+ } else {
+ // we need only two registers
+ if(get8(map, offsetof(struct Ureg, ip), (uvlong*)&ureg.ip) < 0)
+ goto bad;
+ if(get8(map, offsetof(struct Ureg, sp), (uvlong*)&ureg.sp) < 0)
+ goto bad;
}
return 1;
+bad:
+ if(n == 1)
+ fprint(2, "prof: can't read registers: %r\n");
+ return 0;
+}
+
+void
+addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
+{
+ int h;
+ PC *x;
+
+ h = (pc + callerpc*101) % Ncounters;
+ for(x = counters[h]; x != NULL; x = x->next) {
+ if(x->pc == pc && x->callerpc == callerpc) {
+ x->count++;
+ return;
+ }
+ }
+ x = malloc(sizeof(PC));
+ x->pc = pc;
+ x->callerpc = callerpc;
+ x->count = 1;
+ x->next = counters[h];
+ counters[h] = x;
}
uvlong nextpc;
@@ -114,53 +206,40 @@ xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
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);
+ if(histograms)
+ addtohistogram(nextpc, pc, sp);
+ if(!histograms || stacks > 1) {
+ 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");
}
- print("\n");
nextpc = pc;
}
void
-stacktracepcsp(uvlong pc, uvlong sp)
+stacktracepcsp(Map *map, uvlong pc, uvlong sp)
{
- nextpc = 0;
+ nextpc = pc;
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;
- }
+ else {
+ addtohistogram(nextpc, 0, sp);
+ if(!histograms || stacks > 1)
+ print("\n");
}
- x = malloc(sizeof(PC));
- x->pc = pc;
- x->count = 1;
- x->next = counters[h];
- counters[h] = x;
}
void
-printpc(uvlong pc, uvlong sp)
+printpc(Map *map, uvlong pc, uvlong sp)
{
char buf[1024];
if(registers)
@@ -172,117 +251,136 @@ printpc(uvlong pc, uvlong sp)
print("%s\n", buf);
}
if(stacks){
- stacktracepcsp(pc, sp);
+ stacktracepcsp(map, pc, sp);
}
- if(histograms){
- addtohistogram(pc, sp);
+ else if(histograms){
+ addtohistogram(pc, 0, sp);
}
}
void
samples(void)
{
- int msec;
+ int i, pid, 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()) {
+ nsample++;
+ nsamplethread += nthread;
+ for(i = 0; i < nthread; i++) {
+ pid = thread[i];
+ if(ctlproc(pid, "stop") < 0)
+ return;
+ if(!sample(map[i])) {
+ ctlproc(pid, "start");
+ return;
+ }
+ printpc(map[i], ureg.ip, ureg.sp);
ctlproc(pid, "start");
- break;
}
- printpc(ureg.ip, ureg.sp);
- ctlproc(pid, "start");
nanosleep(&req, NULL);
+ getthreads();
+ if(nthread == 0)
+ break;
}
}
-int
-comparepc(const void *va, const void *vb)
+typedef struct Func Func;
+struct Func
{
- const PC *const*a = va;
- const PC *const*b = vb;
- return (*a)->pc - (*b)->pc;
-}
+ Func *next;
+ Symbol s;
+ uint onstack;
+ uint leaf;
+};
-int
-comparecount(const void *va, const void *vb)
+Func *func[257];
+int nfunc;
+
+Func*
+findfunc(uvlong pc)
{
- const PC *const*a = va;
- const PC *const*b = vb;
- return (*b)->count - (*a)->count; // sort downwards
+ Func *f;
+ uint h;
+ Symbol s;
+
+ if(pc == 0)
+ return nil;
+
+ if(!findsym(pc, CTEXT, &s))
+ return nil;
+
+ h = s.value % nelem(func);
+ for(f = func[h]; f != NULL; f = f->next)
+ if(f->s.value == s.value)
+ return f;
+
+ f = mallocz(sizeof *f, 1);
+ f->s = s;
+ f->next = func[h];
+ func[h] = f;
+ nfunc++;
+ return f;
}
-void
-func(char *s, int n, uvlong pc)
+int
+compareleaf(const void *va, const void *vb)
{
- char *p;
+ Func *a, *b;
- symoff(s, n, pc, CANY);
- p = strchr(s, '+');
- if(p != NULL)
- *p = 0;
+ a = *(Func**)va;
+ b = *(Func**)vb;
+ if(a->leaf != b->leaf)
+ return b->leaf - a->leaf;
+ if(a->onstack != b->onstack)
+ return b->onstack - a->onstack;
+ return strcmp(a->s.name, b->s.name);
}
void
dumphistogram()
{
- int h;
+ int i, h, n;
PC *x;
- PC **pcs;
- uint i;
- uint j;
- uint npc;
- uint ncount;
- char b1[100];
- char b2[100];
+ Func *f, **ff;
if(!histograms)
return;
- // count samples
- ncount = 0;
- npc = 0;
- for(h = 0; h < Ncounters; h++)
+ // assign counts to functions.
+ 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;
+ f = findfunc(x->pc);
+ if(f) {
+ f->onstack += x->count;
+ f->leaf += x->count;
}
+ f = findfunc(x->callerpc);
+ if(f)
+ f->leaf -= x->count;
}
- 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);
+
+ // build array
+ ff = malloc(nfunc*sizeof ff[0]);
+ n = 0;
+ for(h = 0; h < nelem(func); h++)
+ for(f = func[h]; f != NULL; f = f->next)
+ ff[n++] = f;
+
+ // sort by leaf counts
+ qsort(ff, nfunc, sizeof ff[0], compareleaf);
+
+ // print.
+ print("%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
+ for(i = 0; i < nfunc; i++) {
+ f = ff[i];
+ print("%6.2f%%\t", 100.0*(double)f->leaf/nsample);
+ if(stacks)
+ print("%6.2f%%\t", 100.0*(double)f->onstack/nsample);
+ print("%s\n", f->s.name);
}
}
@@ -313,13 +411,21 @@ startprocess(char **argv)
return pid;
}
+void
+detach(void)
+{
+ int i;
+
+ for(i = 0; i < nthread; i++)
+ detachproc(map[i]);
+}
+
int
main(int argc, char *argv[])
{
+ int i;
+
ARGBEGIN{
- case 'c':
- collapse = 0;
- break;
case 'd':
delta_msec = atoi(EARGF(Usage()));
break;
@@ -342,7 +448,7 @@ main(int argc, char *argv[])
registers = 1;
break;
case 's':
- stacks = 1;
+ stacks++;
break;
}ARGEND
if(pid <= 0 && argc == 0)
@@ -355,18 +461,13 @@ main(int argc, char *argv[])
}
if(argc > 0)
file = argv[0];
+ else if(pid)
+ file = proctextfile(pid);
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) {
@@ -376,9 +477,18 @@ main(int argc, char *argv[])
fprint(2, "prof: crack header for %s: %r\n", file);
exit(1);
}
- ctlproc(pid, "start");
+ if(pid <= 0)
+ pid = startprocess(argv);
+ attachproc(pid, &fhdr); // initializes thread list
+ if(getthreads() <= 0) {
+ detach();
+ fprint(2, "prof: can't find threads for pid %d\n", pid);
+ exit(1);
+ }
+ for(i = 0; i < nthread; i++)
+ ctlproc(thread[i], "start");
samples();
- detachproc(map);
+ detach();
dumphistogram();
exit(0);
}