golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package symbolizer provides a routine to populate a profile with 16 // symbol, file and line number information. It relies on the 17 // addr2liner and demangle packages to do the actual work. 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 // Symbolizer implements the plugin.Symbolize interface. 36 type Symbolizer struct { 37 Obj plugin.ObjTool 38 UI plugin.UI 39 } 40 41 // test taps for dependency injection 42 var symbolzSymbolize = symbolz.Symbolize 43 var localSymbolize = doLocalSymbolize 44 45 // Symbolize attempts to symbolize profile p. First uses binutils on 46 // local binaries; if the source is a URL it attempts to get any 47 // missed entries using symbolz. 48 func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *profile.Profile) error { 49 remote, local, force, demanglerMode := true, true, false, "" 50 for _, o := range strings.Split(strings.ToLower(mode), ":") { 51 switch o { 52 case "none", "no": 53 return nil 54 case "local", "fastlocal": 55 remote, local = false, true 56 case "remote": 57 remote, local = true, false 58 case "", "force": 59 force = true 60 default: 61 switch d := strings.TrimPrefix(o, "demangle="); d { 62 case "full", "none", "templates": 63 demanglerMode = d 64 force = true 65 continue 66 case "default": 67 continue 68 } 69 s.UI.PrintErr("ignoring unrecognized symbolization option: " + mode) 70 s.UI.PrintErr("expecting -symbolize=[local|fastlocal|remote|none][:force][:demangle=[none|full|templates|default]") 71 } 72 } 73 74 var err error 75 if local { 76 // Symbolize locally using binutils. 77 if err = localSymbolize(mode, p, s.Obj, s.UI); err != nil { 78 s.UI.PrintErr("local symbolization: " + err.Error()) 79 } 80 } 81 if remote { 82 if err = symbolzSymbolize(sources, postURL, p, s.UI); err != nil { 83 return err // Ran out of options. 84 } 85 } 86 87 Demangle(p, force, demanglerMode) 88 return nil 89 } 90 91 // postURL issues a POST to a URL over HTTP. 92 func postURL(source, post string) ([]byte, error) { 93 resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post)) 94 if err != nil { 95 return nil, fmt.Errorf("http post %s: %v", source, err) 96 } 97 defer resp.Body.Close() 98 if resp.StatusCode != http.StatusOK { 99 return nil, statusCodeError(resp) 100 } 101 return ioutil.ReadAll(resp.Body) 102 } 103 104 func statusCodeError(resp *http.Response) error { 105 if resp.Header.Get("X-Go-Pprof") != "" && strings.Contains(resp.Header.Get("Content-Type"), "text/plain") { 106 // error is from pprof endpoint 107 if body, err := ioutil.ReadAll(resp.Body); err == nil { 108 return fmt.Errorf("server response: %s - %s", resp.Status, body) 109 } 110 } 111 return fmt.Errorf("server response: %s", resp.Status) 112 } 113 114 // doLocalSymbolize adds symbol and line number information to all locations 115 // in a profile. mode enables some options to control 116 // symbolization. 117 func doLocalSymbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { 118 force := false 119 // Disable some mechanisms based on mode string. 120 for _, o := range strings.Split(strings.ToLower(mode), ":") { 121 switch { 122 case o == "force": 123 force = true 124 case o == "fastlocal": 125 if bu, ok := obj.(*binutils.Binutils); ok { 126 bu.SetFastSymbolization(true) 127 } 128 default: 129 } 130 } 131 132 mt, err := newMapping(prof, obj, ui, force) 133 if err != nil { 134 return err 135 } 136 defer mt.close() 137 138 functions := make(map[profile.Function]*profile.Function) 139 for _, l := range mt.prof.Location { 140 m := l.Mapping 141 segment := mt.segments[m] 142 if segment == nil { 143 // Nothing to do. 144 continue 145 } 146 147 stack, err := segment.SourceLine(l.Address) 148 if err != nil || len(stack) == 0 { 149 // No answers from addr2line. 150 continue 151 } 152 153 l.Line = make([]profile.Line, len(stack)) 154 for i, frame := range stack { 155 if frame.Func != "" { 156 m.HasFunctions = true 157 } 158 if frame.File != "" { 159 m.HasFilenames = true 160 } 161 if frame.Line != 0 { 162 m.HasLineNumbers = true 163 } 164 f := &profile.Function{ 165 Name: frame.Func, 166 SystemName: frame.Func, 167 Filename: frame.File, 168 } 169 if fp := functions[*f]; fp != nil { 170 f = fp 171 } else { 172 functions[*f] = f 173 f.ID = uint64(len(mt.prof.Function)) + 1 174 mt.prof.Function = append(mt.prof.Function, f) 175 } 176 l.Line[i] = profile.Line{ 177 Function: f, 178 Line: int64(frame.Line), 179 } 180 } 181 182 if len(stack) > 0 { 183 m.HasInlineFrames = true 184 } 185 } 186 187 return nil 188 } 189 190 // Demangle updates the function names in a profile with demangled C++ 191 // names, simplified according to demanglerMode. If force is set, 192 // overwrite any names that appear already demangled. 193 func Demangle(prof *profile.Profile, force bool, demanglerMode string) { 194 if force { 195 // Remove the current demangled names to force demangling 196 for _, f := range prof.Function { 197 if f.Name != "" && f.SystemName != "" { 198 f.Name = f.SystemName 199 } 200 } 201 } 202 203 var options []demangle.Option 204 switch demanglerMode { 205 case "": // demangled, simplified: no parameters, no templates, no return type 206 options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams} 207 case "templates": // demangled, simplified: no parameters, no return type 208 options = []demangle.Option{demangle.NoParams} 209 case "full": 210 options = []demangle.Option{demangle.NoClones} 211 case "none": // no demangling 212 return 213 } 214 215 // Copy the options because they may be updated by the call. 216 o := make([]demangle.Option, len(options)) 217 for _, fn := range prof.Function { 218 if fn.Name != "" && fn.SystemName != fn.Name { 219 continue // Already demangled. 220 } 221 copy(o, options) 222 if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName { 223 fn.Name = demangled 224 continue 225 } 226 // Could not demangle. Apply heuristics in case the name is 227 // already demangled. 228 name := fn.SystemName 229 if looksLikeDemangledCPlusPlus(name) { 230 if demanglerMode == "" || demanglerMode == "templates" { 231 name = removeMatching(name, '(', ')') 232 } 233 if demanglerMode == "" { 234 name = removeMatching(name, '<', '>') 235 } 236 } 237 fn.Name = name 238 } 239 } 240 241 // looksLikeDemangledCPlusPlus is a heuristic to decide if a name is 242 // the result of demangling C++. If so, further heuristics will be 243 // applied to simplify the name. 244 func looksLikeDemangledCPlusPlus(demangled string) bool { 245 if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>" 246 return false 247 } 248 return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::") 249 } 250 251 // removeMatching removes nested instances of start..end from name. 252 func removeMatching(name string, start, end byte) string { 253 s := string(start) + string(end) 254 var nesting, first, current int 255 for index := strings.IndexAny(name[current:], s); index != -1; index = strings.IndexAny(name[current:], s) { 256 switch current += index; name[current] { 257 case start: 258 nesting++ 259 if nesting == 1 { 260 first = current 261 } 262 case end: 263 nesting-- 264 switch { 265 case nesting < 0: 266 return name // Mismatch, abort 267 case nesting == 0: 268 name = name[:first] + name[current+1:] 269 current = first - 1 270 } 271 } 272 current++ 273 } 274 return name 275 } 276 277 // newMapping creates a mappingTable for a profile. 278 func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { 279 mt := &mappingTable{ 280 prof: prof, 281 segments: make(map[*profile.Mapping]plugin.ObjFile), 282 } 283 284 // Identify used mappings 285 mappings := make(map[*profile.Mapping]bool) 286 for _, l := range prof.Location { 287 mappings[l.Mapping] = true 288 } 289 290 missingBinaries := false 291 for midx, m := range prof.Mapping { 292 if !mappings[m] { 293 continue 294 } 295 296 // Do not attempt to re-symbolize a mapping that has already been symbolized. 297 if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { 298 continue 299 } 300 301 if m.File == "" { 302 if midx == 0 { 303 ui.PrintErr("Main binary filename not available.") 304 continue 305 } 306 missingBinaries = true 307 continue 308 } 309 310 // Skip well-known system mappings 311 if m.Unsymbolizable() { 312 continue 313 } 314 315 // Skip mappings pointing to a source URL 316 if m.BuildID == "" { 317 if u, err := url.Parse(m.File); err == nil && u.IsAbs() && strings.Contains(strings.ToLower(u.Scheme), "http") { 318 continue 319 } 320 } 321 322 name := filepath.Base(m.File) 323 f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset) 324 if err != nil { 325 ui.PrintErr("Local symbolization failed for ", name, ": ", err) 326 missingBinaries = true 327 continue 328 } 329 if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { 330 ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch") 331 f.Close() 332 continue 333 } 334 335 mt.segments[m] = f 336 } 337 if missingBinaries { 338 ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.\n" + 339 "Try setting PPROF_BINARY_PATH to the search path for local binaries.") 340 } 341 return mt, nil 342 } 343 344 // mappingTable contains the mechanisms for symbolization of a 345 // profile. 346 type mappingTable struct { 347 prof *profile.Profile 348 segments map[*profile.Mapping]plugin.ObjFile 349 } 350 351 // Close releases any external processes being used for the mapping. 352 func (mt *mappingTable) close() { 353 for _, segment := range mt.segments { 354 segment.Close() 355 } 356 }