1
2
3
4
5
6
7
8
9
10
11
12
13
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
33
34 type addr2LinerNM struct {
35 m []symbolInfo
36 }
37
38 type symbolInfo struct {
39 address uint64
40 size uint64
41 name string
42 symType string
43 }
44
45
46 func (s *symbolInfo) isData() bool {
47
48
49
50
51
52
53
54
55
56 return strings.ContainsAny(s.symType, "bBdDrRvVW")
57 }
58
59
60
61
62 func newAddr2LinerNM(cmd, file string, base uint64) (*addr2LinerNM, error) {
63 if cmd == "" {
64 cmd = defaultNM
65 }
66 var b bytes.Buffer
67 c := exec.Command(cmd, "--numeric-sort", "--print-size", "--format=posix", file)
68 c.Stdout = &b
69 if err := c.Run(); err != nil {
70 return nil, err
71 }
72 return parseAddr2LinerNM(base, &b)
73 }
74
75 func parseAddr2LinerNM(base uint64, nm io.Reader) (*addr2LinerNM, error) {
76 a := &addr2LinerNM{
77 m: []symbolInfo{},
78 }
79
80
81
82 buf := bufio.NewReader(nm)
83 for {
84 line, err := buf.ReadString('\n')
85 if line == "" && err != nil {
86 if err == io.EOF {
87 break
88 }
89 return nil, err
90 }
91 line = strings.TrimSpace(line)
92 fields := strings.Split(line, " ")
93 if len(fields) != 4 {
94 continue
95 }
96 address, err := strconv.ParseUint(fields[2], 16, 64)
97 if err != nil {
98 continue
99 }
100 size, err := strconv.ParseUint(fields[3], 16, 64)
101 if err != nil {
102 continue
103 }
104 a.m = append(a.m, symbolInfo{
105 address: address + base,
106 size: size,
107 name: fields[0],
108 symType: fields[1],
109 })
110 }
111
112 return a, nil
113 }
114
115
116
117 func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
118 if len(a.m) == 0 || addr < a.m[0].address || addr >= (a.m[len(a.m)-1].address+a.m[len(a.m)-1].size) {
119 return nil, nil
120 }
121
122
123 low, high := 0, len(a.m)
124 for low+1 < high {
125 mid := (low + high) / 2
126 v := a.m[mid].address
127 if addr == v {
128 low = mid
129 break
130 } else if addr > v {
131 low = mid
132 } else {
133 high = mid
134 }
135 }
136
137
138
139
140 if a.m[low].isData() && addr >= (a.m[low].address+a.m[low].size) {
141 return nil, nil
142 }
143 return []plugin.Frame{{Func: a.m[low].name}}, nil
144 }
145
View as plain text