"Fossies" - the Fresh Open Source Software Archive

Member "go/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go" (9 Sep 2020, 6091 Bytes) of package /windows/misc/go1.14.9.windows-386.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 Google Inc. All Rights Reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 //
    7 //     http://www.apache.org/licenses/LICENSE-2.0
    8 //
    9 // Unless required by applicable law or agreed to in writing, software
   10 // distributed under the License is distributed on an "AS IS" BASIS,
   11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12 // See the License for the specific language governing permissions and
   13 // limitations under the License.
   14 
   15 package binutils
   16 
   17 import (
   18     "bufio"
   19     "fmt"
   20     "io"
   21     "os/exec"
   22     "strconv"
   23     "strings"
   24     "sync"
   25 
   26     "github.com/google/pprof/internal/plugin"
   27 )
   28 
   29 const (
   30     defaultAddr2line = "addr2line"
   31 
   32     // addr2line may produce multiple lines of output. We
   33     // use this sentinel to identify the end of the output.
   34     sentinel = ^uint64(0)
   35 )
   36 
   37 // addr2Liner is a connection to an addr2line command for obtaining
   38 // address and line number information from a binary.
   39 type addr2Liner struct {
   40     mu   sync.Mutex
   41     rw   lineReaderWriter
   42     base uint64
   43 
   44     // nm holds an addr2Liner using nm tool. Certain versions of addr2line
   45     // produce incomplete names due to
   46     // https://sourceware.org/bugzilla/show_bug.cgi?id=17541. As a workaround,
   47     // the names from nm are used when they look more complete. See addrInfo()
   48     // code below for the exact heuristic.
   49     nm *addr2LinerNM
   50 }
   51 
   52 // lineReaderWriter is an interface to abstract the I/O to an addr2line
   53 // process. It writes a line of input to the job, and reads its output
   54 // one line at a time.
   55 type lineReaderWriter interface {
   56     write(string) error
   57     readLine() (string, error)
   58     close()
   59 }
   60 
   61 type addr2LinerJob struct {
   62     cmd *exec.Cmd
   63     in  io.WriteCloser
   64     out *bufio.Reader
   65 }
   66 
   67 func (a *addr2LinerJob) write(s string) error {
   68     _, err := fmt.Fprint(a.in, s+"\n")
   69     return err
   70 }
   71 
   72 func (a *addr2LinerJob) readLine() (string, error) {
   73     return a.out.ReadString('\n')
   74 }
   75 
   76 // close releases any resources used by the addr2liner object.
   77 func (a *addr2LinerJob) close() {
   78     a.in.Close()
   79     a.cmd.Wait()
   80 }
   81 
   82 // newAddr2liner starts the given addr2liner command reporting
   83 // information about the given executable file. If file is a shared
   84 // library, base should be the address at which it was mapped in the
   85 // program under consideration.
   86 func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
   87     if cmd == "" {
   88         cmd = defaultAddr2line
   89     }
   90 
   91     j := &addr2LinerJob{
   92         cmd: exec.Command(cmd, "-aif", "-e", file),
   93     }
   94 
   95     var err error
   96     if j.in, err = j.cmd.StdinPipe(); err != nil {
   97         return nil, err
   98     }
   99 
  100     outPipe, err := j.cmd.StdoutPipe()
  101     if err != nil {
  102         return nil, err
  103     }
  104 
  105     j.out = bufio.NewReader(outPipe)
  106     if err := j.cmd.Start(); err != nil {
  107         return nil, err
  108     }
  109 
  110     a := &addr2Liner{
  111         rw:   j,
  112         base: base,
  113     }
  114 
  115     return a, nil
  116 }
  117 
  118 func (d *addr2Liner) readString() (string, error) {
  119     s, err := d.rw.readLine()
  120     if err != nil {
  121         return "", err
  122     }
  123     return strings.TrimSpace(s), nil
  124 }
  125 
  126 // readFrame parses the addr2line output for a single address. It
  127 // returns a populated plugin.Frame and whether it has reached the end of the
  128 // data.
  129 func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
  130     funcname, err := d.readString()
  131     if err != nil {
  132         return plugin.Frame{}, true
  133     }
  134     if strings.HasPrefix(funcname, "0x") {
  135         // If addr2line returns a hex address we can assume it is the
  136         // sentinel. Read and ignore next two lines of output from
  137         // addr2line
  138         d.readString()
  139         d.readString()
  140         return plugin.Frame{}, true
  141     }
  142 
  143     fileline, err := d.readString()
  144     if err != nil {
  145         return plugin.Frame{}, true
  146     }
  147 
  148     linenumber := 0
  149 
  150     if funcname == "??" {
  151         funcname = ""
  152     }
  153 
  154     if fileline == "??:0" {
  155         fileline = ""
  156     } else {
  157         if i := strings.LastIndex(fileline, ":"); i >= 0 {
  158             // Remove discriminator, if present
  159             if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
  160                 fileline = fileline[:disc]
  161             }
  162             // If we cannot parse a number after the last ":", keep it as
  163             // part of the filename.
  164             if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
  165                 linenumber = line
  166                 fileline = fileline[:i]
  167             }
  168         }
  169     }
  170 
  171     return plugin.Frame{
  172         Func: funcname,
  173         File: fileline,
  174         Line: linenumber}, false
  175 }
  176 
  177 func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
  178     d.mu.Lock()
  179     defer d.mu.Unlock()
  180 
  181     if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
  182         return nil, err
  183     }
  184 
  185     if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
  186         return nil, err
  187     }
  188 
  189     resp, err := d.readString()
  190     if err != nil {
  191         return nil, err
  192     }
  193 
  194     if !strings.HasPrefix(resp, "0x") {
  195         return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
  196     }
  197 
  198     var stack []plugin.Frame
  199     for {
  200         frame, end := d.readFrame()
  201         if end {
  202             break
  203         }
  204 
  205         if frame != (plugin.Frame{}) {
  206             stack = append(stack, frame)
  207         }
  208     }
  209     return stack, err
  210 }
  211 
  212 // addrInfo returns the stack frame information for a specific program
  213 // address. It returns nil if the address could not be identified.
  214 func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
  215     stack, err := d.rawAddrInfo(addr)
  216     if err != nil {
  217         return nil, err
  218     }
  219 
  220     // Certain versions of addr2line produce incomplete names due to
  221     // https://sourceware.org/bugzilla/show_bug.cgi?id=17541. Attempt to replace
  222     // the name with a better one from nm.
  223     if len(stack) > 0 && d.nm != nil {
  224         nm, err := d.nm.addrInfo(addr)
  225         if err == nil && len(nm) > 0 {
  226             // Last entry in frame list should match since it is non-inlined. As a
  227             // simple heuristic, we only switch to the nm-based name if it is longer
  228             // by 2 or more characters. We consider nm names that are longer by 1
  229             // character insignificant to avoid replacing foo with _foo on MacOS (for
  230             // unknown reasons read2line produces the former and nm produces the
  231             // latter on MacOS even though both tools are asked to produce mangled
  232             // names).
  233             nmName := nm[len(nm)-1].Func
  234             a2lName := stack[len(stack)-1].Func
  235             if len(nmName) > len(a2lName)+1 {
  236                 stack[len(stack)-1].Func = nmName
  237             }
  238         }
  239     }
  240 
  241     return stack, nil
  242 }