...

Source file src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_nm.go

     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	

View as plain text