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 	"bytes"
     20 	"io"
     21 	"os/exec"
     22 	"strconv"
     23 	"strings"
     24 
     25 	"github.com/google/pprof/internal/plugin"
     26 )
     27 
     28 const (
     29 	defaultNM = "nm"
     30 )
     31 
     32 // addr2LinerNM is a connection to an nm command for obtaining address
     33 // information from a binary.
     34 type addr2LinerNM struct {
     35 	m []symbolInfo // Sorted list of addresses from binary.
     36 }
     37 
     38 type symbolInfo struct {
     39 	address uint64
     40 	name    string
     41 }
     42 
     43 //  newAddr2LinerNM starts the given nm command reporting information about the
     44 // given executable file. If file is a shared library, base should be
     45 // the address at which it was mapped in the program under
     46 // consideration.
     47 func newAddr2LinerNM(cmd, file string, base uint64) (*addr2LinerNM, error) {
     48 	if cmd == "" {
     49 		cmd = defaultNM
     50 	}
     51 	var b bytes.Buffer
     52 	c := exec.Command(cmd, "-n", file)
     53 	c.Stdout = &b
     54 	if err := c.Run(); err != nil {
     55 		return nil, err
     56 	}
     57 	return parseAddr2LinerNM(base, &b)
     58 }
     59 
     60 func parseAddr2LinerNM(base uint64, nm io.Reader) (*addr2LinerNM, error) {
     61 	a := &addr2LinerNM{
     62 		m: []symbolInfo{},
     63 	}
     64 
     65 	// Parse nm output and populate symbol map.
     66 	// Skip lines we fail to parse.
     67 	buf := bufio.NewReader(nm)
     68 	for {
     69 		line, err := buf.ReadString('\n')
     70 		if line == "" && err != nil {
     71 			if err == io.EOF {
     72 				break
     73 			}
     74 			return nil, err
     75 		}
     76 		line = strings.TrimSpace(line)
     77 		fields := strings.SplitN(line, " ", 3)
     78 		if len(fields) != 3 {
     79 			continue
     80 		}
     81 		address, err := strconv.ParseUint(fields[0], 16, 64)
     82 		if err != nil {
     83 			continue
     84 		}
     85 		a.m = append(a.m, symbolInfo{
     86 			address: address + base,
     87 			name:    fields[2],
     88 		})
     89 	}
     90 
     91 	return a, nil
     92 }
     93 
     94 // addrInfo returns the stack frame information for a specific program
     95 // address. It returns nil if the address could not be identified.
     96 func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
     97 	if len(a.m) == 0 || addr < a.m[0].address || addr > a.m[len(a.m)-1].address {
     98 		return nil, nil
     99 	}
    100 
    101 	// Binary search. Search until low, high are separated by 1.
    102 	low, high := 0, len(a.m)
    103 	for low+1 < high {
    104 		mid := (low + high) / 2
    105 		v := a.m[mid].address
    106 		if addr == v {
    107 			low = mid
    108 			break
    109 		} else if addr > v {
    110 			low = mid
    111 		} else {
    112 			high = mid
    113 		}
    114 	}
    115 
    116 	// Address is between a.m[low] and a.m[high].
    117 	// Pick low, as it represents [low, high).
    118 	f := []plugin.Frame{
    119 		{
    120 			Func: a.m[low].name,
    121 		},
    122 	}
    123 	return f, nil
    124 }
    125