diff options
| author | Alberto Donizetti <alb.donizetti@gmail.com> | 2017-08-20 12:27:32 +0200 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2017-11-02 23:51:45 +0000 |
| commit | aec345d638fa624f08b7d758e9e173897edc80e8 (patch) | |
| tree | d782d951af4f34de34a08c4775a37f869af25b81 /src/cmd/vendor/github.com/google/pprof/internal/binutils | |
| parent | 3039bff9d07ce05dc9af8c155c6929ae5e53a231 (diff) | |
| download | go-aec345d638fa624f08b7d758e9e173897edc80e8.tar.xz | |
cmd/vendor/github.com/google/pprof: refresh from upstream
Update vendored pprof to commit 4fc39a00b6b8c1aad05260f01429ec70e127252c
from github.com/google/pprof (2017-11-01).
Fixes #19380
Updates #21047
Change-Id: Ib64a94a45209039e5945acbcfa0392790c8ee41e
Reviewed-on: https://go-review.googlesource.com/57370
Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/binutils')
8 files changed, 265 insertions, 53 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go index e3a7777253..71e471b5d6 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go @@ -21,6 +21,7 @@ import ( "os/exec" "strconv" "strings" + "sync" "github.com/google/pprof/internal/plugin" ) @@ -36,6 +37,7 @@ const ( // addr2Liner is a connection to an addr2line command for obtaining // address and line number information from a binary. type addr2Liner struct { + mu sync.Mutex rw lineReaderWriter base uint64 @@ -170,9 +172,10 @@ func (d *addr2Liner) readFrame() (plugin.Frame, bool) { Line: linenumber}, false } -// addrInfo returns the stack frame information for a specific program -// address. It returns nil if the address could not be identified. -func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) { +func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) { + d.mu.Lock() + defer d.mu.Unlock() + if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil { return nil, err } @@ -201,6 +204,16 @@ func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) { stack = append(stack, frame) } } + return stack, err +} + +// addrInfo returns the stack frame information for a specific program +// address. It returns nil if the address could not be identified. +func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) { + stack, err := d.rawAddrInfo(addr) + if err != nil { + return nil, err + } // Get better name from nm if possible. if len(stack) > 0 && d.nm != nil { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go index 7692b0a5cb..68fa5593ad 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go @@ -21,6 +21,7 @@ import ( "os/exec" "strconv" "strings" + "sync" "github.com/google/pprof/internal/plugin" ) @@ -32,6 +33,7 @@ const ( // llvmSymbolizer is a connection to an llvm-symbolizer command for // obtaining address and line number information from a binary. type llvmSymbolizer struct { + sync.Mutex filename string rw lineReaderWriter base uint64 @@ -150,6 +152,9 @@ func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) { // addrInfo returns the stack frame information for a specific program // address. It returns nil if the address could not be identified. func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) { + d.Lock() + defer d.Unlock() + if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil { return nil, err } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go index e7a8e10b34..1987bd3dab 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go @@ -48,22 +48,23 @@ func newAddr2LinerNM(cmd, file string, base uint64) (*addr2LinerNM, error) { if cmd == "" { cmd = defaultNM } - - a := &addr2LinerNM{ - m: []symbolInfo{}, - } - var b bytes.Buffer c := exec.Command(cmd, "-n", file) c.Stdout = &b - if err := c.Run(); err != nil { return nil, err } + return parseAddr2LinerNM(base, &b) +} + +func parseAddr2LinerNM(base uint64, nm io.Reader) (*addr2LinerNM, error) { + a := &addr2LinerNM{ + m: []symbolInfo{}, + } // Parse nm output and populate symbol map. // Skip lines we fail to parse. - buf := bufio.NewReader(&b) + buf := bufio.NewReader(nm) for { line, err := buf.ReadString('\n') if line == "" && err != nil { 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 9854c9a262..9a82cb8e92 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 @@ -24,14 +24,21 @@ import ( "path/filepath" "regexp" "strings" + "sync" "github.com/google/pprof/internal/elfexec" "github.com/google/pprof/internal/plugin" ) // A Binutils implements plugin.ObjTool by invoking the GNU binutils. -// SetConfig must be called before any of the other methods. type Binutils struct { + mu sync.Mutex + rep *binrep +} + +// binrep is an immutable representation for Binutils. It is atomically +// replaced on every mutation to provide thread-safe access. +type binrep struct { // Commands to invoke. llvmSymbolizer string llvmSymbolizerFound bool @@ -47,11 +54,38 @@ type Binutils struct { fast bool } +// get returns the current representation for bu, initializing it if necessary. +func (bu *Binutils) get() *binrep { + bu.mu.Lock() + r := bu.rep + if r == nil { + r = &binrep{} + initTools(r, "") + bu.rep = r + } + bu.mu.Unlock() + return r +} + +// update modifies the rep for bu via the supplied function. +func (bu *Binutils) update(fn func(r *binrep)) { + r := &binrep{} + bu.mu.Lock() + defer bu.mu.Unlock() + if bu.rep == nil { + initTools(r, "") + } else { + *r = *bu.rep + } + fn(r) + bu.rep = r +} + // SetFastSymbolization sets a toggle that makes binutils use fast // symbolization (using nm), which is much faster than addr2line but // provides only symbol name information (no file/line). -func (b *Binutils) SetFastSymbolization(fast bool) { - b.fast = fast +func (bu *Binutils) SetFastSymbolization(fast bool) { + bu.update(func(r *binrep) { r.fast = fast }) } // SetTools processes the contents of the tools option. It @@ -59,7 +93,11 @@ func (b *Binutils) SetFastSymbolization(fast bool) { // of the form t:path, where cmd will be used to look only for the // tool named t. If t is not specified, the path is searched for all // tools. -func (b *Binutils) SetTools(config string) { +func (bu *Binutils) SetTools(config string) { + bu.update(func(r *binrep) { initTools(r, config) }) +} + +func initTools(b *binrep, config string) { // paths collect paths per tool; Key "" contains the default. paths := make(map[string][]string) for _, t := range strings.Split(config, ",") { @@ -91,11 +129,8 @@ func findExe(cmd string, paths []string) (string, bool) { // Disasm returns the assembly instructions for the specified address range // of a binary. -func (b *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { - if b.addr2line == "" { - // Update the command invocations if not initialized. - b.SetTools("") - } +func (bu *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { + b := bu.get() cmd := exec.Command(b.objdump, "-d", "-C", "--no-show-raw-insn", "-l", fmt.Sprintf("--start-address=%#x", start), fmt.Sprintf("--stop-address=%#x", end), @@ -109,11 +144,8 @@ func (b *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) } // Open satisfies the plugin.ObjTool interface. -func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) { - if b.addr2line == "" { - // Update the command invocations if not initialized. - b.SetTools("") - } +func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) { + b := bu.get() // Make sure file is a supported executable. // The pprof driver uses Open to sniff the difference @@ -140,7 +172,7 @@ func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFil return nil, fmt.Errorf("unrecognized binary: %s", name) } -func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) { +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) @@ -153,7 +185,7 @@ func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.O return &fileAddr2Line{file: file{b: b, name: name}}, nil } -func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) { +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) @@ -202,7 +234,7 @@ func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.Obj // file implements the binutils.ObjFile interface. type file struct { - b *Binutils + b *binrep name string base uint64 buildID string @@ -263,22 +295,27 @@ func (f *fileNM) SourceLine(addr uint64) ([]plugin.Frame, error) { // information). It can be slow for large binaries with debug // information. type fileAddr2Line struct { + once sync.Once file addr2liner *addr2Liner llvmSymbolizer *llvmSymbolizer } func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) { + f.once.Do(f.init) if f.llvmSymbolizer != nil { return f.llvmSymbolizer.addrInfo(addr) } if f.addr2liner != nil { return f.addr2liner.addrInfo(addr) } + return nil, fmt.Errorf("could not find local addr2liner") +} +func (f *fileAddr2Line) init() { if llvmSymbolizer, err := newLLVMSymbolizer(f.b.llvmSymbolizer, f.name, f.base); err == nil { f.llvmSymbolizer = llvmSymbolizer - return f.llvmSymbolizer.addrInfo(addr) + return } if addr2liner, err := newAddr2Liner(f.b.addr2line, f.name, f.base); err == nil { @@ -290,13 +327,14 @@ func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) { if nm, err := newAddr2LinerNM(f.b.nm, f.name, f.base); err == nil { f.addr2liner.nm = nm } - return f.addr2liner.addrInfo(addr) } - - return nil, fmt.Errorf("could not find local addr2liner") } func (f *fileAddr2Line) Close() error { + if f.llvmSymbolizer != nil { + f.llvmSymbolizer.rw.close() + f.llvmSymbolizer = nil + } if f.addr2liner != nil { f.addr2liner.rw.close() f.addr2liner = nil 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 b0ba5f67a8..989a290071 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 @@ -15,7 +15,13 @@ package binutils import ( + "bytes" "fmt" + "math" + "path/filepath" + "reflect" + "regexp" + "runtime" "testing" "github.com/google/pprof/internal/plugin" @@ -37,7 +43,7 @@ func functionName(level int) (name string) { func TestAddr2Liner(t *testing.T) { const offset = 0x500 - a := addr2Liner{&mockAddr2liner{}, offset, nil} + a := addr2Liner{rw: &mockAddr2liner{}, base: offset} for i := 1; i < 8; i++ { addr := i*0x1000 + offset s, err := a.addrInfo(uint64(addr)) @@ -112,24 +118,23 @@ func (a *mockAddr2liner) close() { } func TestAddr2LinerLookup(t *testing.T) { - oddSizedMap := addr2LinerNM{ - m: []symbolInfo{ - {0x1000, "0x1000"}, - {0x2000, "0x2000"}, - {0x3000, "0x3000"}, - }, - } - evenSizedMap := addr2LinerNM{ - m: []symbolInfo{ - {0x1000, "0x1000"}, - {0x2000, "0x2000"}, - {0x3000, "0x3000"}, - {0x4000, "0x4000"}, - }, - } - for _, a := range []*addr2LinerNM{ - &oddSizedMap, &evenSizedMap, - } { + const oddSizedData = ` +00001000 T 0x1000 +00002000 T 0x2000 +00003000 T 0x3000 +` + const evenSizedData = ` +0000000000001000 T 0x1000 +0000000000002000 T 0x2000 +0000000000003000 T 0x3000 +0000000000004000 T 0x4000 +` + for _, d := range []string{oddSizedData, evenSizedData} { + a, err := parseAddr2LinerNM(0, bytes.NewBufferString(d)) + if err != nil { + t.Errorf("nm parse error: %v", err) + continue + } for address, want := range map[uint64]string{ 0x1000: "0x1000", 0x1001: "0x1000", @@ -141,6 +146,11 @@ func TestAddr2LinerLookup(t *testing.T) { t.Errorf("%x: got %v, want %s", address, got, want) } } + for _, unknown := range []uint64{0x0fff, 0x4001} { + if got, _ := a.addrInfo(unknown); got != nil { + t.Errorf("%x: got %v, want nil", unknown, got) + } + } } } @@ -150,3 +160,116 @@ func checkAddress(got []plugin.Frame, address uint64, want string) bool { } return got[0].Func == want } + +func TestSetTools(t *testing.T) { + // Test that multiple calls work. + bu := &Binutils{} + bu.SetTools("") + bu.SetTools("") +} + +func TestSetFastSymbolization(t *testing.T) { + // Test that multiple calls work. + bu := &Binutils{} + bu.SetFastSymbolization(true) + bu.SetFastSymbolization(false) +} + +func skipUnlessLinuxAmd64(t *testing.T) { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("Disasm only tested on x86-64 linux") + } +} + +func TestDisasm(t *testing.T) { + skipUnlessLinuxAmd64(t) + bu := &Binutils{} + insts, err := bu.Disasm(filepath.Join("testdata", "hello"), 0, math.MaxUint64) + if err != nil { + t.Fatalf("Disasm: unexpected error %v", err) + } + mainCount := 0 + for _, x := range insts { + if x.Function == "main" { + mainCount++ + } + } + if mainCount == 0 { + t.Error("Disasm: found no main instructions") + } +} + +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) + } + + find := func(name string) *plugin.Sym { + for _, s := range syms { + for _, n := range s.Name { + if n == name { + return s + } + } + } + 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 TestLLVMSymbolizer(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("testtdata/llvm-symbolizer has only been tested on linux") + } + + cmd := filepath.Join("testdata", "fake-llvm-symbolizer") + symbolizer, err := newLLVMSymbolizer(cmd, "foo", 0) + if err != nil { + t.Fatalf("newLLVMSymbolizer: unexpected error %v", err) + } + defer symbolizer.rw.close() + + for _, c := range []struct { + addr uint64 + frames []plugin.Frame + }{ + {0x10, []plugin.Frame{ + {Func: "Inlined_0x10", File: "foo.h", Line: 0}, + {Func: "Func_0x10", File: "foo.c", Line: 2}, + }}, + {0x20, []plugin.Frame{ + {Func: "Inlined_0x20", File: "foo.h", Line: 0}, + {Func: "Func_0x20", File: "foo.c", Line: 2}, + }}, + } { + frames, err := symbolizer.addrInfo(c.addr) + if err != nil { + t.Errorf("LLVM: unexpected error %v", err) + continue + } + if !reflect.DeepEqual(frames, c.frames) { + t.Errorf("LLVM: expect %v; got %v\n", c.frames, frames) + } + } +} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go index 7fc25741ce..3563198f48 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go @@ -73,7 +73,7 @@ func TestFindSymbols(t *testing.T) { func checkSymbol(got []*plugin.Sym, want []plugin.Sym) error { if len(got) != len(want) { - return fmt.Errorf("unexpected number of symbols %d (want %d)\n", len(got), len(want)) + return fmt.Errorf("unexpected number of symbols %d (want %d)", len(got), len(want)) } for i, g := range got { @@ -134,8 +134,6 @@ func TestFunctionAssembly(t *testing.T) { }, } - const objdump = "testdata/wrapper/objdump" - for _, tc := range testcases { insts, err := disassemble([]byte(tc.asm)) if err != nil { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/fake-llvm-symbolizer b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/fake-llvm-symbolizer new file mode 100755 index 0000000000..596713cb04 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/fake-llvm-symbolizer @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright 2014 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Fake llvm-symbolizer to use in tests + +set -f +IFS=" " + +while read line; do + # line has form: + # filename 0xaddr + # Emit dummy output that matches llvm-symbolizer output format. + set -- $line + fname=$1 + addr=$2 + echo "Inlined_$addr" + echo "$fname.h" + echo "Func_$addr" + echo "$fname.c:2" + echo +done 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/hello Binary files differnew file mode 100755 index 0000000000..d86dc7cdfc --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/hello |
