diff options
| author | Daniel Martí <mvdan@mvdan.cc> | 2018-02-12 16:34:48 +0000 |
|---|---|---|
| committer | Daniel Martí <mvdan@mvdan.cc> | 2018-02-15 16:25:43 +0000 |
| commit | e7cbbbe9bb878b6ca4ce04fde645df1c8f1845bd (patch) | |
| tree | f84255198234eb4870c48228cdb4828de1f4f8c5 /src/cmd/vendor/github.com/google/pprof/internal/binutils | |
| parent | afb9fc1de922a4ead9d2d787613255a7ba3490f7 (diff) | |
| download | go-e7cbbbe9bb878b6ca4ce04fde645df1c8f1845bd.tar.xz | |
cmd/vendor/github.com/google/pprof: refresh from upstream
Updating to commit 0e0e5b7254e076a62326ab7305ba49e8515f0c91
from github.com/google/pprof
Recent modifications to the vendored pprof, such as skipping
TestWebInterface to avoid starting a web browser, have all been fixed
upstream.
Change-Id: I72e11108c438e1573bf2f9216e76d157378e8d45
Reviewed-on: https://go-review.googlesource.com/93375
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
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.go | 27 | ||||
| -rw-r--r-- | src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go | 147 | ||||
| -rw-r--r-- | src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go | 34 | ||||
| -rwxr-xr-x | src/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) | bin | 9503 -> 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 Binary files differindex 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 |
