aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/binutils
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/binutils')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go27
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go147
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go34
-rwxr-xr-xsrc/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/exe_linux_64 (renamed from src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/hello)bin9503 -> 9503 bytes
4 files changed, 165 insertions, 43 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
index 9a82cb8e92..390f952feb 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
@@ -175,20 +175,35 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
of, err := macho.Open(name)
if err != nil {
- return nil, fmt.Errorf("Parsing %s: %v", name, err)
+ return nil, fmt.Errorf("error parsing %s: %v", name, err)
}
defer of.Close()
+ // Subtract the load address of the __TEXT section. Usually 0 for shared
+ // libraries or 0x100000000 for executables. You can check this value by
+ // running `objdump -private-headers <file>`.
+
+ textSegment := of.Segment("__TEXT")
+ if textSegment == nil {
+ return nil, fmt.Errorf("could not identify base for %s: no __TEXT segment", name)
+ }
+ if textSegment.Addr > start {
+ return nil, fmt.Errorf("could not identify base for %s: __TEXT segment address (0x%x) > mapping start address (0x%x)",
+ name, textSegment.Addr, start)
+ }
+
+ base := start - textSegment.Addr
+
if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
- return &fileNM{file: file{b: b, name: name}}, nil
+ return &fileNM{file: file{b: b, name: name, base: base}}, nil
}
- return &fileAddr2Line{file: file{b: b, name: name}}, nil
+ return &fileAddr2Line{file: file{b: b, name: name, base: base}}, nil
}
func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
ef, err := elf.Open(name)
if err != nil {
- return nil, fmt.Errorf("Parsing %s: %v", name, err)
+ return nil, fmt.Errorf("error parsing %s: %v", name, err)
}
defer ef.Close()
@@ -215,9 +230,9 @@ func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFi
}
}
- base, err := elfexec.GetBase(&ef.FileHeader, nil, stextOffset, start, limit, offset)
+ base, err := elfexec.GetBase(&ef.FileHeader, elfexec.FindTextProgHeader(ef), stextOffset, start, limit, offset)
if err != nil {
- return nil, fmt.Errorf("Could not identify base for %s: %v", name, err)
+ return nil, fmt.Errorf("could not identify base for %s: %v", name, err)
}
buildID := ""
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go
index 989a290071..0317cf5126 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go
@@ -177,14 +177,20 @@ func TestSetFastSymbolization(t *testing.T) {
func skipUnlessLinuxAmd64(t *testing.T) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
- t.Skip("Disasm only tested on x86-64 linux")
+ t.Skip("This test only works on x86-64 Linux")
+ }
+}
+
+func skipUnlessDarwinAmd64(t *testing.T) {
+ if runtime.GOOS != "darwin" || runtime.GOARCH != "amd64" {
+ t.Skip("This test only works on x86-64 Mac")
}
}
func TestDisasm(t *testing.T) {
skipUnlessLinuxAmd64(t)
bu := &Binutils{}
- insts, err := bu.Disasm(filepath.Join("testdata", "hello"), 0, math.MaxUint64)
+ insts, err := bu.Disasm(filepath.Join("testdata", "exe_linux_64"), 0, math.MaxUint64)
if err != nil {
t.Fatalf("Disasm: unexpected error %v", err)
}
@@ -199,42 +205,119 @@ func TestDisasm(t *testing.T) {
}
}
+func findSymbol(syms []*plugin.Sym, name string) *plugin.Sym {
+ for _, s := range syms {
+ for _, n := range s.Name {
+ if n == name {
+ return s
+ }
+ }
+ }
+ return nil
+}
+
func TestObjFile(t *testing.T) {
skipUnlessLinuxAmd64(t)
- bu := &Binutils{}
- f, err := bu.Open(filepath.Join("testdata", "hello"), 0, math.MaxUint64, 0)
- if err != nil {
- t.Fatalf("Open: unexpected error %v", err)
- }
- defer f.Close()
- syms, err := f.Symbols(regexp.MustCompile("main"), 0)
- if err != nil {
- t.Fatalf("Symbols: unexpected error %v", err)
- }
+ for _, tc := range []struct {
+ desc string
+ start, limit, offset uint64
+ addr uint64
+ }{
+ {"fake mapping", 0, math.MaxUint64, 0, 0x40052d},
+ {"fixed load address", 0x400000, 0x4006fc, 0, 0x40052d},
+ // True user-mode ASLR binaries are ET_DYN rather than ET_EXEC so this case
+ // is a bit artificial except that it approximates the
+ // vmlinux-with-kernel-ASLR case where the binary *is* ET_EXEC.
+ {"simulated ASLR address", 0x500000, 0x5006fc, 0, 0x50052d},
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ bu := &Binutils{}
+ f, err := bu.Open(filepath.Join("testdata", "exe_linux_64"), tc.start, tc.limit, tc.offset)
+ if err != nil {
+ t.Fatalf("Open: unexpected error %v", err)
+ }
+ defer f.Close()
+ syms, err := f.Symbols(regexp.MustCompile("main"), 0)
+ if err != nil {
+ t.Fatalf("Symbols: unexpected error %v", err)
+ }
- find := func(name string) *plugin.Sym {
- for _, s := range syms {
- for _, n := range s.Name {
- if n == name {
- return s
+ m := findSymbol(syms, "main")
+ if m == nil {
+ t.Fatalf("Symbols: did not find main")
+ }
+ for _, addr := range []uint64{m.Start + f.Base(), tc.addr} {
+ gotFrames, err := f.SourceLine(addr)
+ if err != nil {
+ t.Fatalf("SourceLine: unexpected error %v", err)
+ }
+ wantFrames := []plugin.Frame{
+ {Func: "main", File: "/tmp/hello.c", Line: 3},
+ }
+ if !reflect.DeepEqual(gotFrames, wantFrames) {
+ t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames)
}
}
- }
- return nil
- }
- m := find("main")
- if m == nil {
- t.Fatalf("Symbols: did not find main")
- }
- frames, err := f.SourceLine(m.Start)
- if err != nil {
- t.Fatalf("SourceLine: unexpected error %v", err)
- }
- expect := []plugin.Frame{
- {Func: "main", File: "/tmp/hello.c", Line: 3},
+ })
}
- if !reflect.DeepEqual(frames, expect) {
- t.Fatalf("SourceLine for main: expect %v; got %v\n", expect, frames)
+}
+
+func TestMachoFiles(t *testing.T) {
+ skipUnlessDarwinAmd64(t)
+
+ t.Skip("Disabled because of issues with addr2line (see https://github.com/google/pprof/pull/313#issuecomment-364073010)")
+
+ // Load `file`, pretending it was mapped at `start`. Then get the symbol
+ // table. Check that it contains the symbol `sym` and that the address
+ // `addr` gives the `expected` stack trace.
+ for _, tc := range []struct {
+ desc string
+ file string
+ start, limit, offset uint64
+ addr uint64
+ sym string
+ expected []plugin.Frame
+ }{
+ {"normal mapping", "exe_mac_64", 0x100000000, math.MaxUint64, 0,
+ 0x100000f50, "_main",
+ []plugin.Frame{
+ {Func: "main", File: "/tmp/hello.c", Line: 3},
+ }},
+ {"other mapping", "exe_mac_64", 0x200000000, math.MaxUint64, 0,
+ 0x200000f50, "_main",
+ []plugin.Frame{
+ {Func: "main", File: "/tmp/hello.c", Line: 3},
+ }},
+ {"lib normal mapping", "lib_mac_64", 0, math.MaxUint64, 0,
+ 0xfa0, "_bar",
+ []plugin.Frame{
+ {Func: "bar", File: "/tmp/lib.c", Line: 6},
+ }},
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ bu := &Binutils{}
+ f, err := bu.Open(filepath.Join("testdata", tc.file), tc.start, tc.limit, tc.offset)
+ if err != nil {
+ t.Fatalf("Open: unexpected error %v", err)
+ }
+ defer f.Close()
+ syms, err := f.Symbols(nil, 0)
+ if err != nil {
+ t.Fatalf("Symbols: unexpected error %v", err)
+ }
+
+ m := findSymbol(syms, tc.sym)
+ if m == nil {
+ t.Fatalf("Symbols: could not find symbol %v", tc.sym)
+ }
+ gotFrames, err := f.SourceLine(tc.addr)
+ if err != nil {
+ t.Fatalf("SourceLine: unexpected error %v", err)
+ }
+ if !reflect.DeepEqual(gotFrames, tc.expected) {
+ t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, tc.expected)
+ }
+ })
}
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
index 1a3b6f8d6a..28c89aa163 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
@@ -34,24 +34,48 @@ var (
func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
// Collect all symbols from the nm output, grouping names mapped to
// the same address into a single symbol.
+
+ // The symbols to return.
var symbols []*plugin.Sym
+
+ // The current group of symbol names, and the address they are all at.
names, start := []string{}, uint64(0)
+
buf := bytes.NewBuffer(syms)
- for symAddr, name, err := nextSymbol(buf); err == nil; symAddr, name, err = nextSymbol(buf) {
+
+ for {
+ symAddr, name, err := nextSymbol(buf)
+ if err == io.EOF {
+ // Done. If there was an unfinished group, append it.
+ if len(names) != 0 {
+ if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
+ symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
+ }
+ }
+
+ // And return the symbols.
+ return symbols, nil
+ }
+
if err != nil {
+ // There was some kind of serious error reading nm's output.
return nil, err
}
- if start == symAddr {
+
+ // If this symbol is at the same address as the current group, add it to the group.
+ if symAddr == start {
names = append(names, name)
continue
}
+
+ // Otherwise append the current group to the list of symbols.
if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
}
+
+ // And start a new group.
names, start = []string{name}, symAddr
}
-
- return symbols, nil
}
// matchSymbol checks if a symbol is to be selected by checking its
@@ -62,7 +86,7 @@ func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, address ui
return names
}
for _, name := range names {
- if r.MatchString(name) {
+ if r == nil || r.MatchString(name) {
return []string{name}
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/hello b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/exe_linux_64
index d86dc7cdfc..d86dc7cdfc 100755
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/hello
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/exe_linux_64
Binary files differ