aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/binutils
diff options
context:
space:
mode:
authorRaul Silvera <rsilvera@google.com>2017-02-10 14:52:02 -0800
committerRuss Cox <rsc@golang.org>2017-02-24 19:18:53 +0000
commit7844ef427a61bd68db81912a254a3f99633a9354 (patch)
tree6d2be738f8c2cecdfc90ee43fba835ae7ce79d52 /src/cmd/vendor/github.com/google/pprof/internal/binutils
parent2818cb5c9e183aed539d6a539a821e229671fe56 (diff)
downloadgo-7844ef427a61bd68db81912a254a3f99633a9354.tar.xz
cmd/pprof: vendor pprof from github.com/google/pprof
Import the github.com/google/pprof and github.com/ianlancetaylor/demangle packages, without modification. Build the golang version of pprof from cmd/pprof/pprof.go by importing the packages from src/cmd/vendot/github.com/google/pprof The versions upstreamed are: github.com/ianlancetaylor/demangle 4883227f66371e02c4948937d3e2be1664d9be38 github.com/google/pprof 7eb5ba977f28f2ad8dd5f6bb82cc9b454e123cdc Update misc/nacl/testzip.proto for new tests. Change-Id: I076584856491353607a3b98b67d0ca6838be50d6 Reviewed-on: https://go-review.googlesource.com/36798 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Russ Cox <rsc@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/addr2liner.go219
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go170
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go123
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go305
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go152
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go147
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go154
7 files changed, 1270 insertions, 0 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
new file mode 100644
index 0000000000..6b9e6abb22
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go
@@ -0,0 +1,219 @@
+// 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.
+
+package binutils
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os/exec"
+ "strconv"
+ "strings"
+
+ "github.com/google/pprof/internal/plugin"
+)
+
+const (
+ defaultAddr2line = "addr2line"
+
+ // addr2line may produce multiple lines of output. We
+ // use this sentinel to identify the end of the output.
+ sentinel = ^uint64(0)
+)
+
+// addr2Liner is a connection to an addr2line command for obtaining
+// address and line number information from a binary.
+type addr2Liner struct {
+ rw lineReaderWriter
+ base uint64
+
+ // nm holds an NM based addr2Liner which can provide
+ // better full names compared to addr2line, which often drops
+ // namespaces etc. from the names it returns.
+ nm *addr2LinerNM
+}
+
+// lineReaderWriter is an interface to abstract the I/O to an addr2line
+// process. It writes a line of input to the job, and reads its output
+// one line at a time.
+type lineReaderWriter interface {
+ write(string) error
+ readLine() (string, error)
+ close()
+}
+
+type addr2LinerJob struct {
+ cmd *exec.Cmd
+ in io.WriteCloser
+ out *bufio.Reader
+}
+
+func (a *addr2LinerJob) write(s string) error {
+ _, err := fmt.Fprint(a.in, s+"\n")
+ return err
+}
+
+func (a *addr2LinerJob) readLine() (string, error) {
+ return a.out.ReadString('\n')
+}
+
+// close releases any resources used by the addr2liner object.
+func (a *addr2LinerJob) close() {
+ a.in.Close()
+ a.cmd.Wait()
+}
+
+// newAddr2liner starts the given addr2liner command reporting
+// information about the given executable file. If file is a shared
+// library, base should be the address at which it was mapped in the
+// program under consideration.
+func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
+ if cmd == "" {
+ cmd = defaultAddr2line
+ }
+
+ j := &addr2LinerJob{
+ cmd: exec.Command(cmd, "-aif", "-e", file),
+ }
+
+ var err error
+ if j.in, err = j.cmd.StdinPipe(); err != nil {
+ return nil, err
+ }
+
+ outPipe, err := j.cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ j.out = bufio.NewReader(outPipe)
+ if err := j.cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ a := &addr2Liner{
+ rw: j,
+ base: base,
+ }
+
+ return a, nil
+}
+
+func (d *addr2Liner) readString() (string, error) {
+ s, err := d.rw.readLine()
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(s), nil
+}
+
+// readFrame parses the addr2line output for a single address. It
+// returns a populated plugin.Frame and whether it has reached the end of the
+// data.
+func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
+ funcname, err := d.readString()
+ if err != nil {
+ return plugin.Frame{}, true
+ }
+ if strings.HasPrefix(funcname, "0x") {
+ // If addr2line returns a hex address we can assume it is the
+ // sentinel. Read and ignore next two lines of output from
+ // addr2line
+ d.readString()
+ d.readString()
+ return plugin.Frame{}, true
+ }
+
+ fileline, err := d.readString()
+ if err != nil {
+ return plugin.Frame{}, true
+ }
+
+ linenumber := 0
+
+ if funcname == "??" {
+ funcname = ""
+ }
+
+ if fileline == "??:0" {
+ fileline = ""
+ } else {
+ if i := strings.LastIndex(fileline, ":"); i >= 0 {
+ // Remove discriminator, if present
+ if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
+ fileline = fileline[:disc]
+ }
+ // If we cannot parse a number after the last ":", keep it as
+ // part of the filename.
+ if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
+ linenumber = line
+ fileline = fileline[:i]
+ }
+ }
+ }
+
+ return plugin.Frame{funcname, fileline, 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) {
+ if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
+ return nil, err
+ }
+
+ if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
+ return nil, err
+ }
+
+ resp, err := d.readString()
+ if err != nil {
+ return nil, err
+ }
+
+ if !strings.HasPrefix(resp, "0x") {
+ return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
+ }
+
+ var stack []plugin.Frame
+ for {
+ frame, end := d.readFrame()
+ if end {
+ break
+ }
+
+ if frame != (plugin.Frame{}) {
+ stack = append(stack, frame)
+ }
+ }
+
+ // Get better name from nm if possible.
+ if len(stack) > 0 && d.nm != nil {
+ nm, err := d.nm.addrInfo(addr)
+ if err == nil && len(nm) > 0 {
+ // Last entry in frame list should match since
+ // it is non-inlined. As a simple heuristic,
+ // we only switch to the nm-based name if it
+ // is longer.
+ nmName := nm[len(nm)-1].Func
+ a2lName := stack[len(stack)-1].Func
+ if len(nmName) > len(a2lName) {
+ stack[len(stack)-1].Func = nmName
+ }
+ }
+ }
+
+ return stack, 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
new file mode 100644
index 0000000000..17ff5fd836
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
@@ -0,0 +1,170 @@
+// 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.
+
+package binutils
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os/exec"
+ "strconv"
+ "strings"
+
+ "github.com/google/pprof/internal/plugin"
+)
+
+const (
+ defaultLLVMSymbolizer = "llvm-symbolizer"
+)
+
+// llvmSymbolizer is a connection to an llvm-symbolizer command for
+// obtaining address and line number information from a binary.
+type llvmSymbolizer struct {
+ filename string
+ rw lineReaderWriter
+ base uint64
+}
+
+type llvmSymbolizerJob struct {
+ cmd *exec.Cmd
+ in io.WriteCloser
+ out *bufio.Reader
+}
+
+func (a *llvmSymbolizerJob) write(s string) error {
+ _, err := fmt.Fprint(a.in, s+"\n")
+ return err
+}
+
+func (a *llvmSymbolizerJob) readLine() (string, error) {
+ return a.out.ReadString('\n')
+}
+
+// close releases any resources used by the llvmSymbolizer object.
+func (a *llvmSymbolizerJob) close() {
+ a.in.Close()
+ a.cmd.Wait()
+}
+
+// newLlvmSymbolizer starts the given llvmSymbolizer command reporting
+// information about the given executable file. If file is a shared
+// library, base should be the address at which it was mapped in the
+// program under consideration.
+func newLLVMSymbolizer(cmd, file string, base uint64) (*llvmSymbolizer, error) {
+ if cmd == "" {
+ cmd = defaultLLVMSymbolizer
+ }
+
+ j := &llvmSymbolizerJob{
+ cmd: exec.Command(cmd, "-inlining", "-demangle=false"),
+ }
+
+ var err error
+ if j.in, err = j.cmd.StdinPipe(); err != nil {
+ return nil, err
+ }
+
+ outPipe, err := j.cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ j.out = bufio.NewReader(outPipe)
+ if err := j.cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ a := &llvmSymbolizer{
+ filename: file,
+ rw: j,
+ base: base,
+ }
+
+ return a, nil
+}
+
+func (d *llvmSymbolizer) readString() (string, error) {
+ s, err := d.rw.readLine()
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(s), nil
+}
+
+// readFrame parses the llvm-symbolizer output for a single address. It
+// returns a populated plugin.Frame and whether it has reached the end of the
+// data.
+func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
+ funcname, err := d.readString()
+ if err != nil {
+ return plugin.Frame{}, true
+ }
+
+ switch funcname {
+ case "":
+ return plugin.Frame{}, true
+ case "??":
+ funcname = ""
+ }
+
+ fileline, err := d.readString()
+ if err != nil {
+ return plugin.Frame{funcname, "", 0}, true
+ }
+
+ linenumber := 0
+ if fileline == "??:0" {
+ fileline = ""
+ } else {
+ switch split := strings.Split(fileline, ":"); len(split) {
+ case 1:
+ // filename
+ fileline = split[0]
+ case 2, 3:
+ // filename:line , or
+ // filename:line:disc , or
+ fileline = split[0]
+ if line, err := strconv.Atoi(split[1]); err == nil {
+ linenumber = line
+ }
+ default:
+ // Unrecognized, ignore
+ }
+ }
+
+ return plugin.Frame{funcname, fileline, 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 *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
+ if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
+ return nil, err
+ }
+
+ var stack []plugin.Frame
+ for {
+ frame, end := d.readFrame()
+ if end {
+ break
+ }
+
+ if frame != (plugin.Frame{}) {
+ stack = append(stack, frame)
+ }
+ }
+
+ return stack, nil
+}
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
new file mode 100644
index 0000000000..e7a8e10b34
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go
@@ -0,0 +1,123 @@
+// 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.
+
+package binutils
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "os/exec"
+ "strconv"
+ "strings"
+
+ "github.com/google/pprof/internal/plugin"
+)
+
+const (
+ defaultNM = "nm"
+)
+
+// addr2LinerNM is a connection to an nm command for obtaining address
+// information from a binary.
+type addr2LinerNM struct {
+ m []symbolInfo // Sorted list of addresses from binary.
+}
+
+type symbolInfo struct {
+ address uint64
+ name string
+}
+
+// newAddr2LinerNM starts the given nm command reporting information about the
+// given executable file. If file is a shared library, base should be
+// the address at which it was mapped in the program under
+// consideration.
+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
+ }
+
+ // Parse nm output and populate symbol map.
+ // Skip lines we fail to parse.
+ buf := bufio.NewReader(&b)
+ for {
+ line, err := buf.ReadString('\n')
+ if line == "" && err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+ line = strings.TrimSpace(line)
+ fields := strings.SplitN(line, " ", 3)
+ if len(fields) != 3 {
+ continue
+ }
+ address, err := strconv.ParseUint(fields[0], 16, 64)
+ if err != nil {
+ continue
+ }
+ a.m = append(a.m, symbolInfo{
+ address: address + base,
+ name: fields[2],
+ })
+ }
+
+ return a, nil
+}
+
+// addrInfo returns the stack frame information for a specific program
+// address. It returns nil if the address could not be identified.
+func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
+ if len(a.m) == 0 || addr < a.m[0].address || addr > a.m[len(a.m)-1].address {
+ return nil, nil
+ }
+
+ // Binary search. Search until low, high are separated by 1.
+ low, high := 0, len(a.m)
+ for low+1 < high {
+ mid := (low + high) / 2
+ v := a.m[mid].address
+ if addr == v {
+ low = mid
+ break
+ } else if addr > v {
+ low = mid
+ } else {
+ high = mid
+ }
+ }
+
+ // Address is between a.m[low] and a.m[high].
+ // Pick low, as it represents [low, high).
+ f := []plugin.Frame{
+ {
+ Func: a.m[low].name,
+ },
+ }
+ return f, 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
new file mode 100644
index 0000000000..9854c9a262
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
@@ -0,0 +1,305 @@
+// 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.
+
+// Package binutils provides access to the GNU binutils.
+package binutils
+
+import (
+ "debug/elf"
+ "debug/macho"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "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 {
+ // Commands to invoke.
+ llvmSymbolizer string
+ llvmSymbolizerFound bool
+ addr2line string
+ addr2lineFound bool
+ nm string
+ nmFound bool
+ objdump string
+ objdumpFound bool
+
+ // if fast, perform symbolization using nm (symbol names only),
+ // instead of file-line detail from the slower addr2line.
+ fast bool
+}
+
+// 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
+}
+
+// SetTools processes the contents of the tools option. It
+// expects a set of entries separated by commas; each entry is a pair
+// 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) {
+ // paths collect paths per tool; Key "" contains the default.
+ paths := make(map[string][]string)
+ for _, t := range strings.Split(config, ",") {
+ name, path := "", t
+ if ct := strings.SplitN(t, ":", 2); len(ct) == 2 {
+ name, path = ct[0], ct[1]
+ }
+ paths[name] = append(paths[name], path)
+ }
+
+ defaultPath := paths[""]
+ b.llvmSymbolizer, b.llvmSymbolizerFound = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
+ b.addr2line, b.addr2lineFound = findExe("addr2line", append(paths["addr2line"], defaultPath...))
+ b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
+ b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
+}
+
+// findExe looks for an executable command on a set of paths.
+// If it cannot find it, returns cmd.
+func findExe(cmd string, paths []string) (string, bool) {
+ for _, p := range paths {
+ cp := filepath.Join(p, cmd)
+ if c, err := exec.LookPath(cp); err == nil {
+ return c, true
+ }
+ }
+ return cmd, false
+}
+
+// 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("")
+ }
+ cmd := exec.Command(b.objdump, "-d", "-C", "--no-show-raw-insn", "-l",
+ fmt.Sprintf("--start-address=%#x", start),
+ fmt.Sprintf("--stop-address=%#x", end),
+ file)
+ out, err := cmd.Output()
+ if err != nil {
+ return nil, fmt.Errorf("%v: %v", cmd.Args, err)
+ }
+
+ return disassemble(out)
+}
+
+// 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("")
+ }
+
+ // Make sure file is a supported executable.
+ // The pprof driver uses Open to sniff the difference
+ // between an executable and a profile.
+ // For now, only ELF is supported.
+ // Could read the first few bytes of the file and
+ // use a table of prefixes if we need to support other
+ // systems at some point.
+
+ if _, err := os.Stat(name); err != nil {
+ // For testing, do not require file name to exist.
+ if strings.Contains(b.addr2line, "testdata/") {
+ return &fileAddr2Line{file: file{b: b, name: name}}, nil
+ }
+ return nil, err
+ }
+
+ if f, err := b.openELF(name, start, limit, offset); err == nil {
+ return f, nil
+ }
+ if f, err := b.openMachO(name, start, limit, offset); err == nil {
+ return f, nil
+ }
+ return nil, fmt.Errorf("unrecognized binary: %s", name)
+}
+
+func (b *Binutils) 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)
+ }
+ defer of.Close()
+
+ if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
+ return &fileNM{file: file{b: b, name: name}}, nil
+ }
+ return &fileAddr2Line{file: file{b: b, name: name}}, nil
+}
+
+func (b *Binutils) 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)
+ }
+ defer ef.Close()
+
+ var stextOffset *uint64
+ var pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
+ if strings.Contains(name, "vmlinux") || !pageAligned(start) || !pageAligned(limit) || !pageAligned(offset) {
+ // Reading all Symbols is expensive, and we only rarely need it so
+ // we don't want to do it every time. But if _stext happens to be
+ // page-aligned but isn't the same as Vaddr, we would symbolize
+ // wrong. So if the name the addresses aren't page aligned, or if
+ // the name is "vmlinux" we read _stext. We can be wrong if: (1)
+ // someone passes a kernel path that doesn't contain "vmlinux" AND
+ // (2) _stext is page-aligned AND (3) _stext is not at Vaddr
+ symbols, err := ef.Symbols()
+ if err != nil {
+ return nil, err
+ }
+ for _, s := range symbols {
+ if s.Name == "_stext" {
+ // The kernel may use _stext as the mapping start address.
+ stextOffset = &s.Value
+ break
+ }
+ }
+ }
+
+ base, err := elfexec.GetBase(&ef.FileHeader, nil, stextOffset, start, limit, offset)
+ if err != nil {
+ return nil, fmt.Errorf("Could not identify base for %s: %v", name, err)
+ }
+
+ buildID := ""
+ if f, err := os.Open(name); err == nil {
+ if id, err := elfexec.GetBuildID(f); err == nil {
+ buildID = fmt.Sprintf("%x", id)
+ }
+ }
+ if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
+ return &fileNM{file: file{b, name, base, buildID}}, nil
+ }
+ return &fileAddr2Line{file: file{b, name, base, buildID}}, nil
+}
+
+// file implements the binutils.ObjFile interface.
+type file struct {
+ b *Binutils
+ name string
+ base uint64
+ buildID string
+}
+
+func (f *file) Name() string {
+ return f.name
+}
+
+func (f *file) Base() uint64 {
+ return f.base
+}
+
+func (f *file) BuildID() string {
+ return f.buildID
+}
+
+func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
+ return []plugin.Frame{}, nil
+}
+
+func (f *file) Close() error {
+ return nil
+}
+
+func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
+ // Get from nm a list of symbols sorted by address.
+ cmd := exec.Command(f.b.nm, "-n", f.name)
+ out, err := cmd.Output()
+ if err != nil {
+ return nil, fmt.Errorf("%v: %v", cmd.Args, err)
+ }
+
+ return findSymbols(out, f.name, r, addr)
+}
+
+// fileNM implements the binutils.ObjFile interface, using 'nm' to map
+// addresses to symbols (without file/line number information). It is
+// faster than fileAddr2Line.
+type fileNM struct {
+ file
+ addr2linernm *addr2LinerNM
+}
+
+func (f *fileNM) SourceLine(addr uint64) ([]plugin.Frame, error) {
+ if f.addr2linernm == nil {
+ addr2liner, err := newAddr2LinerNM(f.b.nm, f.name, f.base)
+ if err != nil {
+ return nil, err
+ }
+ f.addr2linernm = addr2liner
+ }
+ return f.addr2linernm.addrInfo(addr)
+}
+
+// fileAddr2Line implements the binutils.ObjFile interface, using
+// 'addr2line' to map addresses to symbols (with file/line number
+// information). It can be slow for large binaries with debug
+// information.
+type fileAddr2Line struct {
+ file
+ addr2liner *addr2Liner
+ llvmSymbolizer *llvmSymbolizer
+}
+
+func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) {
+ if f.llvmSymbolizer != nil {
+ return f.llvmSymbolizer.addrInfo(addr)
+ }
+ if f.addr2liner != nil {
+ return f.addr2liner.addrInfo(addr)
+ }
+
+ if llvmSymbolizer, err := newLLVMSymbolizer(f.b.llvmSymbolizer, f.name, f.base); err == nil {
+ f.llvmSymbolizer = llvmSymbolizer
+ return f.llvmSymbolizer.addrInfo(addr)
+ }
+
+ if addr2liner, err := newAddr2Liner(f.b.addr2line, f.name, f.base); err == nil {
+ f.addr2liner = addr2liner
+
+ // When addr2line encounters some gcc compiled binaries, it
+ // drops interesting parts of names in anonymous namespaces.
+ // Fallback to NM for better function names.
+ 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.addr2liner != nil {
+ f.addr2liner.rw.close()
+ f.addr2liner = nil
+ }
+ return 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
new file mode 100644
index 0000000000..b7190e7ae2
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go
@@ -0,0 +1,152 @@
+// 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.
+
+package binutils
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/google/pprof/internal/plugin"
+)
+
+var testAddrMap = map[int]string{
+ 1000: "_Z3fooid.clone2",
+ 2000: "_ZNSaIiEC1Ev.clone18",
+ 3000: "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm",
+}
+
+func functionName(level int) (name string) {
+ if name = testAddrMap[level]; name != "" {
+ return name
+ }
+ return fmt.Sprintf("fun%d", level)
+}
+
+func TestAddr2Liner(t *testing.T) {
+ const offset = 0x500
+
+ a := addr2Liner{&mockAddr2liner{}, offset, nil}
+ for i := 1; i < 8; i++ {
+ addr := i*0x1000 + offset
+ s, err := a.addrInfo(uint64(addr))
+ if err != nil {
+ t.Fatalf("addrInfo(%#x): %v", addr, err)
+ }
+ if len(s) != i {
+ t.Fatalf("addrInfo(%#x): got len==%d, want %d", addr, len(s), i)
+ }
+ for l, f := range s {
+ level := (len(s) - l) * 1000
+ want := plugin.Frame{functionName(level), fmt.Sprintf("file%d", level), level}
+
+ if f != want {
+ t.Errorf("AddrInfo(%#x)[%d]: = %+v, want %+v", addr, l, f, want)
+ }
+ }
+ }
+ s, err := a.addrInfo(0xFFFF)
+ if err != nil {
+ t.Fatalf("addrInfo(0xFFFF): %v", err)
+ }
+ if len(s) != 0 {
+ t.Fatalf("AddrInfo(0xFFFF): got len==%d, want 0", len(s))
+ }
+ a.rw.close()
+}
+
+type mockAddr2liner struct {
+ output []string
+}
+
+func (a *mockAddr2liner) write(s string) error {
+ var lines []string
+ switch s {
+ case "1000":
+ lines = []string{"_Z3fooid.clone2", "file1000:1000"}
+ case "2000":
+ lines = []string{"_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "3000":
+ lines = []string{"_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "4000":
+ lines = []string{"fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "5000":
+ lines = []string{"fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "6000":
+ lines = []string{"fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "7000":
+ lines = []string{"fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "8000":
+ lines = []string{"fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ case "9000":
+ lines = []string{"fun9000", "file9000:9000", "fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
+ default:
+ lines = []string{"??", "??:0"}
+ }
+ a.output = append(a.output, "0x"+s)
+ a.output = append(a.output, lines...)
+ return nil
+}
+
+func (a *mockAddr2liner) readLine() (string, error) {
+ if len(a.output) == 0 {
+ return "", fmt.Errorf("end of file")
+ }
+ next := a.output[0]
+ a.output = a.output[1:]
+ return next, nil
+}
+
+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,
+ } {
+ for address, want := range map[uint64]string{
+ 0x1000: "0x1000",
+ 0x1001: "0x1000",
+ 0x1FFF: "0x1000",
+ 0x2000: "0x2000",
+ 0x2001: "0x2000",
+ } {
+ if got, _ := a.addrInfo(address); !checkAddress(got, address, want) {
+ t.Errorf("%x: got %v, want %s", address, got, want)
+ }
+ }
+ }
+}
+
+func checkAddress(got []plugin.Frame, address uint64, want string) bool {
+ if len(got) != 1 {
+ return false
+ }
+ return got[0].Func == want
+}
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
new file mode 100644
index 0000000000..fcdc555dc1
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
@@ -0,0 +1,147 @@
+// 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.
+
+package binutils
+
+import (
+ "bytes"
+ "io"
+ "regexp"
+ "strconv"
+
+ "github.com/google/pprof/internal/plugin"
+ "github.com/ianlancetaylor/demangle"
+)
+
+var (
+ nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
+ objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
+ objdumpOutputFileLine = regexp.MustCompile(`^(.*):([0-9]+)`)
+ objdumpOutputFunction = regexp.MustCompile(`^(\S.*)\(\):`)
+)
+
+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.
+ var symbols []*plugin.Sym
+ names, start := []string{}, uint64(0)
+ buf := bytes.NewBuffer(syms)
+ for symAddr, name, err := nextSymbol(buf); err == nil; symAddr, name, err = nextSymbol(buf) {
+ if err != nil {
+ return nil, err
+ }
+ if start == symAddr {
+ names = append(names, name)
+ continue
+ }
+ if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
+ symbols = append(symbols, &plugin.Sym{match, file, start, symAddr - 1})
+ }
+ names, start = []string{name}, symAddr
+ }
+
+ return symbols, nil
+}
+
+// matchSymbol checks if a symbol is to be selected by checking its
+// name to the regexp and optionally its address. It returns the name(s)
+// to be used for the matched symbol, or nil if no match
+func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, address uint64) []string {
+ if address != 0 && address >= start && address <= end {
+ return names
+ }
+ for _, name := range names {
+ if r.MatchString(name) {
+ return []string{name}
+ }
+
+ // Match all possible demangled versions of the name.
+ for _, o := range [][]demangle.Option{
+ {demangle.NoClones},
+ {demangle.NoParams},
+ {demangle.NoParams, demangle.NoTemplateParams},
+ } {
+ if demangled, err := demangle.ToString(name, o...); err == nil && r.MatchString(demangled) {
+ return []string{demangled}
+ }
+ }
+ }
+ return nil
+}
+
+// disassemble parses the output of the objdump command and returns
+// the assembly instructions in a slice.
+func disassemble(asm []byte) ([]plugin.Inst, error) {
+ buf := bytes.NewBuffer(asm)
+ function, file, line := "", "", 0
+ var assembly []plugin.Inst
+ for {
+ input, err := buf.ReadString('\n')
+ if err != nil {
+ if err != io.EOF {
+ return nil, err
+ }
+ if input == "" {
+ break
+ }
+ }
+
+ if fields := objdumpAsmOutputRE.FindStringSubmatch(input); len(fields) == 3 {
+ if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
+ assembly = append(assembly,
+ plugin.Inst{
+ Addr: address,
+ Text: fields[2],
+ Function: function,
+ File: file,
+ Line: line,
+ })
+ continue
+ }
+ }
+ if fields := objdumpOutputFileLine.FindStringSubmatch(input); len(fields) == 3 {
+ if l, err := strconv.ParseUint(fields[2], 10, 32); err == nil {
+ file, line = fields[1], int(l)
+ }
+ continue
+ }
+ if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
+ function = fields[1]
+ continue
+ }
+ // Reset on unrecognized lines.
+ function, file, line = "", "", 0
+ }
+
+ return assembly, nil
+}
+
+// nextSymbol parses the nm output to find the next symbol listed.
+// Skips over any output it cannot recognize.
+func nextSymbol(buf *bytes.Buffer) (uint64, string, error) {
+ for {
+ line, err := buf.ReadString('\n')
+ if err != nil {
+ if err != io.EOF || line == "" {
+ return 0, "", err
+ }
+ }
+
+ if fields := nmOutputRE.FindStringSubmatch(line); len(fields) == 4 {
+ if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
+ return address, fields[3], nil
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000000..bb08023884
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm_test.go
@@ -0,0 +1,154 @@
+// 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.
+
+package binutils
+
+import (
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/google/pprof/internal/plugin"
+)
+
+// TestFindSymbols tests the FindSymbols routine using a hardcoded nm output.
+func TestFindSymbols(t *testing.T) {
+ type testcase struct {
+ query, syms string
+ want []plugin.Sym
+ }
+
+ testsyms := `0000000000001000 t lineA001
+0000000000001000 t lineA002
+0000000000001000 t line1000
+0000000000002000 t line200A
+0000000000002000 t line2000
+0000000000002000 t line200B
+0000000000003000 t line3000
+0000000000003000 t _ZNK4DumbclEPKc
+0000000000003000 t lineB00C
+0000000000003000 t line300D
+0000000000004000 t _the_end
+ `
+ testcases := []testcase{
+ {
+ "line.*[AC]",
+ testsyms,
+ []plugin.Sym{
+ {[]string{"lineA001"}, "object.o", 0x1000, 0x1FFF},
+ {[]string{"line200A"}, "object.o", 0x2000, 0x2FFF},
+ {[]string{"lineB00C"}, "object.o", 0x3000, 0x3FFF},
+ },
+ },
+ {
+ "Dumb::operator",
+ testsyms,
+ []plugin.Sym{
+ {[]string{"Dumb::operator()(char const*) const"}, "object.o", 0x3000, 0x3FFF},
+ },
+ },
+ }
+
+ for _, tc := range testcases {
+ syms, err := findSymbols([]byte(tc.syms), "object.o", regexp.MustCompile(tc.query), 0)
+ if err != nil {
+ t.Fatalf("%q: findSymbols: %v", tc.query, err)
+ }
+ if err := checkSymbol(syms, tc.want); err != nil {
+ t.Errorf("%q: %v", tc.query, err)
+ }
+ }
+}
+
+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))
+ }
+
+ for i, g := range got {
+ w := want[i]
+ if len(g.Name) != len(w.Name) {
+ return fmt.Errorf("names, got %d, want %d", len(g.Name), len(w.Name))
+ }
+ for n := range g.Name {
+ if g.Name[n] != w.Name[n] {
+ return fmt.Errorf("name %d, got %q, want %q", n, g.Name[n], w.Name[n])
+ }
+ }
+ if g.File != w.File {
+ return fmt.Errorf("filename, got %q, want %q", g.File, w.File)
+ }
+ if g.Start != w.Start {
+ return fmt.Errorf("start address, got %#x, want %#x", g.Start, w.Start)
+ }
+ if g.End != w.End {
+ return fmt.Errorf("end address, got %#x, want %#x", g.End, w.End)
+ }
+ }
+ return nil
+}
+
+// TestFunctionAssembly tests the FunctionAssembly routine by using a
+// fake objdump script.
+func TestFunctionAssembly(t *testing.T) {
+ type testcase struct {
+ s plugin.Sym
+ asm string
+ want []plugin.Inst
+ }
+ testcases := []testcase{
+ {
+ plugin.Sym{[]string{"symbol1"}, "", 0x1000, 0x1FFF},
+ ` 1000: instruction one
+ 1001: instruction two
+ 1002: instruction three
+ 1003: instruction four
+`,
+ []plugin.Inst{
+ {Addr: 0x1000, Text: "instruction one"},
+ {Addr: 0x1001, Text: "instruction two"},
+ {Addr: 0x1002, Text: "instruction three"},
+ {Addr: 0x1003, Text: "instruction four"},
+ },
+ },
+ {
+ plugin.Sym{[]string{"symbol2"}, "", 0x2000, 0x2FFF},
+ ` 2000: instruction one
+ 2001: instruction two
+`,
+ []plugin.Inst{
+ {Addr: 0x2000, Text: "instruction one"},
+ {Addr: 0x2001, Text: "instruction two"},
+ },
+ },
+ }
+
+ const objdump = "testdata/wrapper/objdump"
+
+ for _, tc := range testcases {
+ insts, err := disassemble([]byte(tc.asm))
+ if err != nil {
+ t.Fatalf("FunctionAssembly: %v", err)
+ }
+
+ if len(insts) != len(tc.want) {
+ t.Errorf("Unexpected number of assembly instructions %d (want %d)\n", len(insts), len(tc.want))
+ }
+ for i := range insts {
+ if insts[i] != tc.want[i] {
+ t.Errorf("Expected symbol %v, got %v\n", tc.want[i], insts[i])
+ }
+ }
+ }
+}