Source file src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package binutils
16
17 import (
18 "bufio"
19 "fmt"
20 "io"
21 "os/exec"
22 "strconv"
23 "strings"
24 "sync"
25
26 "github.com/google/pprof/internal/plugin"
27 )
28
29 const (
30 defaultAddr2line = "addr2line"
31
32
33
34 sentinel = ^uint64(0)
35 )
36
37
38
39 type addr2Liner struct {
40 mu sync.Mutex
41 rw lineReaderWriter
42 base uint64
43
44
45
46
47
48
49 nm *addr2LinerNM
50 }
51
52
53
54
55 type lineReaderWriter interface {
56 write(string) error
57 readLine() (string, error)
58 close()
59 }
60
61 type addr2LinerJob struct {
62 cmd *exec.Cmd
63 in io.WriteCloser
64 out *bufio.Reader
65 }
66
67 func (a *addr2LinerJob) write(s string) error {
68 _, err := fmt.Fprint(a.in, s+"\n")
69 return err
70 }
71
72 func (a *addr2LinerJob) readLine() (string, error) {
73 return a.out.ReadString('\n')
74 }
75
76
77 func (a *addr2LinerJob) close() {
78 a.in.Close()
79 a.cmd.Wait()
80 }
81
82
83
84
85
86 func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
87 if cmd == "" {
88 cmd = defaultAddr2line
89 }
90
91 j := &addr2LinerJob{
92 cmd: exec.Command(cmd, "-aif", "-e", file),
93 }
94
95 var err error
96 if j.in, err = j.cmd.StdinPipe(); err != nil {
97 return nil, err
98 }
99
100 outPipe, err := j.cmd.StdoutPipe()
101 if err != nil {
102 return nil, err
103 }
104
105 j.out = bufio.NewReader(outPipe)
106 if err := j.cmd.Start(); err != nil {
107 return nil, err
108 }
109
110 a := &addr2Liner{
111 rw: j,
112 base: base,
113 }
114
115 return a, nil
116 }
117
118 func (d *addr2Liner) readString() (string, error) {
119 s, err := d.rw.readLine()
120 if err != nil {
121 return "", err
122 }
123 return strings.TrimSpace(s), nil
124 }
125
126
127
128
129 func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
130 funcname, err := d.readString()
131 if err != nil {
132 return plugin.Frame{}, true
133 }
134 if strings.HasPrefix(funcname, "0x") {
135
136
137
138 d.readString()
139 d.readString()
140 return plugin.Frame{}, true
141 }
142
143 fileline, err := d.readString()
144 if err != nil {
145 return plugin.Frame{}, true
146 }
147
148 linenumber := 0
149
150 if funcname == "??" {
151 funcname = ""
152 }
153
154 if fileline == "??:0" {
155 fileline = ""
156 } else {
157 if i := strings.LastIndex(fileline, ":"); i >= 0 {
158
159 if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
160 fileline = fileline[:disc]
161 }
162
163
164 if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
165 linenumber = line
166 fileline = fileline[:i]
167 }
168 }
169 }
170
171 return plugin.Frame{
172 Func: funcname,
173 File: fileline,
174 Line: linenumber}, false
175 }
176
177 func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
178 d.mu.Lock()
179 defer d.mu.Unlock()
180
181 if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
182 return nil, err
183 }
184
185 if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
186 return nil, err
187 }
188
189 resp, err := d.readString()
190 if err != nil {
191 return nil, err
192 }
193
194 if !strings.HasPrefix(resp, "0x") {
195 return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
196 }
197
198 var stack []plugin.Frame
199 for {
200 frame, end := d.readFrame()
201 if end {
202 break
203 }
204
205 if frame != (plugin.Frame{}) {
206 stack = append(stack, frame)
207 }
208 }
209 return stack, err
210 }
211
212
213
214 func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
215 stack, err := d.rawAddrInfo(addr)
216 if err != nil {
217 return nil, err
218 }
219
220
221
222
223 if len(stack) > 0 && d.nm != nil {
224 nm, err := d.nm.addrInfo(addr)
225 if err == nil && len(nm) > 0 {
226
227
228
229
230
231
232
233 nmName := nm[len(nm)-1].Func
234 a2lName := stack[len(stack)-1].Func
235 if len(nmName) > len(a2lName)+1 {
236 stack[len(stack)-1].Func = nmName
237 }
238 }
239 }
240
241 return stack, nil
242 }
243
View as plain text