diff options
Diffstat (limited to 'src/cmd')
-rw-r--r-- | src/cmd/5g/reg.c | 2 | ||||
-rw-r--r-- | src/cmd/api/goapi.go | 1 | ||||
-rw-r--r-- | src/cmd/dist/build.c | 9 | ||||
-rw-r--r-- | src/cmd/gc/builtin.c | 31 | ||||
-rw-r--r-- | src/cmd/gc/go.h | 1 | ||||
-rw-r--r-- | src/cmd/gc/lex.c | 4 | ||||
-rw-r--r-- | src/cmd/gc/plive.c | 19 | ||||
-rw-r--r-- | src/cmd/gc/popt.c | 4 | ||||
-rw-r--r-- | src/cmd/gc/reflect.c | 8 | ||||
-rw-r--r-- | src/cmd/gc/runtime.go | 33 | ||||
-rw-r--r-- | src/cmd/gc/typecheck.c | 6 | ||||
-rw-r--r-- | src/cmd/gc/walk.c | 63 | ||||
-rw-r--r-- | src/cmd/go/build.go | 10 | ||||
-rw-r--r-- | src/cmd/go/doc.go | 2 | ||||
-rw-r--r-- | src/cmd/go/pkg.go | 24 | ||||
-rwxr-xr-x | src/cmd/go/test.bash | 18 | ||||
-rw-r--r-- | src/cmd/go/tool.go | 2 | ||||
-rw-r--r-- | src/cmd/go/vet.go | 2 | ||||
-rw-r--r-- | src/cmd/internal/objfile/disasm.go | 248 | ||||
-rw-r--r-- | src/cmd/internal/objfile/objfile.go | 14 | ||||
-rw-r--r-- | src/cmd/objdump/main.go | 242 | ||||
-rw-r--r-- | src/cmd/objdump/objdump_test.go | 102 | ||||
-rw-r--r-- | src/cmd/pprof/README | 8 | ||||
-rw-r--r-- | src/cmd/pprof/doc.go | 12 | ||||
-rw-r--r-- | src/cmd/pprof/pprof.go | 41 |
25 files changed, 511 insertions, 395 deletions
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 712841329..441792873 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -199,7 +199,7 @@ regopt(Prog *firstp) proginfo(&info, p); // Avoid making variables for direct-called functions. - if(p->as == ABL && p->to.type == D_EXTERN) + if(p->as == ABL && p->to.name == D_EXTERN) continue; bit = mkvar(r, &p->from); diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 5a8c87603..e49ba33bb 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -405,6 +405,7 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) { " note struct{};" + " p struct{};" + " parfor struct{};" + + " slice struct{};" + " slicetype struct{};" + " stkframe struct{};" + " sudog struct{};" + diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 8fd2e998a..9c81dd8b2 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -691,13 +691,6 @@ install(char *dir) bpathf(&final_path, "%s/src/%s", goroot_final, dir); name = lastelem(dir); - // For misc/prof, copy into the tool directory and we're done. - if(hasprefix(dir, "misc/")) { - copyfile(bpathf(&b, "%s/%s", tooldir, name), - bpathf(&b1, "%s/misc/%s", goroot, name), 1); - goto out; - } - // set up gcc command line on first run. if(gccargs.len == 0) { bprintf(&b, "%s %s", defaultcc, defaultcflags); @@ -1328,8 +1321,6 @@ static char *buildorder[] = { "libbio", "liblink", - "misc/pprof", - "cmd/cc", // must be before c "cmd/gc", // must be before g "cmd/%sl", // must be before a, c, g diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index fbca4ee5f..aeeadedca 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -24,6 +24,8 @@ char *runtimeimport = "func @\"\".printslice (? any)\n" "func @\"\".printnl ()\n" "func @\"\".printsp ()\n" + "func @\"\".printlock ()\n" + "func @\"\".printunlock ()\n" "func @\"\".concatstring2 (? string, ? string) (? string)\n" "func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n" "func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n" @@ -86,10 +88,33 @@ char *runtimeimport = "func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n" "func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n" "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n" - "func @\"\".writebarrierfat2 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" - "func @\"\".writebarrierfat3 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" - "func @\"\".writebarrierfat4 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat01 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat10 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat11 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat001 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat010 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat011 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat100 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat101 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat110 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat111 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0001 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0010 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0011 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0100 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0101 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0110 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat0111 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1000 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1001 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1010 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1011 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1100 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1101 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1110 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" + "func @\"\".writebarrierfat1111 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n" "func @\"\".writebarrierfat (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n" + "func @\"\".writebarriercopy (@\"\".typ·2 *byte, @\"\".dst·3 any, @\"\".src·4 any) (? int)\n" "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n" "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n" "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n" diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index d3c4193b5..c695c5bf3 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -1466,6 +1466,7 @@ void walk(Node *fn); void walkexpr(Node **np, NodeList **init); void walkexprlist(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init); +void walkexprlistcheap(NodeList *l, NodeList **init); void walkstmt(Node **np); void walkstmtlist(NodeList *l); Node* conv(Node*, Type*); diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 2303b442c..523ba37aa 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -344,8 +344,8 @@ main(int argc, char *argv[]) break; } } - if(j == nelem(debugtab)) - fatal("unknown debug information -d '%s'\n", f[i]); + if(debugtab[j].name == nil) + sysfatal("unknown debug information -d '%s'\n", f[i]); } } diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c index 0feb2c710..3bfa69b1f 100644 --- a/src/cmd/gc/plive.c +++ b/src/cmd/gc/plive.c @@ -1092,7 +1092,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) case TCOMPLEX64: case TCOMPLEX128: for(i = 0; i < t->width; i++) { - bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar + bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar (BitsScalar) } *xoffset += t->width; break; @@ -1105,7 +1105,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) case TMAP: if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr (BitsPointer) *xoffset += t->width; break; @@ -1113,7 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) // struct { byte *str; intgo len; } if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer) *xoffset += t->width; break; @@ -1123,15 +1123,8 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) // struct { Type *type; union { void *ptr, uintptr val } data; } if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid alignment, %T", t); - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0); - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); // 3 = multiword - // next word contains 2 = Iface, 3 = Eface - if(isnilinter(t)) { - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2); - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3); - } else { - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3); - } + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer) + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 3); // 2 = live ptr in second slot (BitsPointer) *xoffset += t->width; break; @@ -1144,7 +1137,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) // struct { byte *array; uintgo len; uintgo cap; } if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid TARRAY alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer) *xoffset += t->width; } else for(i = 0; i < t->bound; i++) diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c index 993bb2482..6e6db88ef 100644 --- a/src/cmd/gc/popt.c +++ b/src/cmd/gc/popt.c @@ -847,6 +847,10 @@ nilopt(Prog *firstp) Graph *g; int ncheck, nkill; + // TODO(minux): nilopt on power64 throw away seemly random segment of code. + if(thechar == '9') + return; + g = flowstart(firstp, sizeof(NilFlow)); if(g == nil) return; diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index b2ff2fbc5..0f8802abc 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -1525,11 +1525,9 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset) *xoffset += t->width; break; case TINTER: - proggendata(g, BitsMultiWord); - if(isnilinter(t)) - proggendata(g, BitsEface); - else - proggendata(g, BitsIface); + // Assuming IfacePointerOnly=1. + proggendata(g, BitsPointer); + proggendata(g, BitsPointer); *xoffset += t->width; break; case TARRAY: diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 0fb15c265..c6007714c 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -36,6 +36,8 @@ func printeface(any) func printslice(any) func printnl() func printsp() +func printlock() +func printunlock() func concatstring2(string, string) string func concatstring3(string, string, string) string @@ -115,10 +117,35 @@ func writebarrieriface(dst *any, src any) // The unused *byte argument makes sure that src is 2-pointer-aligned, // which is the maximum alignment on NaCl amd64p32 // (and possibly on 32-bit systems if we start 64-bit aligning uint64s). -func writebarrierfat2(dst *any, _ *byte, src any) -func writebarrierfat3(dst *any, _ *byte, src any) -func writebarrierfat4(dst *any, _ *byte, src any) +// The bitmap in the name tells which words being copied are pointers. +func writebarrierfat01(dst *any, _ *byte, src any) +func writebarrierfat10(dst *any, _ *byte, src any) +func writebarrierfat11(dst *any, _ *byte, src any) +func writebarrierfat001(dst *any, _ *byte, src any) +func writebarrierfat010(dst *any, _ *byte, src any) +func writebarrierfat011(dst *any, _ *byte, src any) +func writebarrierfat100(dst *any, _ *byte, src any) +func writebarrierfat101(dst *any, _ *byte, src any) +func writebarrierfat110(dst *any, _ *byte, src any) +func writebarrierfat111(dst *any, _ *byte, src any) +func writebarrierfat0001(dst *any, _ *byte, src any) +func writebarrierfat0010(dst *any, _ *byte, src any) +func writebarrierfat0011(dst *any, _ *byte, src any) +func writebarrierfat0100(dst *any, _ *byte, src any) +func writebarrierfat0101(dst *any, _ *byte, src any) +func writebarrierfat0110(dst *any, _ *byte, src any) +func writebarrierfat0111(dst *any, _ *byte, src any) +func writebarrierfat1000(dst *any, _ *byte, src any) +func writebarrierfat1001(dst *any, _ *byte, src any) +func writebarrierfat1010(dst *any, _ *byte, src any) +func writebarrierfat1011(dst *any, _ *byte, src any) +func writebarrierfat1100(dst *any, _ *byte, src any) +func writebarrierfat1101(dst *any, _ *byte, src any) +func writebarrierfat1110(dst *any, _ *byte, src any) +func writebarrierfat1111(dst *any, _ *byte, src any) + func writebarrierfat(typ *byte, dst *any, src *any) +func writebarriercopy(typ *byte, dst any, src any) int func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 714c66268..f05d8022d 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -2891,7 +2891,8 @@ typecheckas(Node *n) case OSLICE3: case OSLICESTR: // For x = x[0:y], x can be updated in place, without touching pointer. - if(samesafeexpr(n->left, n->right->left) && (n->right->right->left == N || iszero(n->right->right->left))) + // TODO(rsc): Reenable once it is actually updated in place without touching the pointer. + if(0 && samesafeexpr(n->left, n->right->left) && (n->right->right->left == N || iszero(n->right->right->left))) n->right->reslice = 1; break; @@ -2899,7 +2900,8 @@ typecheckas(Node *n) // For x = append(x, ...), x can be updated in place when there is capacity, // without touching the pointer; otherwise the emitted code to growslice // can take care of updating the pointer, and only in that case. - if(n->right->list != nil && samesafeexpr(n->left, n->right->list->n)) + // TODO(rsc): Reenable once the emitted code does update the pointer. + if(0 && n->right->list != nil && samesafeexpr(n->left, n->right->list->n)) n->right->reslice = 1; break; } diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index d4d0f449c..37bd62dea 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -6,6 +6,7 @@ #include <libc.h> #include "go.h" #include "../ld/textflag.h" +#include "../../runtime/mgc0.h" static Node* walkprint(Node*, NodeList**); static Node* writebarrierfn(char*, Type*, Type*); @@ -363,6 +364,15 @@ walkexprlistsafe(NodeList *l, NodeList **init) } void +walkexprlistcheap(NodeList *l, NodeList **init) +{ + for(; l; l=l->next) { + l->n = cheapexpr(l->n, init); + walkexpr(&l->n, init); + } +} + +void walkexpr(Node **np, NodeList **init) { Node *r, *l, *var, *a; @@ -1772,6 +1782,11 @@ walkprint(Node *nn, NodeList **init) calls = nil; notfirst = 0; + // Hoist all the argument evaluation up before the lock. + walkexprlistcheap(all, init); + + calls = list(calls, mkcall("printlock", T, init)); + for(l=all; l; l=l->next) { if(notfirst) { calls = list(calls, mkcall("printsp", T, init)); @@ -1852,6 +1867,9 @@ walkprint(Node *nn, NodeList **init) if(op == OPRINTN) calls = list(calls, mkcall("printnl", T, nil)); + + calls = list(calls, mkcall("printunlock", T, init)); + typechecklist(calls, Etop); walkexprlist(calls, init); @@ -1988,6 +2006,9 @@ applywritebarrier(Node *n, NodeList **init) { Node *l, *r; Type *t; + vlong x; + static Bvec *bv; + char name[32]; if(n->left && n->right && needwritebarrier(n->left, n->right)) { t = n->left->type; @@ -2005,14 +2026,35 @@ applywritebarrier(Node *n, NodeList **init) } else if(isinter(t)) { n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init, l, n->right); - } else if(t->width == 2*widthptr) { - n = mkcall1(writebarrierfn("writebarrierfat2", t, n->right->type), T, init, - l, nodnil(), n->right); - } else if(t->width == 3*widthptr) { - n = mkcall1(writebarrierfn("writebarrierfat3", t, n->right->type), T, init, - l, nodnil(), n->right); - } else if(t->width == 4*widthptr) { - n = mkcall1(writebarrierfn("writebarrierfat4", t, n->right->type), T, init, + } else if(t->width <= 4*widthptr) { + x = 0; + if(bv == nil) + bv = bvalloc(BitsPerPointer*4); + bvresetall(bv); + twobitwalktype1(t, &x, bv); + // The bvgets are looking for BitsPointer in successive slots. + enum { + PtrBit = 1, + }; + if(BitsPointer != (1<<PtrBit)) + fatal("wrong PtrBit"); + switch(t->width/widthptr) { + default: + fatal("found writebarrierfat for %d-byte object of type %T", (int)t->width, t); + case 2: + snprint(name, sizeof name, "writebarrierfat%d%d", + bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit)); + break; + case 3: + snprint(name, sizeof name, "writebarrierfat%d%d%d", + bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit)); + break; + case 4: + snprint(name, sizeof name, "writebarrierfat%d%d%d%d", + bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit), bvget(bv, 3*BitsPerPointer+PtrBit)); + break; + } + n = mkcall1(writebarrierfn(name, t, n->right->type), T, init, l, nodnil(), n->right); } else { r = n->right; @@ -2874,6 +2916,11 @@ copyany(Node *n, NodeList **init, int runtimecall) { Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn; NodeList *l; + + if(haspointers(n->left->type->type)) { + fn = writebarrierfn("writebarriercopy", n->left->type, n->right->type); + return mkcall1(fn, n->type, init, typename(n->left->type->type), n->left, n->right); + } if(runtimecall) { if(n->right->type->etype == TSTRING) diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 79a27116a..1dd4314da 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1826,7 +1826,15 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) - args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) + warn := []string{"-w"} + if p.usesSwig() { + // When using SWIG, this compiler is only used to + // compile the C files generated by SWIG. + // We don't want warnings. + // See issue 9065 for details. + warn = nil + } + args := stringList(tool(archChar+"c"), "-F", "-V", warn, "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) return b.run(p.Dir, p.ImportPath, nil, args) } diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index cf3a54565..43a315944 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -590,7 +590,7 @@ Usage: Vet runs the Go vet command on the packages named by the import paths. -For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. +For more about vet, see 'godoc golang.org/x/tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index e17326442..b71feb7a6 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -383,9 +383,10 @@ func findInternal(path string) (index int, ok bool) { type targetDir int const ( - toRoot targetDir = iota // to bin dir inside package root (default) - toTool // GOROOT/pkg/tool - toBin // GOROOT/bin + toRoot targetDir = iota // to bin dir inside package root (default) + toTool // GOROOT/pkg/tool + toBin // GOROOT/bin + stalePath // the old import path; fail to build ) // goTools is a map of Go program import path to install target directory. @@ -398,10 +399,14 @@ var goTools = map[string]targetDir{ "cmd/nm": toTool, "cmd/objdump": toTool, "cmd/pack": toTool, + "cmd/pprof": toTool, "cmd/yacc": toTool, - "code.google.com/p/go.tools/cmd/cover": toTool, - "code.google.com/p/go.tools/cmd/godoc": toBin, - "code.google.com/p/go.tools/cmd/vet": toTool, + "golang.org/x/tools/cmd/cover": toTool, + "golang.org/x/tools/cmd/godoc": toBin, + "golang.org/x/tools/cmd/vet": toTool, + "code.google.com/p/go.tools/cmd/cover": stalePath, + "code.google.com/p/go.tools/cmd/godoc": stalePath, + "code.google.com/p/go.tools/cmd/vet": stalePath, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -462,6 +467,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } if p.Name == "main" { + // Report an error when the old code.google.com/p/go.tools paths are used. + if goTools[p.ImportPath] == stalePath { + newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1) + e := fmt.Sprintf("the %v command has moved; use %v instead.", p.ImportPath, newPath) + p.Error = &PackageError{Err: e} + return p + } _, elem := filepath.Split(p.Dir) full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH { diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 2b5230b1a..e0f066f18 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -433,20 +433,20 @@ TEST godoc installs into GOBIN d=$(mktemp -d -t testgoXXX) export GOPATH=$d mkdir $d/gobin -GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc || ok=false +GOBIN=$d/gobin ./testgo get golang.org/x/tools/cmd/godoc || ok=false if [ ! -x $d/gobin/godoc ]; then echo did not install godoc to '$GOBIN' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true ok=false fi TEST godoc installs into GOROOT GOROOT=$(./testgo env GOROOT) rm -f $GOROOT/bin/godoc -./testgo install code.google.com/p/go.tools/cmd/godoc || ok=false +./testgo install golang.org/x/tools/cmd/godoc || ok=false if [ ! -x $GOROOT/bin/godoc ]; then echo did not install godoc to '$GOROOT/bin' - ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true + ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true ok=false fi @@ -561,8 +561,8 @@ fi TEST without GOPATH, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src -if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then - echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset' +if GOPATH= GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then + echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with $GOPATH unset' ok=false fi rm -rf $d @@ -571,8 +571,8 @@ rm -rf $d TEST with GOPATH=GOROOT, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src -if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then - echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT' +if GOPATH=$d GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then + echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT' ok=false fi rm -rf $d @@ -728,7 +728,7 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then fi TEST go get cover -./testgo get code.google.com/p/go.tools/cmd/cover || ok=false +./testgo get golang.org/x/tools/cmd/cover || ok=false unset GOPATH rm -rf $d diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index 6d26f7a4b..c96161e0f 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -53,7 +53,7 @@ func tool(toolName string) string { // Give a nice message if there is no tool with that name. if _, err := os.Stat(toolPath); err != nil { if isInGoToolsRepo(toolName) { - fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get code.google.com/p/go.tools/cmd/%s\n", toolName, toolName) + fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get golang.org/x/tools/cmd/%s\n", toolName, toolName) } else { fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) } diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go index de7befc61..02ff54b2a 100644 --- a/src/cmd/go/vet.go +++ b/src/cmd/go/vet.go @@ -17,7 +17,7 @@ var cmdVet = &Command{ Long: ` Vet runs the Go vet command on the packages named by the import paths. -For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. +For more about vet, see 'godoc golang.org/x/tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go new file mode 100644 index 000000000..1a339c321 --- /dev/null +++ b/src/cmd/internal/objfile/disasm.go @@ -0,0 +1,248 @@ +// Copyright 2014 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. + +package objfile + +import ( + "bufio" + "debug/gosym" + "encoding/binary" + "fmt" + "io" + "regexp" + "sort" + "strings" + "text/tabwriter" + + "cmd/internal/rsc.io/arm/armasm" + "cmd/internal/rsc.io/x86/x86asm" +) + +// Disasm is a disassembler for a given File. +type Disasm struct { + syms []Sym //symbols in file, sorted by address + pcln *gosym.Table // pcln table + text []byte // bytes of text segment (actual instructions) + textStart uint64 // start PC of text + textEnd uint64 // end PC of text + goarch string // GOARCH string + disasm disasmFunc // disassembler function for goarch + byteOrder binary.ByteOrder // byte order for goarch +} + +// Disasm returns a disassembler for the file f. +func (f *File) Disasm() (*Disasm, error) { + syms, err := f.Symbols() + if err != nil { + return nil, err + } + + pcln, err := f.PCLineTable() + if err != nil { + return nil, err + } + + textStart, textBytes, err := f.Text() + if err != nil { + return nil, err + } + + goarch := f.GOARCH() + disasm := disasms[goarch] + byteOrder := byteOrders[goarch] + if disasm == nil || byteOrder == nil { + return nil, fmt.Errorf("unsupported architecture") + } + + // Filter out section symbols, overwriting syms in place. + keep := syms[:0] + for _, sym := range syms { + switch sym.Name { + case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext": + // drop + default: + keep = append(keep, sym) + } + } + syms = keep + d := &Disasm{ + syms: syms, + pcln: pcln, + text: textBytes, + textStart: textStart, + textEnd: textStart + uint64(len(textBytes)), + goarch: goarch, + disasm: disasm, + byteOrder: byteOrder, + } + + return d, nil +} + +// lookup finds the symbol name containing addr. +func (d *Disasm) lookup(addr uint64) (name string, base uint64) { + i := sort.Search(len(d.syms), func(i int) bool { return addr < d.syms[i].Addr }) + if i > 0 { + s := d.syms[i-1] + if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) { + return s.Name, s.Addr + } + } + return "", 0 +} + +// base returns the final element in the path. +// It works on both Windows and Unix paths, +// regardless of host operating system. +func base(path string) string { + path = path[strings.LastIndex(path, "/")+1:] + path = path[strings.LastIndex(path, `\`)+1:] + return path +} + +// Print prints a disassembly of the file to w. +// If filter is non-nil, the disassembly only includes functions with names matching filter. +// The disassembly only includes functions that overlap the range [start, end). +func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + printed := false + bw := bufio.NewWriter(w) + for _, sym := range d.syms { + symStart := sym.Addr + symEnd := sym.Addr + uint64(sym.Size) + if sym.Code != 'T' && sym.Code != 't' || + symStart < d.textStart || + symEnd <= start || end <= symStart || + filter != nil && !filter.MatchString(sym.Name) { + continue + } + if printed { + fmt.Fprintf(bw, "\n") + } + printed = true + + file, _, _ := d.pcln.PCToLine(sym.Addr) + fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file) + + tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0) + if symEnd > end { + symEnd = end + } + code := d.text[:end-d.textStart] + d.Decode(symStart, symEnd, func(pc, size uint64, file string, line int, text string) { + i := pc - d.textStart + fmt.Fprintf(tw, "\t%s:%d\t%#x\t", base(file), line, pc) + if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" { + // Print instruction as bytes. + fmt.Fprintf(tw, "%x", code[i:i+size]) + } else { + // Print instruction as 32-bit words. + for j := uint64(0); j < size; j += 4 { + if j > 0 { + fmt.Fprintf(tw, " ") + } + fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:])) + } + } + fmt.Fprintf(tw, "\t%s\n", text) + }) + tw.Flush() + } + bw.Flush() +} + +// Decode disassembles the text segment range [start, end), calling f for each instruction. +func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string, line int, text string)) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + code := d.text[:end-d.textStart] + lookup := d.lookup + for pc := start; pc < end; { + i := pc - d.textStart + text, size := d.disasm(code[i:], pc, lookup) + file, line, _ := d.pcln.PCToLine(pc) + f(pc, uint64(size), file, line, text) + pc += uint64(size) + } +} + +type lookupFunc func(addr uint64) (sym string, base uint64) +type disasmFunc func(code []byte, pc uint64, lookup lookupFunc) (text string, size int) + +func disasm_386(code []byte, pc uint64, lookup lookupFunc) (string, int) { + return disasm_x86(code, pc, lookup, 32) +} + +func disasm_amd64(code []byte, pc uint64, lookup lookupFunc) (string, int) { + return disasm_x86(code, pc, lookup, 64) +} + +func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, int) { + inst, err := x86asm.Decode(code, 64) + var text string + size := inst.Len + if err != nil || size == 0 || inst.Op == 0 { + size = 1 + text = "?" + } else { + text = x86asm.Plan9Syntax(inst, pc, lookup) + } + return text, size +} + +type textReader struct { + code []byte + pc uint64 +} + +func (r textReader) ReadAt(data []byte, off int64) (n int, err error) { + if off < 0 || uint64(off) < r.pc { + return 0, io.EOF + } + d := uint64(off) - r.pc + if d >= uint64(len(r.code)) { + return 0, io.EOF + } + n = copy(data, r.code[d:]) + if n < len(data) { + err = io.ErrUnexpectedEOF + } + return +} + +func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) { + inst, err := armasm.Decode(code, armasm.ModeARM) + var text string + size := inst.Len + if err != nil || size == 0 || inst.Op == 0 { + size = 4 + text = "?" + } else { + text = armasm.Plan9Syntax(inst, pc, lookup, textReader{code, pc}) + } + return text, size +} + +var disasms = map[string]disasmFunc{ + "386": disasm_386, + "amd64": disasm_amd64, + "arm": disasm_arm, +} + +var byteOrders = map[string]binary.ByteOrder{ + "386": binary.LittleEndian, + "amd64": binary.LittleEndian, + "arm": binary.LittleEndian, + "power64": binary.BigEndian, + "power64le": binary.LittleEndian, +} diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go index 3d4a5d27c..9227ef387 100644 --- a/src/cmd/internal/objfile/objfile.go +++ b/src/cmd/internal/objfile/objfile.go @@ -9,6 +9,7 @@ import ( "debug/gosym" "fmt" "os" + "sort" ) type rawFile interface { @@ -62,9 +63,20 @@ func (f *File) Close() error { } func (f *File) Symbols() ([]Sym, error) { - return f.raw.symbols() + syms, err := f.raw.symbols() + if err != nil { + return nil, err + } + sort.Sort(byAddr(syms)) + return syms, nil } +type byAddr []Sym + +func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr } +func (x byAddr) Len() int { return len(x) } +func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + func (f *File) PCLineTable() (*gosym.Table, error) { textStart, symtab, pclntab, err := f.raw.pcln() if err != nil { diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go index 0f125c98b..708a85370 100644 --- a/src/cmd/objdump/main.go +++ b/src/cmd/objdump/main.go @@ -32,24 +32,15 @@ package main import ( - "bufio" - "debug/gosym" - "encoding/binary" "flag" "fmt" - "io" "log" "os" "regexp" - "sort" "strconv" "strings" - "text/tabwriter" "cmd/internal/objfile" - - "cmd/internal/rsc.io/arm/armasm" - "cmd/internal/rsc.io/x86/x86asm" ) var symregexp = flag.String("s", "", "only dump symbols matching this regexp") @@ -87,227 +78,30 @@ func main() { log.Fatal(err) } - syms, err := f.Symbols() + dis, err := f.Disasm() if err != nil { - log.Fatalf("reading %s: %v", flag.Arg(0), err) + log.Fatal("disassemble %s: %v", flag.Arg(0), err) } - tab, err := f.PCLineTable() - if err != nil { - log.Fatalf("reading %s: %v", flag.Arg(0), err) - } - - textStart, textBytes, err := f.Text() - if err != nil { - log.Fatalf("reading %s: %v", flag.Arg(0), err) - } - - goarch := f.GOARCH() - - disasm := disasms[goarch] - if disasm == nil { - log.Fatalf("reading %s: unknown architecture", flag.Arg(0)) - } - - // Filter out section symbols, overwriting syms in place. - keep := syms[:0] - for _, sym := range syms { - switch sym.Name { - case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext": - // drop - default: - keep = append(keep, sym) - } - } - syms = keep - - sort.Sort(ByAddr(syms)) - lookup := func(addr uint64) (string, uint64) { - i := sort.Search(len(syms), func(i int) bool { return addr < syms[i].Addr }) - if i > 0 { - s := syms[i-1] - if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) { - return s.Name, s.Addr - } - } - return "", 0 - } - - if flag.NArg() == 1 { - // disassembly of entire object - our format - dump(tab, lookup, disasm, goarch, syms, textBytes, textStart) + switch flag.NArg() { + default: + usage() + case 1: + // disassembly of entire object + dis.Print(os.Stdout, symRE, 0, ^uint64(0)) os.Exit(0) - } - - // disassembly of specific piece of object - gnu objdump format for pprof - gnuDump(tab, lookup, disasm, textBytes, textStart) - os.Exit(0) -} - -// base returns the final element in the path. -// It works on both Windows and Unix paths. -func base(path string) string { - path = path[strings.LastIndex(path, "/")+1:] - path = path[strings.LastIndex(path, `\`)+1:] - return path -} - -func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, goarch string, syms []objfile.Sym, textData []byte, textStart uint64) { - stdout := bufio.NewWriter(os.Stdout) - defer stdout.Flush() - - printed := false - for _, sym := range syms { - if (sym.Code != 'T' && sym.Code != 't') || sym.Size == 0 || sym.Addr < textStart || symRE != nil && !symRE.MatchString(sym.Name) { - continue - } - if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint64(sym.Size) > textStart+uint64(len(textData)) { - break - } - if printed { - fmt.Fprintf(stdout, "\n") - } else { - printed = true - } - file, _, _ := tab.PCToLine(sym.Addr) - fmt.Fprintf(stdout, "TEXT %s(SB) %s\n", sym.Name, file) - tw := tabwriter.NewWriter(stdout, 1, 8, 1, '\t', 0) - start := sym.Addr - end := sym.Addr + uint64(sym.Size) - for pc := start; pc < end; { - i := pc - textStart - text, size := disasm(textData[i:end-textStart], pc, lookup) - file, line, _ := tab.PCToLine(pc) - - // ARM is word-based, so show actual word hex, not byte hex. - // Since ARM is little endian, they're different. - if goarch == "arm" && size == 4 { - fmt.Fprintf(tw, "\t%s:%d\t%#x\t%08x\t%s\n", base(file), line, pc, binary.LittleEndian.Uint32(textData[i:i+uint64(size)]), text) - } else { - fmt.Fprintf(tw, "\t%s:%d\t%#x\t%x\t%s\n", base(file), line, pc, textData[i:i+uint64(size)], text) - } - pc += uint64(size) - } - tw.Flush() - } -} - -func disasm_386(code []byte, pc uint64, lookup lookupFunc) (string, int) { - return disasm_x86(code, pc, lookup, 32) -} - -func disasm_amd64(code []byte, pc uint64, lookup lookupFunc) (string, int) { - return disasm_x86(code, pc, lookup, 64) -} - -func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, int) { - inst, err := x86asm.Decode(code, 64) - var text string - size := inst.Len - if err != nil || size == 0 || inst.Op == 0 { - size = 1 - text = "?" - } else { - text = x86asm.Plan9Syntax(inst, pc, lookup) - } - return text, size -} - -type textReader struct { - code []byte - pc uint64 -} - -func (r textReader) ReadAt(data []byte, off int64) (n int, err error) { - if off < 0 || uint64(off) < r.pc { - return 0, io.EOF - } - d := uint64(off) - r.pc - if d >= uint64(len(r.code)) { - return 0, io.EOF - } - n = copy(data, r.code[d:]) - if n < len(data) { - err = io.ErrUnexpectedEOF - } - return -} - -func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) { - inst, err := armasm.Decode(code, armasm.ModeARM) - var text string - size := inst.Len - if err != nil || size == 0 || inst.Op == 0 { - size = 4 - text = "?" - } else { - text = armasm.Plan9Syntax(inst, pc, lookup, textReader{code, pc}) - } - return text, size -} - -var disasms = map[string]disasmFunc{ - "386": disasm_386, - "amd64": disasm_amd64, - "arm": disasm_arm, -} - -func gnuDump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, textData []byte, textStart uint64) { - start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64) - if err != nil { - log.Fatalf("invalid start PC: %v", err) - } - end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64) - if err != nil { - log.Fatalf("invalid end PC: %v", err) - } - if start < textStart { - start = textStart - } - if end < start { - end = start - } - if end > textStart+uint64(len(textData)) { - end = textStart + uint64(len(textData)) - } - - stdout := bufio.NewWriter(os.Stdout) - defer stdout.Flush() - - // For now, find spans of same PC/line/fn and - // emit them as having dummy instructions. - var ( - spanPC uint64 - spanFile string - spanLine int - spanFn *gosym.Func - ) - flush := func(endPC uint64) { - if spanPC == 0 { - return - } - fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine) - for pc := spanPC; pc < endPC; { - text, size := disasm(textData[pc-textStart:], pc, lookup) - fmt.Fprintf(stdout, " %x: %s\n", pc, text) - pc += uint64(size) + case 3: + // disassembly of PC range + start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64) + if err != nil { + log.Fatalf("invalid start PC: %v", err) } - spanPC = 0 - } - - for pc := start; pc < end; pc++ { - file, line, fn := tab.PCToLine(pc) - if file != spanFile || line != spanLine || fn != spanFn { - flush(pc) - spanPC, spanFile, spanLine, spanFn = pc, file, line, fn + end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64) + if err != nil { + log.Fatalf("invalid end PC: %v", err) } + dis.Print(os.Stdout, symRE, start, end) + os.Exit(0) } - flush(end) } - -type ByAddr []objfile.Sym - -func (x ByAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr } -func (x ByAddr) Len() int { return len(x) } -func (x ByAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go index 5047f9aa8..bd09ae9f9 100644 --- a/src/cmd/objdump/objdump_test.go +++ b/src/cmd/objdump/objdump_test.go @@ -5,117 +5,15 @@ package main import ( - "bufio" - "bytes" - "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" - "strconv" "strings" "testing" ) -func loadSyms(t *testing.T) map[string]string { - switch runtime.GOOS { - case "android", "nacl": - t.Skipf("skipping on %s", runtime.GOOS) - } - - cmd := exec.Command("go", "tool", "nm", os.Args[0]) - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out)) - } - syms := make(map[string]string) - scanner := bufio.NewScanner(bytes.NewReader(out)) - for scanner.Scan() { - f := strings.Fields(scanner.Text()) - if len(f) < 3 { - continue - } - syms[f[2]] = f[0] - } - if err := scanner.Err(); err != nil { - t.Fatalf("error reading symbols: %v", err) - } - return syms -} - -func runObjDump(t *testing.T, exe, startaddr, endaddr string) (path, lineno string) { - switch runtime.GOOS { - case "android", "nacl": - t.Skipf("skipping on %s", runtime.GOOS) - } - switch runtime.GOARCH { - case "power64", "power64le": - t.Skipf("skipping on %s, issue 9039", runtime.GOARCH) - } - - cmd := exec.Command(exe, os.Args[0], startaddr, endaddr) - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("go tool objdump %v: %v\n%s", os.Args[0], err, string(out)) - } - f := strings.Split(string(out), "\n") - if len(f) < 1 { - t.Fatal("objdump output must have at least one line") - } - pathAndLineNo := f[0] - f = strings.Split(pathAndLineNo, ":") - if runtime.GOOS == "windows" { - switch len(f) { - case 2: - return f[0], f[1] - case 3: - return f[0] + ":" + f[1], f[2] - default: - t.Fatalf("no line number found in %q", pathAndLineNo) - } - } - if len(f) != 2 { - t.Fatalf("no line number found in %q", pathAndLineNo) - } - return f[0], f[1] -} - -func testObjDump(t *testing.T, exe, startaddr, endaddr string, line int) { - srcPath, srcLineNo := runObjDump(t, exe, startaddr, endaddr) - fi1, err := os.Stat("objdump_test.go") - if err != nil { - t.Fatalf("Stat failed: %v", err) - } - fi2, err := os.Stat(srcPath) - if err != nil { - t.Fatalf("Stat failed: %v", err) - } - if !os.SameFile(fi1, fi2) { - t.Fatalf("objdump_test.go and %s are not same file", srcPath) - } - if srcLineNo != fmt.Sprint(line) { - t.Fatalf("line number = %v; want %d", srcLineNo, line) - } -} - -func TestObjDump(t *testing.T) { - _, _, line, _ := runtime.Caller(0) - syms := loadSyms(t) - - tmp, exe := buildObjdump(t) - defer os.RemoveAll(tmp) - - startaddr := syms["cmd/objdump.TestObjDump"] - addr, err := strconv.ParseUint(startaddr, 16, 64) - if err != nil { - t.Fatalf("invalid start address %v: %v", startaddr, err) - } - endaddr := fmt.Sprintf("%x", addr+10) - testObjDump(t, exe, startaddr, endaddr, line-1) - testObjDump(t, exe, "0x"+startaddr, "0x"+endaddr, line-1) -} - func buildObjdump(t *testing.T) (tmp, exe string) { switch runtime.GOOS { case "android", "nacl": diff --git a/src/cmd/pprof/README b/src/cmd/pprof/README new file mode 100644 index 000000000..a728ef235 --- /dev/null +++ b/src/cmd/pprof/README @@ -0,0 +1,8 @@ +The pprof in this directory is adapted from the pprof used inside Google +for C++, Java, and Go programs. Because it was developed for that broader +context, it is overgeneralized when used here for the specific use case +of profiling standard Go programs. However, we've left the abstractions +intact in order to share updates between this copy and Google's internal one. + +Please do not take the level of abstraction in this program as an example +to follow in your own. diff --git a/src/cmd/pprof/doc.go b/src/cmd/pprof/doc.go new file mode 100644 index 000000000..c6ff11d10 --- /dev/null +++ b/src/cmd/pprof/doc.go @@ -0,0 +1,12 @@ +// Copyright 2014 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. + +// Pprof interprets and displays profiles of Go programs. +// +// Usage: +// +// go tool pprof binary profile +// +// For more information, see http://blog.golang.org/profiling-go-programs. +package main diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index 89a5bb7d2..44f4f6cb7 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -11,6 +11,7 @@ import ( "os" "regexp" "strings" + "sync" "cmd/internal/objfile" "cmd/pprof/internal/commands" @@ -100,7 +101,10 @@ func (flags) ExtraUsage() string { // objTool implements plugin.ObjTool using Go libraries // (instead of invoking GNU binutils). -type objTool struct{} +type objTool struct { + mu sync.Mutex + disasmCache map[string]*objfile.Disasm +} func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) { of, err := objfile.Open(name) @@ -119,8 +123,39 @@ func (*objTool) Demangle(names []string) (map[string]string, error) { return make(map[string]string), nil } -func (*objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { - return nil, fmt.Errorf("disassembly not supported") +func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { + d, err := t.cachedDisasm(file) + if err != nil { + return nil, err + } + var asm []plugin.Inst + d.Decode(start, end, func(pc, size uint64, file string, line int, text string) { + asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text}) + }) + return asm, nil +} + +func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.disasmCache == nil { + t.disasmCache = make(map[string]*objfile.Disasm) + } + d := t.disasmCache[file] + if d != nil { + return d, nil + } + f, err := objfile.Open(file) + if err != nil { + return nil, err + } + d, err = f.Disasm() + f.Close() + if err != nil { + return nil, err + } + t.disasmCache[file] = d + return d, nil } func (*objTool) SetConfig(config string) { |