Home | History | Annotate | Download | only in binutils
      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 NM based addr2Liner which can provide
     45 	// better full names compared to addr2line, which often drops
     46 	// namespaces etc. from the names it returns.
     47 	nm *addr2LinerNM
     48 }
     49 
     50 // lineReaderWriter is an interface to abstract the I/O to an addr2line
     51 // process. It writes a line of input to the job, and reads its output
     52 // one line at a time.
     53 type lineReaderWriter interface {
     54 	write(string) error
     55 	readLine() (string, error)
     56 	close()
     57 }
     58 
     59 type addr2LinerJob struct {
     60 	cmd *exec.Cmd
     61 	in  io.WriteCloser
     62 	out *bufio.Reader
     63 }
     64 
     65 func (a *addr2LinerJob) write(s string) error {
     66 	_, err := fmt.Fprint(a.in, s+"\n")
     67 	return err
     68 }
     69 
     70 func (a *addr2LinerJob) readLine() (string, error) {
     71 	return a.out.ReadString('\n')
     72 }
     73 
     74 // close releases any resources used by the addr2liner object.
     75 func (a *addr2LinerJob) close() {
     76 	a.in.Close()
     77 	a.cmd.Wait()
     78 }
     79 
     80 // newAddr2liner starts the given addr2liner command reporting
     81 // information about the given executable file. If file is a shared
     82 // library, base should be the address at which it was mapped in the
     83 // program under consideration.
     84 func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
     85 	if cmd == "" {
     86 		cmd = defaultAddr2line
     87 	}
     88 
     89 	j := &addr2LinerJob{
     90 		cmd: exec.Command(cmd, "-aif", "-e", file),
     91 	}
     92 
     93 	var err error
     94 	if j.in, err = j.cmd.StdinPipe(); err != nil {
     95 		return nil, err
     96 	}
     97 
     98 	outPipe, err := j.cmd.StdoutPipe()
     99 	if err != nil {
    100 		return nil, err
    101 	}
    102 
    103 	j.out = bufio.NewReader(outPipe)
    104 	if err := j.cmd.Start(); err != nil {
    105 		return nil, err
    106 	}
    107 
    108 	a := &addr2Liner{
    109 		rw:   j,
    110 		base: base,
    111 	}
    112 
    113 	return a, nil
    114 }
    115 
    116 func (d *addr2Liner) readString() (string, error) {
    117 	s, err := d.rw.readLine()
    118 	if err != nil {
    119 		return "", err
    120 	}
    121 	return strings.TrimSpace(s), nil
    122 }
    123 
    124 // readFrame parses the addr2line output for a single address. It
    125 // returns a populated plugin.Frame and whether it has reached the end of the
    126 // data.
    127 func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
    128 	funcname, err := d.readString()
    129 	if err != nil {
    130 		return plugin.Frame{}, true
    131 	}
    132 	if strings.HasPrefix(funcname, "0x") {
    133 		// If addr2line returns a hex address we can assume it is the
    134 		// sentinel. Read and ignore next two lines of output from
    135 		// addr2line
    136 		d.readString()
    137 		d.readString()
    138 		return plugin.Frame{}, true
    139 	}
    140 
    141 	fileline, err := d.readString()
    142 	if err != nil {
    143 		return plugin.Frame{}, true
    144 	}
    145 
    146 	linenumber := 0
    147 
    148 	if funcname == "??" {
    149 		funcname = ""
    150 	}
    151 
    152 	if fileline == "??:0" {
    153 		fileline = ""
    154 	} else {
    155 		if i := strings.LastIndex(fileline, ":"); i >= 0 {
    156 			// Remove discriminator, if present
    157 			if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
    158 				fileline = fileline[:disc]
    159 			}
    160 			// If we cannot parse a number after the last ":", keep it as
    161 			// part of the filename.
    162 			if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
    163 				linenumber = line
    164 				fileline = fileline[:i]
    165 			}
    166 		}
    167 	}
    168 
    169 	return plugin.Frame{
    170 		Func: funcname,
    171 		File: fileline,
    172 		Line: linenumber}, false
    173 }
    174 
    175 func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
    176 	d.mu.Lock()
    177 	defer d.mu.Unlock()
    178 
    179 	if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
    180 		return nil, err
    181 	}
    182 
    183 	if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
    184 		return nil, err
    185 	}
    186 
    187 	resp, err := d.readString()
    188 	if err != nil {
    189 		return nil, err
    190 	}
    191 
    192 	if !strings.HasPrefix(resp, "0x") {
    193 		return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
    194 	}
    195 
    196 	var stack []plugin.Frame
    197 	for {
    198 		frame, end := d.readFrame()
    199 		if end {
    200 			break
    201 		}
    202 
    203 		if frame != (plugin.Frame{}) {
    204 			stack = append(stack, frame)
    205 		}
    206 	}
    207 	return stack, err
    208 }
    209 
    210 // addrInfo returns the stack frame information for a specific program
    211 // address. It returns nil if the address could not be identified.
    212 func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
    213 	stack, err := d.rawAddrInfo(addr)
    214 	if err != nil {
    215 		return nil, err
    216 	}
    217 
    218 	// Get better name from nm if possible.
    219 	if len(stack) > 0 && d.nm != nil {
    220 		nm, err := d.nm.addrInfo(addr)
    221 		if err == nil && len(nm) > 0 {
    222 			// Last entry in frame list should match since
    223 			// it is non-inlined. As a simple heuristic,
    224 			// we only switch to the nm-based name if it
    225 			// is longer.
    226 			nmName := nm[len(nm)-1].Func
    227 			a2lName := stack[len(stack)-1].Func
    228 			if len(nmName) > len(a2lName) {
    229 				stack[len(stack)-1].Func = nmName
    230 			}
    231 		}
    232 	}
    233 
    234 	return stack, nil
    235 }
    236