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 s, err := a.out.ReadString('\n')
74 if err != nil {
75 return "", err
76 }
77 return strings.TrimSpace(s), nil
78 }
79
80
81 func (a *addr2LinerJob) close() {
82 a.in.Close()
83 a.cmd.Wait()
84 }
85
86
87
88
89
90 func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
91 if cmd == "" {
92 cmd = defaultAddr2line
93 }
94
95 j := &addr2LinerJob{
96 cmd: exec.Command(cmd, "-aif", "-e", file),
97 }
98
99 var err error
100 if j.in, err = j.cmd.StdinPipe(); err != nil {
101 return nil, err
102 }
103
104 outPipe, err := j.cmd.StdoutPipe()
105 if err != nil {
106 return nil, err
107 }
108
109 j.out = bufio.NewReader(outPipe)
110 if err := j.cmd.Start(); err != nil {
111 return nil, err
112 }
113
114 a := &addr2Liner{
115 rw: j,
116 base: base,
117 }
118
119 return a, nil
120 }
121
122
123
124
125 func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
126 funcname, err := d.rw.readLine()
127 if err != nil {
128 return plugin.Frame{}, true
129 }
130 if strings.HasPrefix(funcname, "0x") {
131
132
133
134 d.rw.readLine()
135 d.rw.readLine()
136 return plugin.Frame{}, true
137 }
138
139 fileline, err := d.rw.readLine()
140 if err != nil {
141 return plugin.Frame{}, true
142 }
143
144 linenumber := 0
145
146 if funcname == "??" {
147 funcname = ""
148 }
149
150 if fileline == "??:0" {
151 fileline = ""
152 } else {
153 if i := strings.LastIndex(fileline, ":"); i >= 0 {
154
155 if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
156 fileline = fileline[:disc]
157 }
158
159
160 if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
161 linenumber = line
162 fileline = fileline[:i]
163 }
164 }
165 }
166
167 return plugin.Frame{
168 Func: funcname,
169 File: fileline,
170 Line: linenumber}, false
171 }
172
173 func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
174 d.mu.Lock()
175 defer d.mu.Unlock()
176
177 if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
178 return nil, err
179 }
180
181 if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
182 return nil, err
183 }
184
185 resp, err := d.rw.readLine()
186 if err != nil {
187 return nil, err
188 }
189
190 if !strings.HasPrefix(resp, "0x") {
191 return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
192 }
193
194 var stack []plugin.Frame
195 for {
196 frame, end := d.readFrame()
197 if end {
198 break
199 }
200
201 if frame != (plugin.Frame{}) {
202 stack = append(stack, frame)
203 }
204 }
205 return stack, err
206 }
207
208
209
210 func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
211 stack, err := d.rawAddrInfo(addr)
212 if err != nil {
213 return nil, err
214 }
215
216
217
218
219 if len(stack) > 0 && d.nm != nil {
220 nm, err := d.nm.addrInfo(addr)
221 if err == nil && len(nm) > 0 {
222
223
224
225
226
227
228
229 nmName := nm[len(nm)-1].Func
230 a2lName := stack[len(stack)-1].Func
231 if len(nmName) > len(a2lName)+1 {
232 stack[len(stack)-1].Func = nmName
233 }
234 }
235 }
236
237 return stack, nil
238 }
239
View as plain text