浏览代码

Merge pull request #24 from rauls5382/mapping

Add symbolization support for Mach-O
Josef Jelinek 8 年前
父节点
当前提交
9848121c0b
共有 2 个文件被更改,包括 57 次插入24 次删除
  1. 47
    20
      internal/binutils/binutils.go
  2. 10
    4
      internal/driver/fetch.go

+ 47
- 20
internal/binutils/binutils.go 查看文件

17
 
17
 
18
 import (
18
 import (
19
 	"debug/elf"
19
 	"debug/elf"
20
+	"debug/macho"
20
 	"fmt"
21
 	"fmt"
21
 	"os"
22
 	"os"
22
 	"os/exec"
23
 	"os/exec"
32
 // SetConfig must be called before any of the other methods.
33
 // SetConfig must be called before any of the other methods.
33
 type Binutils struct {
34
 type Binutils struct {
34
 	// Commands to invoke.
35
 	// Commands to invoke.
35
-	llvmSymbolizer string
36
-	addr2line      string
37
-	nm             string
38
-	objdump        string
36
+	llvmSymbolizer      string
37
+	llvmSymbolizerFound bool
38
+	addr2line           string
39
+	addr2lineFound      bool
40
+	nm                  string
41
+	nmFound             bool
42
+	objdump             string
43
+	objdumpFound        bool
39
 
44
 
40
 	// if fast, perform symbolization using nm (symbol names only),
45
 	// if fast, perform symbolization using nm (symbol names only),
41
 	// instead of file-line detail from the slower addr2line.
46
 	// instead of file-line detail from the slower addr2line.
66
 	}
71
 	}
67
 
72
 
68
 	defaultPath := paths[""]
73
 	defaultPath := paths[""]
69
-	b.llvmSymbolizer = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
70
-	b.addr2line = findExe("addr2line", append(paths["addr2line"], defaultPath...))
71
-	b.nm = findExe("nm", append(paths["nm"], defaultPath...))
72
-	b.objdump = findExe("objdump", append(paths["objdump"], defaultPath...))
74
+	b.llvmSymbolizer, b.llvmSymbolizerFound = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
75
+	b.addr2line, b.addr2lineFound = findExe("addr2line", append(paths["addr2line"], defaultPath...))
76
+	b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
77
+	b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
73
 }
78
 }
74
 
79
 
75
 // findExe looks for an executable command on a set of paths.
80
 // findExe looks for an executable command on a set of paths.
76
 // If it cannot find it, returns cmd.
81
 // If it cannot find it, returns cmd.
77
-func findExe(cmd string, paths []string) string {
82
+func findExe(cmd string, paths []string) (string, bool) {
78
 	for _, p := range paths {
83
 	for _, p := range paths {
79
 		cp := filepath.Join(p, cmd)
84
 		cp := filepath.Join(p, cmd)
80
 		if c, err := exec.LookPath(cp); err == nil {
85
 		if c, err := exec.LookPath(cp); err == nil {
81
-			return c
86
+			return c, true
82
 		}
87
 		}
83
 	}
88
 	}
84
-	return cmd
89
+	return cmd, false
85
 }
90
 }
86
 
91
 
87
 // Disasm returns the assembly instructions for the specified address range
92
 // Disasm returns the assembly instructions for the specified address range
118
 	// use a table of prefixes if we need to support other
123
 	// use a table of prefixes if we need to support other
119
 	// systems at some point.
124
 	// systems at some point.
120
 
125
 
121
-	f, err := os.Open(name)
122
-	if err != nil {
126
+	if _, err := os.Stat(name); err != nil {
123
 		// For testing, do not require file name to exist.
127
 		// For testing, do not require file name to exist.
124
 		if strings.Contains(b.addr2line, "testdata/") {
128
 		if strings.Contains(b.addr2line, "testdata/") {
125
 			return &fileAddr2Line{file: file{b: b, name: name}}, nil
129
 			return &fileAddr2Line{file: file{b: b, name: name}}, nil
126
 		}
130
 		}
127
-
128
 		return nil, err
131
 		return nil, err
129
 	}
132
 	}
130
-	defer f.Close()
131
 
133
 
132
-	ef, err := elf.NewFile(f)
134
+	if f, err := b.openELF(name, start, limit, offset); err == nil {
135
+		return f, nil
136
+	}
137
+	if f, err := b.openMachO(name, start, limit, offset); err == nil {
138
+		return f, nil
139
+	}
140
+	return nil, fmt.Errorf("unrecognized binary: %s", name)
141
+}
142
+
143
+func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
144
+	of, err := macho.Open(name)
145
+	if err != nil {
146
+		return nil, fmt.Errorf("Parsing %s: %v", name, err)
147
+	}
148
+	defer of.Close()
149
+
150
+	if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
151
+		return &fileNM{file: file{b: b, name: name}}, nil
152
+	}
153
+	return &fileAddr2Line{file: file{b: b, name: name}}, nil
154
+}
155
+
156
+func (b *Binutils) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
157
+	ef, err := elf.Open(name)
133
 	if err != nil {
158
 	if err != nil {
134
 		return nil, fmt.Errorf("Parsing %s: %v", name, err)
159
 		return nil, fmt.Errorf("Parsing %s: %v", name, err)
135
 	}
160
 	}
161
+	defer ef.Close()
136
 
162
 
137
 	var stextOffset *uint64
163
 	var stextOffset *uint64
138
 	var pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
164
 	var pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
162
 		return nil, fmt.Errorf("Could not identify base for %s: %v", name, err)
188
 		return nil, fmt.Errorf("Could not identify base for %s: %v", name, err)
163
 	}
189
 	}
164
 
190
 
165
-	// Find build ID, while we have the file open.
166
 	buildID := ""
191
 	buildID := ""
167
-	if id, err := elfexec.GetBuildID(f); err == nil {
168
-		buildID = fmt.Sprintf("%x", id)
192
+	if f, err := os.Open(name); err == nil {
193
+		if id, err := elfexec.GetBuildID(f); err == nil {
194
+			buildID = fmt.Sprintf("%x", id)
195
+		}
169
 	}
196
 	}
170
-	if b.fast {
197
+	if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
171
 		return &fileNM{file: file{b, name, base, buildID}}, nil
198
 		return &fileNM{file: file{b, name, base, buildID}}, nil
172
 	}
199
 	}
173
 	return &fileAddr2Line{file: file{b, name, base, buildID}}, nil
200
 	return &fileAddr2Line{file: file{b, name, base, buildID}}, nil

+ 10
- 4
internal/driver/fetch.go 查看文件

273
 		}{
273
 		}{
274
 			source, m.Start,
274
 			source, m.Start,
275
 		}
275
 		}
276
-		if key := m.BuildID; key != "" {
277
-			ms[key] = append(ms[key], src)
276
+		key := m.BuildID
277
+		if key == "" {
278
+			key = m.File
278
 		}
279
 		}
279
-		if key := m.File; key != "" {
280
-			ms[key] = append(ms[key], src)
280
+		if key == "" {
281
+			// If there is no build id or source file, use the source as the
282
+			// mapping file. This will enable remote symbolization for this
283
+			// mapping, in particular for Go profiles on the legacy format.
284
+			m.File = source
285
+			key = source
281
 		}
286
 		}
287
+		ms[key] = append(ms[key], src)
282
 	}
288
 	}
283
 	return ms
289
 	return ms
284
 }
290
 }