github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/pprof/pprof.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "debug/dwarf" 9 "debug/gosym" 10 "flag" 11 "fmt" 12 "os" 13 "regexp" 14 "strings" 15 "sync" 16 17 "cmd/internal/objfile" 18 "cmd/pprof/internal/commands" 19 "cmd/pprof/internal/driver" 20 "cmd/pprof/internal/fetch" 21 "cmd/pprof/internal/plugin" 22 "cmd/pprof/internal/profile" 23 "cmd/pprof/internal/symbolizer" 24 "cmd/pprof/internal/symbolz" 25 ) 26 27 func main() { 28 var extraCommands map[string]*commands.Command // no added Go-specific commands 29 if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil { 30 fmt.Fprintf(os.Stderr, "%v\n", err) 31 os.Exit(2) 32 } 33 } 34 35 // symbolize attempts to symbolize profile p. 36 // If the source is a local binary, it tries using symbolizer and obj. 37 // If the source is a URL, it fetches symbol information using symbolz. 38 func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { 39 remote, local := true, true 40 for _, o := range strings.Split(strings.ToLower(mode), ":") { 41 switch o { 42 case "none", "no": 43 return nil 44 case "local": 45 remote, local = false, true 46 case "remote": 47 remote, local = true, false 48 default: 49 ui.PrintErr("ignoring unrecognized symbolization option: " + mode) 50 ui.PrintErr("expecting -symbolize=[local|remote|none][:force]") 51 fallthrough 52 case "", "force": 53 // Ignore these options, -force is recognized by symbolizer.Symbolize 54 } 55 } 56 57 var err error 58 if local { 59 // Symbolize using binutils. 60 if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil { 61 return nil 62 } 63 } 64 if remote { 65 err = symbolz.Symbolize(source, fetch.PostURL, p) 66 } 67 return err 68 } 69 70 // flags implements the driver.FlagPackage interface using the builtin flag package. 71 type flags struct { 72 } 73 74 func (flags) Bool(o string, d bool, c string) *bool { 75 return flag.Bool(o, d, c) 76 } 77 78 func (flags) Int(o string, d int, c string) *int { 79 return flag.Int(o, d, c) 80 } 81 82 func (flags) Float64(o string, d float64, c string) *float64 { 83 return flag.Float64(o, d, c) 84 } 85 86 func (flags) String(o, d, c string) *string { 87 return flag.String(o, d, c) 88 } 89 90 func (flags) Parse(usage func()) []string { 91 flag.Usage = usage 92 flag.Parse() 93 args := flag.Args() 94 if len(args) == 0 { 95 usage() 96 } 97 return args 98 } 99 100 func (flags) ExtraUsage() string { 101 return "" 102 } 103 104 // objTool implements plugin.ObjTool using Go libraries 105 // (instead of invoking GNU binutils). 106 type objTool struct { 107 mu sync.Mutex 108 disasmCache map[string]*objfile.Disasm 109 } 110 111 func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) { 112 of, err := objfile.Open(name) 113 if err != nil { 114 return nil, err 115 } 116 f := &file{ 117 name: name, 118 file: of, 119 } 120 return f, nil 121 } 122 123 func (*objTool) Demangle(names []string) (map[string]string, error) { 124 // No C++, nothing to demangle. 125 return make(map[string]string), nil 126 } 127 128 func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { 129 d, err := t.cachedDisasm(file) 130 if err != nil { 131 return nil, err 132 } 133 var asm []plugin.Inst 134 d.Decode(start, end, func(pc, size uint64, file string, line int, text string) { 135 asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text}) 136 }) 137 return asm, nil 138 } 139 140 func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { 141 t.mu.Lock() 142 defer t.mu.Unlock() 143 if t.disasmCache == nil { 144 t.disasmCache = make(map[string]*objfile.Disasm) 145 } 146 d := t.disasmCache[file] 147 if d != nil { 148 return d, nil 149 } 150 f, err := objfile.Open(file) 151 if err != nil { 152 return nil, err 153 } 154 d, err = f.Disasm() 155 f.Close() 156 if err != nil { 157 return nil, err 158 } 159 t.disasmCache[file] = d 160 return d, nil 161 } 162 163 func (*objTool) SetConfig(config string) { 164 // config is usually used to say what binaries to invoke. 165 // Ignore entirely. 166 } 167 168 // file implements plugin.ObjFile using Go libraries 169 // (instead of invoking GNU binutils). 170 // A file represents a single executable being analyzed. 171 type file struct { 172 name string 173 sym []objfile.Sym 174 file *objfile.File 175 pcln *gosym.Table 176 177 triedDwarf bool 178 dwarf *dwarf.Data 179 } 180 181 func (f *file) Name() string { 182 return f.name 183 } 184 185 func (f *file) Base() uint64 { 186 // No support for shared libraries. 187 return 0 188 } 189 190 func (f *file) BuildID() string { 191 // No support for build ID. 192 return "" 193 } 194 195 func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { 196 if f.pcln == nil { 197 pcln, err := f.file.PCLineTable() 198 if err != nil { 199 return nil, err 200 } 201 f.pcln = pcln 202 } 203 file, line, fn := f.pcln.PCToLine(addr) 204 if fn != nil { 205 frame := []plugin.Frame{ 206 { 207 Func: fn.Name, 208 File: file, 209 Line: line, 210 }, 211 } 212 return frame, nil 213 } 214 215 frames := f.dwarfSourceLine(addr) 216 if frames != nil { 217 return frames, nil 218 } 219 220 return nil, fmt.Errorf("no line information for PC=%#x", addr) 221 } 222 223 // dwarfSourceLine tries to get file/line information using DWARF. 224 // This is for C functions that appear in the profile. 225 // Returns nil if there is no information available. 226 func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame { 227 if f.dwarf == nil && !f.triedDwarf { 228 // Ignore any error--we don't care exactly why there 229 // is no DWARF info. 230 f.dwarf, _ = f.file.DWARF() 231 f.triedDwarf = true 232 } 233 234 if f.dwarf != nil { 235 r := f.dwarf.Reader() 236 unit, err := r.SeekPC(addr) 237 if err == nil { 238 if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil { 239 return frames 240 } 241 } 242 } 243 244 return nil 245 } 246 247 // dwarfSourceLineEntry tries to get file/line information from a 248 // DWARF compilation unit. Returns nil if it doesn't find anything. 249 func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame { 250 lines, err := f.dwarf.LineReader(entry) 251 if err != nil { 252 return nil 253 } 254 var lentry dwarf.LineEntry 255 if err := lines.SeekPC(addr, &lentry); err != nil { 256 return nil 257 } 258 259 // Try to find the function name. 260 name := "" 261 FindName: 262 for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { 263 if entry.Tag == dwarf.TagSubprogram { 264 ranges, err := f.dwarf.Ranges(entry) 265 if err != nil { 266 return nil 267 } 268 for _, pcs := range ranges { 269 if pcs[0] <= addr && addr < pcs[1] { 270 var ok bool 271 // TODO: AT_linkage_name, AT_MIPS_linkage_name. 272 name, ok = entry.Val(dwarf.AttrName).(string) 273 if ok { 274 break FindName 275 } 276 } 277 } 278 } 279 } 280 281 // TODO: Report inlined functions. 282 283 frames := []plugin.Frame{ 284 { 285 Func: name, 286 File: lentry.File.Name, 287 Line: lentry.Line, 288 }, 289 } 290 291 return frames 292 } 293 294 func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { 295 if f.sym == nil { 296 sym, err := f.file.Symbols() 297 if err != nil { 298 return nil, err 299 } 300 f.sym = sym 301 } 302 var out []*plugin.Sym 303 for _, s := range f.sym { 304 if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) { 305 out = append(out, &plugin.Sym{ 306 Name: []string{s.Name}, 307 File: f.name, 308 Start: s.Addr, 309 End: s.Addr + uint64(s.Size) - 1, 310 }) 311 } 312 } 313 return out, nil 314 } 315 316 func (f *file) Close() error { 317 f.file.Close() 318 return nil 319 }