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 }