1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package symbolizer
19
20 import (
21 "fmt"
22 "io/ioutil"
23 "net/http"
24 "net/url"
25 "path/filepath"
26 "strings"
27
28 "github.com/google/pprof/internal/binutils"
29 "github.com/google/pprof/internal/plugin"
30 "github.com/google/pprof/internal/symbolz"
31 "github.com/google/pprof/profile"
32 "github.com/ianlancetaylor/demangle"
33 )
34
35
36 type Symbolizer struct {
37 Obj plugin.ObjTool
38 UI plugin.UI
39 Transport http.RoundTripper
40 }
41
42
43 var symbolzSymbolize = symbolz.Symbolize
44 var localSymbolize = doLocalSymbolize
45 var demangleFunction = Demangle
46
47
48
49
50 func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *profile.Profile) error {
51 remote, local, fast, force, demanglerMode := true, true, false, false, ""
52 for _, o := range strings.Split(strings.ToLower(mode), ":") {
53 switch o {
54 case "":
55 continue
56 case "none", "no":
57 return nil
58 case "local":
59 remote, local = false, true
60 case "fastlocal":
61 remote, local, fast = false, true, true
62 case "remote":
63 remote, local = true, false
64 case "force":
65 force = true
66 default:
67 switch d := strings.TrimPrefix(o, "demangle="); d {
68 case "full", "none", "templates":
69 demanglerMode = d
70 force = true
71 continue
72 case "default":
73 continue
74 }
75 s.UI.PrintErr("ignoring unrecognized symbolization option: " + mode)
76 s.UI.PrintErr("expecting -symbolize=[local|fastlocal|remote|none][:force][:demangle=[none|full|templates|default]")
77 }
78 }
79
80 var err error
81 if local {
82
83 if err = localSymbolize(p, fast, force, s.Obj, s.UI); err != nil {
84 s.UI.PrintErr("local symbolization: " + err.Error())
85 }
86 }
87 if remote {
88 post := func(source, post string) ([]byte, error) {
89 return postURL(source, post, s.Transport)
90 }
91 if err = symbolzSymbolize(p, force, sources, post, s.UI); err != nil {
92 return err
93 }
94 }
95
96 demangleFunction(p, force, demanglerMode)
97 return nil
98 }
99
100
101 func postURL(source, post string, tr http.RoundTripper) ([]byte, error) {
102 client := &http.Client{
103 Transport: tr,
104 }
105 resp, err := client.Post(source, "application/octet-stream", strings.NewReader(post))
106 if err != nil {
107 return nil, fmt.Errorf("http post %s: %v", source, err)
108 }
109 defer resp.Body.Close()
110 if resp.StatusCode != http.StatusOK {
111 return nil, fmt.Errorf("http post %s: %v", source, statusCodeError(resp))
112 }
113 return ioutil.ReadAll(resp.Body)
114 }
115
116 func statusCodeError(resp *http.Response) error {
117 if resp.Header.Get("X-Go-Pprof") != "" && strings.Contains(resp.Header.Get("Content-Type"), "text/plain") {
118
119 if body, err := ioutil.ReadAll(resp.Body); err == nil {
120 return fmt.Errorf("server response: %s - %s", resp.Status, body)
121 }
122 }
123 return fmt.Errorf("server response: %s", resp.Status)
124 }
125
126
127
128
129 func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error {
130 if fast {
131 if bu, ok := obj.(*binutils.Binutils); ok {
132 bu.SetFastSymbolization(true)
133 }
134 }
135
136 mt, err := newMapping(prof, obj, ui, force)
137 if err != nil {
138 return err
139 }
140 defer mt.close()
141
142 functions := make(map[profile.Function]*profile.Function)
143 for _, l := range mt.prof.Location {
144 m := l.Mapping
145 segment := mt.segments[m]
146 if segment == nil {
147
148 continue
149 }
150
151 stack, err := segment.SourceLine(l.Address)
152 if err != nil || len(stack) == 0 {
153
154 continue
155 }
156
157 l.Line = make([]profile.Line, len(stack))
158 l.IsFolded = false
159 for i, frame := range stack {
160 if frame.Func != "" {
161 m.HasFunctions = true
162 }
163 if frame.File != "" {
164 m.HasFilenames = true
165 }
166 if frame.Line != 0 {
167 m.HasLineNumbers = true
168 }
169 f := &profile.Function{
170 Name: frame.Func,
171 SystemName: frame.Func,
172 Filename: frame.File,
173 }
174 if fp := functions[*f]; fp != nil {
175 f = fp
176 } else {
177 functions[*f] = f
178 f.ID = uint64(len(mt.prof.Function)) + 1
179 mt.prof.Function = append(mt.prof.Function, f)
180 }
181 l.Line[i] = profile.Line{
182 Function: f,
183 Line: int64(frame.Line),
184 }
185 }
186
187 if len(stack) > 0 {
188 m.HasInlineFrames = true
189 }
190 }
191
192 return nil
193 }
194
195
196
197
198 func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
199 if force {
200
201 for _, f := range prof.Function {
202 if f.Name != "" && f.SystemName != "" {
203 f.Name = f.SystemName
204 }
205 }
206 }
207
208 var options []demangle.Option
209 switch demanglerMode {
210 case "":
211 options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
212 case "templates":
213 options = []demangle.Option{demangle.NoParams}
214 case "full":
215 options = []demangle.Option{demangle.NoClones}
216 case "none":
217 return
218 }
219
220
221 o := make([]demangle.Option, len(options))
222 for _, fn := range prof.Function {
223 if fn.Name != "" && fn.SystemName != fn.Name {
224 continue
225 }
226 copy(o, options)
227 if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
228 fn.Name = demangled
229 continue
230 }
231
232
233 name := fn.SystemName
234 if looksLikeDemangledCPlusPlus(name) {
235 if demanglerMode == "" || demanglerMode == "templates" {
236 name = removeMatching(name, '(', ')')
237 }
238 if demanglerMode == "" {
239 name = removeMatching(name, '<', '>')
240 }
241 }
242 fn.Name = name
243 }
244 }
245
246
247
248
249 func looksLikeDemangledCPlusPlus(demangled string) bool {
250 if strings.Contains(demangled, ".<") {
251 return false
252 }
253 return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
254 }
255
256
257 func removeMatching(name string, start, end byte) string {
258 s := string(start) + string(end)
259 var nesting, first, current int
260 for index := strings.IndexAny(name[current:], s); index != -1; index = strings.IndexAny(name[current:], s) {
261 switch current += index; name[current] {
262 case start:
263 nesting++
264 if nesting == 1 {
265 first = current
266 }
267 case end:
268 nesting--
269 switch {
270 case nesting < 0:
271 return name
272 case nesting == 0:
273 name = name[:first] + name[current+1:]
274 current = first - 1
275 }
276 }
277 current++
278 }
279 return name
280 }
281
282
283 func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
284 mt := &mappingTable{
285 prof: prof,
286 segments: make(map[*profile.Mapping]plugin.ObjFile),
287 }
288
289
290 mappings := make(map[*profile.Mapping]bool)
291 for _, l := range prof.Location {
292 mappings[l.Mapping] = true
293 }
294
295 missingBinaries := false
296 for midx, m := range prof.Mapping {
297 if !mappings[m] {
298 continue
299 }
300
301
302 if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
303 continue
304 }
305
306 if m.File == "" {
307 if midx == 0 {
308 ui.PrintErr("Main binary filename not available.")
309 continue
310 }
311 missingBinaries = true
312 continue
313 }
314
315
316 if m.Unsymbolizable() {
317 continue
318 }
319
320
321 if m.BuildID == "" {
322 if u, err := url.Parse(m.File); err == nil && u.IsAbs() && strings.Contains(strings.ToLower(u.Scheme), "http") {
323 continue
324 }
325 }
326
327 name := filepath.Base(m.File)
328 f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
329 if err != nil {
330 ui.PrintErr("Local symbolization failed for ", name, ": ", err)
331 missingBinaries = true
332 continue
333 }
334 if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
335 ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch")
336 f.Close()
337 continue
338 }
339
340 mt.segments[m] = f
341 }
342 if missingBinaries {
343 ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.\n" +
344 "Try setting PPROF_BINARY_PATH to the search path for local binaries.")
345 }
346 return mt, nil
347 }
348
349
350
351 type mappingTable struct {
352 prof *profile.Profile
353 segments map[*profile.Mapping]plugin.ObjFile
354 }
355
356
357 func (mt *mappingTable) close() {
358 for _, segment := range mt.segments {
359 segment.Close()
360 }
361 }
362
View as plain text