|
@@ -24,14 +24,21 @@ import (
|
24
|
24
|
"path/filepath"
|
25
|
25
|
"regexp"
|
26
|
26
|
"strings"
|
|
27
|
+ "sync"
|
27
|
28
|
|
28
|
29
|
"github.com/google/pprof/internal/elfexec"
|
29
|
30
|
"github.com/google/pprof/internal/plugin"
|
30
|
31
|
)
|
31
|
32
|
|
32
|
33
|
// A Binutils implements plugin.ObjTool by invoking the GNU binutils.
|
33
|
|
-// SetConfig must be called before any of the other methods.
|
34
|
34
|
type Binutils struct {
|
|
35
|
+ sync.Mutex
|
|
36
|
+ rep *binrep
|
|
37
|
+}
|
|
38
|
+
|
|
39
|
+// binrep is an immutable representation for Binutils. It is atomically
|
|
40
|
+// replaced on every mutation to provide thread-safe access.
|
|
41
|
+type binrep struct {
|
35
|
42
|
// Commands to invoke.
|
36
|
43
|
llvmSymbolizer string
|
37
|
44
|
llvmSymbolizerFound bool
|
|
@@ -47,11 +54,38 @@ type Binutils struct {
|
47
|
54
|
fast bool
|
48
|
55
|
}
|
49
|
56
|
|
|
57
|
+// get returns the current representation for bu, initializing it if necessary.
|
|
58
|
+func (bu *Binutils) get() *binrep {
|
|
59
|
+ bu.Mutex.Lock()
|
|
60
|
+ r := bu.rep
|
|
61
|
+ if r == nil {
|
|
62
|
+ r = &binrep{}
|
|
63
|
+ initTools(r, "")
|
|
64
|
+ bu.rep = r
|
|
65
|
+ }
|
|
66
|
+ bu.Mutex.Unlock()
|
|
67
|
+ return r
|
|
68
|
+}
|
|
69
|
+
|
|
70
|
+// update modifies the rep for bu via the supplied function.
|
|
71
|
+func (bu *Binutils) update(fn func(r *binrep)) {
|
|
72
|
+ r := &binrep{}
|
|
73
|
+ bu.Mutex.Lock()
|
|
74
|
+ defer bu.Mutex.Unlock()
|
|
75
|
+ if bu.rep == nil {
|
|
76
|
+ initTools(r, "")
|
|
77
|
+ } else {
|
|
78
|
+ *r = *bu.rep
|
|
79
|
+ }
|
|
80
|
+ fn(r)
|
|
81
|
+ bu.rep = r
|
|
82
|
+}
|
|
83
|
+
|
50
|
84
|
// SetFastSymbolization sets a toggle that makes binutils use fast
|
51
|
85
|
// symbolization (using nm), which is much faster than addr2line but
|
52
|
86
|
// provides only symbol name information (no file/line).
|
53
|
|
-func (b *Binutils) SetFastSymbolization(fast bool) {
|
54
|
|
- b.fast = fast
|
|
87
|
+func (bu *Binutils) SetFastSymbolization(fast bool) {
|
|
88
|
+ bu.update(func(r *binrep) { r.fast = fast })
|
55
|
89
|
}
|
56
|
90
|
|
57
|
91
|
// SetTools processes the contents of the tools option. It
|
|
@@ -59,7 +93,11 @@ func (b *Binutils) SetFastSymbolization(fast bool) {
|
59
|
93
|
// of the form t:path, where cmd will be used to look only for the
|
60
|
94
|
// tool named t. If t is not specified, the path is searched for all
|
61
|
95
|
// tools.
|
62
|
|
-func (b *Binutils) SetTools(config string) {
|
|
96
|
+func (bu *Binutils) SetTools(config string) {
|
|
97
|
+ bu.update(func(r *binrep) { initTools(r, config) })
|
|
98
|
+}
|
|
99
|
+
|
|
100
|
+func initTools(b *binrep, config string) {
|
63
|
101
|
// paths collect paths per tool; Key "" contains the default.
|
64
|
102
|
paths := make(map[string][]string)
|
65
|
103
|
for _, t := range strings.Split(config, ",") {
|
|
@@ -91,11 +129,8 @@ func findExe(cmd string, paths []string) (string, bool) {
|
91
|
129
|
|
92
|
130
|
// Disasm returns the assembly instructions for the specified address range
|
93
|
131
|
// of a binary.
|
94
|
|
-func (b *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
|
95
|
|
- if b.addr2line == "" {
|
96
|
|
- // Update the command invocations if not initialized.
|
97
|
|
- b.SetTools("")
|
98
|
|
- }
|
|
132
|
+func (bu *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
|
|
133
|
+ b := bu.get()
|
99
|
134
|
cmd := exec.Command(b.objdump, "-d", "-C", "--no-show-raw-insn", "-l",
|
100
|
135
|
fmt.Sprintf("--start-address=%#x", start),
|
101
|
136
|
fmt.Sprintf("--stop-address=%#x", end),
|
|
@@ -109,11 +144,8 @@ func (b *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error)
|
109
|
144
|
}
|
110
|
145
|
|
111
|
146
|
// Open satisfies the plugin.ObjTool interface.
|
112
|
|
-func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
113
|
|
- if b.addr2line == "" {
|
114
|
|
- // Update the command invocations if not initialized.
|
115
|
|
- b.SetTools("")
|
116
|
|
- }
|
|
147
|
+func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
148
|
+ b := bu.get()
|
117
|
149
|
|
118
|
150
|
// Make sure file is a supported executable.
|
119
|
151
|
// The pprof driver uses Open to sniff the difference
|
|
@@ -140,7 +172,7 @@ func (b *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFil
|
140
|
172
|
return nil, fmt.Errorf("unrecognized binary: %s", name)
|
141
|
173
|
}
|
142
|
174
|
|
143
|
|
-func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
175
|
+func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
144
|
176
|
of, err := macho.Open(name)
|
145
|
177
|
if err != nil {
|
146
|
178
|
return nil, fmt.Errorf("Parsing %s: %v", name, err)
|
|
@@ -153,7 +185,7 @@ func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.O
|
153
|
185
|
return &fileAddr2Line{file: file{b: b, name: name}}, nil
|
154
|
186
|
}
|
155
|
187
|
|
156
|
|
-func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
188
|
+func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
157
|
189
|
ef, err := elf.Open(name)
|
158
|
190
|
if err != nil {
|
159
|
191
|
return nil, fmt.Errorf("Parsing %s: %v", name, err)
|
|
@@ -202,7 +234,7 @@ func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.Obj
|
202
|
234
|
|
203
|
235
|
// file implements the binutils.ObjFile interface.
|
204
|
236
|
type file struct {
|
205
|
|
- b *Binutils
|
|
237
|
+ b *binrep
|
206
|
238
|
name string
|
207
|
239
|
base uint64
|
208
|
240
|
buildID string
|
|
@@ -263,22 +295,27 @@ func (f *fileNM) SourceLine(addr uint64) ([]plugin.Frame, error) {
|
263
|
295
|
// information). It can be slow for large binaries with debug
|
264
|
296
|
// information.
|
265
|
297
|
type fileAddr2Line struct {
|
|
298
|
+ once sync.Once
|
266
|
299
|
file
|
267
|
300
|
addr2liner *addr2Liner
|
268
|
301
|
llvmSymbolizer *llvmSymbolizer
|
269
|
302
|
}
|
270
|
303
|
|
271
|
304
|
func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) {
|
|
305
|
+ f.once.Do(f.init)
|
272
|
306
|
if f.llvmSymbolizer != nil {
|
273
|
307
|
return f.llvmSymbolizer.addrInfo(addr)
|
274
|
308
|
}
|
275
|
309
|
if f.addr2liner != nil {
|
276
|
310
|
return f.addr2liner.addrInfo(addr)
|
277
|
311
|
}
|
|
312
|
+ return nil, fmt.Errorf("could not find local addr2liner")
|
|
313
|
+}
|
278
|
314
|
|
|
315
|
+func (f *fileAddr2Line) init() {
|
279
|
316
|
if llvmSymbolizer, err := newLLVMSymbolizer(f.b.llvmSymbolizer, f.name, f.base); err == nil {
|
280
|
317
|
f.llvmSymbolizer = llvmSymbolizer
|
281
|
|
- return f.llvmSymbolizer.addrInfo(addr)
|
|
318
|
+ return
|
282
|
319
|
}
|
283
|
320
|
|
284
|
321
|
if addr2liner, err := newAddr2Liner(f.b.addr2line, f.name, f.base); err == nil {
|
|
@@ -290,13 +327,14 @@ func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) {
|
290
|
327
|
if nm, err := newAddr2LinerNM(f.b.nm, f.name, f.base); err == nil {
|
291
|
328
|
f.addr2liner.nm = nm
|
292
|
329
|
}
|
293
|
|
- return f.addr2liner.addrInfo(addr)
|
294
|
330
|
}
|
295
|
|
-
|
296
|
|
- return nil, fmt.Errorf("could not find local addr2liner")
|
297
|
331
|
}
|
298
|
332
|
|
299
|
333
|
func (f *fileAddr2Line) Close() error {
|
|
334
|
+ if f.llvmSymbolizer != nil {
|
|
335
|
+ f.llvmSymbolizer.rw.close()
|
|
336
|
+ f.llvmSymbolizer = nil
|
|
337
|
+ }
|
300
|
338
|
if f.addr2liner != nil {
|
301
|
339
|
f.addr2liner.rw.close()
|
302
|
340
|
f.addr2liner = nil
|