github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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/internal/pprof/commands" 19 "cmd/internal/pprof/driver" 20 "cmd/internal/pprof/fetch" 21 "cmd/internal/pprof/plugin" 22 "cmd/internal/pprof/profile" 23 "cmd/internal/pprof/symbolizer" 24 "cmd/internal/pprof/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 if load, err := of.LoadAddress(); err == nil { 121 f.offset = start - load 122 } 123 return f, nil 124 } 125 126 func (*objTool) Demangle(names []string) (map[string]string, error) { 127 // No C++, nothing to demangle. 128 return make(map[string]string), nil 129 } 130 131 func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { 132 d, err := t.cachedDisasm(file) 133 if err != nil { 134 return nil, err 135 } 136 var asm []plugin.Inst 137 d.Decode(start, end, func(pc, size uint64, file string, line int, text string) { 138 asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text}) 139 }) 140 return asm, nil 141 } 142 143 func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { 144 t.mu.Lock() 145 defer t.mu.Unlock() 146 if t.disasmCache == nil { 147 t.disasmCache = make(map[string]*objfile.Disasm) 148 } 149 d := t.disasmCache[file] 150 if d != nil { 151 return d, nil 152 } 153 f, err := objfile.Open(file) 154 if err != nil { 155 return nil, err 156 } 157 d, err = f.Disasm() 158 f.Close() 159 if err != nil { 160 return nil, err 161 } 162 t.disasmCache[file] = d 163 return d, nil 164 } 165 166 func (*objTool) SetConfig(config string) { 167 // config is usually used to say what binaries to invoke. 168 // Ignore entirely. 169 } 170 171 // file implements plugin.ObjFile using Go libraries 172 // (instead of invoking GNU binutils). 173 // A file represents a single executable being analyzed. 174 type file struct { 175 name string 176 offset uint64 177 sym []objfile.Sym 178 file *objfile.File 179 pcln *gosym.Table 180 181 triedDwarf bool 182 dwarf *dwarf.Data 183 } 184 185 func (f *file) Name() string { 186 return f.name 187 } 188 189 func (f *file) Base() uint64 { 190 // No support for shared libraries. 191 return 0 192 } 193 194 func (f *file) BuildID() string { 195 // No support for build ID. 196 return "" 197 } 198 199 func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { 200 if f.pcln == nil { 201 pcln, err := f.file.PCLineTable() 202 if err != nil { 203 return nil, err 204 } 205 f.pcln = pcln 206 } 207 addr -= f.offset 208 file, line, fn := f.pcln.PCToLine(addr) 209 if fn != nil { 210 frame := []plugin.Frame{ 211 { 212 Func: fn.Name, 213 File: file, 214 Line: line, 215 }, 216 } 217 return frame, nil 218 } 219 220 frames := f.dwarfSourceLine(addr) 221 if frames != nil { 222 return frames, nil 223 } 224 225 return nil, fmt.Errorf("no line information for PC=%#x", addr) 226 } 227 228 // dwarfSourceLine tries to get file/line information using DWARF. 229 // This is for C functions that appear in the profile. 230 // Returns nil if there is no information available. 231 func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame { 232 if f.dwarf == nil && !f.triedDwarf { 233 // Ignore any error--we don't care exactly why there 234 // is no DWARF info. 235 f.dwarf, _ = f.file.DWARF() 236 f.triedDwarf = true 237 } 238 239 if f.dwarf != nil { 240 r := f.dwarf.Reader() 241 unit, err := r.SeekPC(addr) 242 if err == nil { 243 if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil { 244 return frames 245 } 246 } 247 } 248 249 return nil 250 } 251 252 // dwarfSourceLineEntry tries to get file/line information from a 253 // DWARF compilation unit. Returns nil if it doesn't find anything. 254 func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame { 255 lines, err := f.dwarf.LineReader(entry) 256 if err != nil { 257 return nil 258 } 259 var lentry dwarf.LineEntry 260 if err := lines.SeekPC(addr, &lentry); err != nil { 261 return nil 262 } 263 264 // Try to find the function name. 265 name := "" 266 FindName: 267 for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { 268 if entry.Tag == dwarf.TagSubprogram { 269 ranges, err := f.dwarf.Ranges(entry) 270 if err != nil { 271 return nil 272 } 273 for _, pcs := range ranges { 274 if pcs[0] <= addr && addr < pcs[1] { 275 var ok bool 276 // TODO: AT_linkage_name, AT_MIPS_linkage_name. 277 name, ok = entry.Val(dwarf.AttrName).(string) 278 if ok { 279 break FindName 280 } 281 } 282 } 283 } 284 } 285 286 // TODO: Report inlined functions. 287 288 frames := []plugin.Frame{ 289 { 290 Func: name, 291 File: lentry.File.Name, 292 Line: lentry.Line, 293 }, 294 } 295 296 return frames 297 } 298 299 func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { 300 if f.sym == nil { 301 sym, err := f.file.Symbols() 302 if err != nil { 303 return nil, err 304 } 305 f.sym = sym 306 } 307 var out []*plugin.Sym 308 for _, s := range f.sym { 309 if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) { 310 out = append(out, &plugin.Sym{ 311 Name: []string{s.Name}, 312 File: f.name, 313 Start: s.Addr, 314 End: s.Addr + uint64(s.Size) - 1, 315 }) 316 } 317 } 318 return out, nil 319 } 320 321 func (f *file) Close() error { 322 f.file.Close() 323 return nil 324 }