From 5a38d0ad98fdb867271c9a95a6540b14d4c3abfe Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 28 Feb 2012 16:18:24 -0500 Subject: runtime/pprof: support OS X CPU profiling Work around profiling kernel bug with signal masks. Still broken on 64-bit Snow Leopard kernel, but I think we can ignore that one and let people upgrade to Lion. Add new trivial tools addr2line and objdump to take the place of the GNU tools of the same name, since those are not installed on OS X. Adapt pprof to invoke 'go tool addr2line' and 'go tool objdump' if the system tools do not exist. Clean up disassembly of base register on amd64. Fixes issue 2008. R=golang-dev, bradfitz, mikioh.mikioh, r, iant CC=golang-dev http://codereview.appspot.com/5697066 --- src/cmd/addr2line/main.c | 68 ++++++++++++++++++++++++++++++++ src/cmd/dist/build.c | 100 ++++++++++++++++++++++++----------------------- src/cmd/objdump/main.c | 68 ++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 48 deletions(-) create mode 100644 src/cmd/addr2line/main.c create mode 100644 src/cmd/objdump/main.c (limited to 'src/cmd') diff --git a/src/cmd/addr2line/main.c b/src/cmd/addr2line/main.c new file mode 100644 index 000000000..6b2fe5dfe --- /dev/null +++ b/src/cmd/addr2line/main.c @@ -0,0 +1,68 @@ +// Copyright 2012 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. + +/* + * addr2line simulation - only enough to make pprof work on Macs + */ + +#include +#include +#include +#include + +void +usage(void) +{ + fprint(2, "usage: addr2line binary\n"); + fprint(2, "reads addresses from standard input and writes two lines for each:\n"); + fprint(2, "\tfunction name\n"); + fprint(2, "\tfile:line\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fd; + char *p; + uvlong pc; + Symbol s; + Fhdr fhdr; + Biobuf bin, bout; + char file[1024]; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("open %s: %r", argv[0]); + if(crackhdr(fd, &fhdr) <= 0) + sysfatal("crackhdr: %r"); + machbytype(fhdr.type); + if(syminit(fd, &fhdr) <= 0) + sysfatal("syminit: %r"); + + Binit(&bin, 0, OREAD); + Binit(&bout, 1, OWRITE); + for(;;) { + p = Brdline(&bin, '\n'); + if(p == nil) + break; + p[Blinelen(&bin)-1] = '\0'; + pc = strtoull(p, 0, 16); + if(!findsym(pc, CTEXT, &s)) + s.name = "??"; + if(!fileline(file, sizeof file, pc)) + strcpy(file, "??:0"); + Bprint(&bout, "%s\n%s\n", s.name, file); + } + Bflush(&bout); + exits(0); +} diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 5664c1890..66b5c1f18 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -59,7 +59,7 @@ int find(char *p, char **l, int n) { int i; - + for(i=0; i 0) bwriteb(&b, &bmore); @@ -249,14 +249,14 @@ findgoversion(void) done: p = btake(&b); - - + + bfree(&b); bfree(&path); bfree(&bmore); bfree(&branch); vfree(&tags); - + return p; } @@ -325,7 +325,7 @@ setup(void) xremoveall(p); xmkdirall(p); } - + // Create object directory. // We keep it in pkg/ so that all the generated binaries // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from @@ -393,7 +393,7 @@ static char *proto_gccargs[] = { static Vec gccargs; // deptab lists changes to the default dependencies for a given prefix. -// deps ending in /* read the whole directory; deps beginning with - +// deps ending in /* read the whole directory; deps beginning with - // exclude files with that prefix. static struct { char *prefix; // prefix of target @@ -559,7 +559,7 @@ install(char *dir) vinit(&clean); vinit(&lib); vinit(&extra); - + // path = full path to dir. bpathf(&path, "%s/src/%s", goroot, dir); name = lastelem(dir); @@ -599,7 +599,7 @@ install(char *dir) exe = ""; if(streq(gohostos, "windows")) exe = ".exe"; - + // Start final link command line. // Note: code below knows that link.p[targ] is the target. if(islib) { @@ -696,13 +696,13 @@ install(char *dir) } files.len = n; continue; - } + } vadd(&files, p); } } } vuniq(&files); - + // Convert to absolute paths. for(i=0; i ttarg) stale = 1; - + if(!stale) goto out; @@ -792,9 +792,9 @@ install(char *dir) copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0); } - + // Generate .c files from .goc files. - if(streq(dir, "pkg/runtime")) { + if(streq(dir, "pkg/runtime")) { for(i=0; i 1) @@ -833,13 +833,13 @@ install(char *dir) vadd(&compile, "-m32"); if(streq(dir, "lib9")) vadd(&compile, "-DPLAN9PORT"); - + vadd(&compile, "-I"); vadd(&compile, bpathf(&b, "%s/include", goroot)); - + vadd(&compile, "-I"); vadd(&compile, bstr(&path)); - + // lib9/goos.c gets the default constants hard-coded. if(streq(name, "goos.c")) { vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos)); @@ -849,7 +849,7 @@ install(char *dir) vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1))); vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion)); } - + // gc/lex.c records the GOEXPERIMENT setting used during the build. if(streq(name, "lex.c")) { xgetenv(&b, "GOEXPERIMENT"); @@ -867,7 +867,7 @@ install(char *dir) vadd(&compile, workdir); vadd(&compile, bprintf(&b, "-DGOOS_%s", goos)); vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch)); - } + } bpathf(&b, "%s/%s", workdir, lastelem(files.p[i])); doclean = 1; @@ -893,7 +893,7 @@ install(char *dir) vadd(&clean, bstr(&b)); } bgwait(); - + if(isgo) { // The last loop was compiling individual files. // Hand the Go files to the compiler en masse. @@ -905,16 +905,16 @@ install(char *dir) vadd(&compile, bstr(&b)); vadd(&clean, bstr(&b)); vadd(&link, bstr(&b)); - + vadd(&compile, "-p"); if(hasprefix(dir, "pkg/")) vadd(&compile, dir+4); else vadd(&compile, "main"); - + if(streq(dir, "pkg/runtime")) vadd(&compile, "-+"); - + vcopy(&compile, go.p, go.len); runv(nil, bstr(&path), CheckExit, &compile); @@ -980,7 +980,7 @@ shouldbuild(char *file, char *dir) int i, j, ret; Buf b; Vec lines, fields; - + // Check file name for GOOS or GOARCH. name = lastelem(file); for(i=0; i 1) xprintf("cp %s %s\n", src, dst); @@ -1070,11 +1070,13 @@ static char *buildorder[] = { "lib9", "libbio", "libmach", - + "misc/pprof", + "cmd/addr2line", "cmd/cov", "cmd/nm", + "cmd/objdump", "cmd/pack", "cmd/prof", @@ -1122,12 +1124,12 @@ static char *buildorder[] = { "pkg/go/scanner", "pkg/go/ast", "pkg/go/parser", - "pkg/go/build", "pkg/os/exec", "pkg/net/url", "pkg/text/template/parse", "pkg/text/template", "pkg/go/doc", + "pkg/go/build", "cmd/go", }; @@ -1147,11 +1149,13 @@ static char *cleantab[] = { "cmd/8c", "cmd/8g", "cmd/8l", + "cmd/addr2line", "cmd/cc", "cmd/cov", "cmd/gc", "cmd/go", "cmd/nm", + "cmd/objdump", "cmd/pack", "cmd/prof", "lib9", @@ -1204,11 +1208,11 @@ clean(void) int i, j, k; Buf b, path; Vec dir; - + binit(&b); binit(&path); vinit(&dir); - + for(i=0; i 0) usage(); - + xprintf(format, "GOROOT", goroot); xprintf(format, "GOBIN", gobin); xprintf(format, "GOARCH", goarch); @@ -1346,7 +1350,7 @@ cmdbootstrap(int argc, char **argv) clean(); goversion = findgoversion(); setup(); - + // For the main bootstrap, building for host os/arch. oldgoos = goos; oldgoarch = goarch; @@ -1356,7 +1360,7 @@ cmdbootstrap(int argc, char **argv) gochar = gohostchar; xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); - + for(i=0; i +#include +#include +#include + +void +usage(void) +{ + fprint(2, "usage: objdump binary start stop\n"); + fprint(2, "Disassembles binary from PC start up to stop.\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fd, n; + uvlong pc, start, stop; + Fhdr fhdr; + Biobuf bout; + char buf[1024]; + Map *text; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + if(argc != 3) + usage(); + start = strtoull(argv[1], 0, 16); + stop = strtoull(argv[2], 0, 16); + + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("open %s: %r", argv[0]); + if(crackhdr(fd, &fhdr) <= 0) + sysfatal("crackhdr: %r"); + machbytype(fhdr.type); + if(syminit(fd, &fhdr) <= 0) + sysfatal("syminit: %r"); + text = loadmap(nil, fd, &fhdr); + if(text == nil) + sysfatal("loadmap: %r"); + + Binit(&bout, 1, OWRITE); + for(pc=start; pcdas(text, pc, 0, buf, sizeof buf); + Bprint(&bout, " %llx: %s\n", pc, buf); + n = machdata->instsize(text, pc); + if(n <= 0) + break; + pc += n; + } + Bflush(&bout); + exits(0); +} -- cgit v1.2.1