golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go (about)

     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  
    52  	a := &addr2LinerNM{
    53  		m: []symbolInfo{},
    54  	}
    55  
    56  	var b bytes.Buffer
    57  	c := exec.Command(cmd, "-n", file)
    58  	c.Stdout = &b
    59  
    60  	if err := c.Run(); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Parse nm output and populate symbol map.
    65  	// Skip lines we fail to parse.
    66  	buf := bufio.NewReader(&b)
    67  	for {
    68  		line, err := buf.ReadString('\n')
    69  		if line == "" && err != nil {
    70  			if err == io.EOF {
    71  				break
    72  			}
    73  			return nil, err
    74  		}
    75  		line = strings.TrimSpace(line)
    76  		fields := strings.SplitN(line, " ", 3)
    77  		if len(fields) != 3 {
    78  			continue
    79  		}
    80  		address, err := strconv.ParseUint(fields[0], 16, 64)
    81  		if err != nil {
    82  			continue
    83  		}
    84  		a.m = append(a.m, symbolInfo{
    85  			address: address + base,
    86  			name:    fields[2],
    87  		})
    88  	}
    89  
    90  	return a, nil
    91  }
    92  
    93  // addrInfo returns the stack frame information for a specific program
    94  // address. It returns nil if the address could not be identified.
    95  func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
    96  	if len(a.m) == 0 || addr < a.m[0].address || addr > a.m[len(a.m)-1].address {
    97  		return nil, nil
    98  	}
    99  
   100  	// Binary search. Search until low, high are separated by 1.
   101  	low, high := 0, len(a.m)
   102  	for low+1 < high {
   103  		mid := (low + high) / 2
   104  		v := a.m[mid].address
   105  		if addr == v {
   106  			low = mid
   107  			break
   108  		} else if addr > v {
   109  			low = mid
   110  		} else {
   111  			high = mid
   112  		}
   113  	}
   114  
   115  	// Address is between a.m[low] and a.m[high].
   116  	// Pick low, as it represents [low, high).
   117  	f := []plugin.Frame{
   118  		{
   119  			Func: a.m[low].name,
   120  		},
   121  	}
   122  	return f, nil
   123  }