| // Copyright 2014 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "debug/gosym" | |
| "flag" | |
| "fmt" | |
| "os" | |
| "regexp" | |
| "strings" | |
| "sync" | |
| "cmd/internal/objfile" | |
| "cmd/pprof/internal/commands" | |
| "cmd/pprof/internal/driver" | |
| "cmd/pprof/internal/fetch" | |
| "cmd/pprof/internal/plugin" | |
| "cmd/pprof/internal/profile" | |
| "cmd/pprof/internal/symbolizer" | |
| "cmd/pprof/internal/symbolz" | |
| ) | |
| func main() { | |
| var extraCommands map[string]*commands.Command // no added Go-specific commands | |
| if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil { | |
| fmt.Fprintf(os.Stderr, "%v\n", err) | |
| os.Exit(2) | |
| } | |
| } | |
| // symbolize attempts to symbolize profile p. | |
| // If the source is a local binary, it tries using symbolizer and obj. | |
| // If the source is a URL, it fetches symbol information using symbolz. | |
| func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { | |
| remote, local := true, true | |
| for _, o := range strings.Split(strings.ToLower(mode), ":") { | |
| switch o { | |
| case "none", "no": | |
| return nil | |
| case "local": | |
| remote, local = false, true | |
| case "remote": | |
| remote, local = true, false | |
| default: | |
| ui.PrintErr("ignoring unrecognized symbolization option: " + mode) | |
| ui.PrintErr("expecting -symbolize=[local|remote|none][:force]") | |
| fallthrough | |
| case "", "force": | |
| // Ignore these options, -force is recognized by symbolizer.Symbolize | |
| } | |
| } | |
| var err error | |
| if local { | |
| // Symbolize using binutils. | |
| if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil { | |
| return nil | |
| } | |
| } | |
| if remote { | |
| err = symbolz.Symbolize(source, fetch.PostURL, p) | |
| } | |
| return err | |
| } | |
| // flags implements the driver.FlagPackage interface using the builtin flag package. | |
| type flags struct { | |
| } | |
| func (flags) Bool(o string, d bool, c string) *bool { | |
| return flag.Bool(o, d, c) | |
| } | |
| func (flags) Int(o string, d int, c string) *int { | |
| return flag.Int(o, d, c) | |
| } | |
| func (flags) Float64(o string, d float64, c string) *float64 { | |
| return flag.Float64(o, d, c) | |
| } | |
| func (flags) String(o, d, c string) *string { | |
| return flag.String(o, d, c) | |
| } | |
| func (flags) Parse(usage func()) []string { | |
| flag.Usage = usage | |
| flag.Parse() | |
| args := flag.Args() | |
| if len(args) == 0 { | |
| usage() | |
| } | |
| return args | |
| } | |
| func (flags) ExtraUsage() string { | |
| return "" | |
| } | |
| // objTool implements plugin.ObjTool using Go libraries | |
| // (instead of invoking GNU binutils). | |
| type objTool struct { | |
| mu sync.Mutex | |
| disasmCache map[string]*objfile.Disasm | |
| } | |
| func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) { | |
| of, err := objfile.Open(name) | |
| if err != nil { | |
| return nil, err | |
| } | |
| f := &file{ | |
| name: name, | |
| file: of, | |
| } | |
| return f, nil | |
| } | |
| func (*objTool) Demangle(names []string) (map[string]string, error) { | |
| // No C++, nothing to demangle. | |
| return make(map[string]string), nil | |
| } | |
| func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { | |
| d, err := t.cachedDisasm(file) | |
| if err != nil { | |
| return nil, err | |
| } | |
| var asm []plugin.Inst | |
| d.Decode(start, end, func(pc, size uint64, file string, line int, text string) { | |
| asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text}) | |
| }) | |
| return asm, nil | |
| } | |
| func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { | |
| t.mu.Lock() | |
| defer t.mu.Unlock() | |
| if t.disasmCache == nil { | |
| t.disasmCache = make(map[string]*objfile.Disasm) | |
| } | |
| d := t.disasmCache[file] | |
| if d != nil { | |
| return d, nil | |
| } | |
| f, err := objfile.Open(file) | |
| if err != nil { | |
| return nil, err | |
| } | |
| d, err = f.Disasm() | |
| f.Close() | |
| if err != nil { | |
| return nil, err | |
| } | |
| t.disasmCache[file] = d | |
| return d, nil | |
| } | |
| func (*objTool) SetConfig(config string) { | |
| // config is usually used to say what binaries to invoke. | |
| // Ignore entirely. | |
| } | |
| // file implements plugin.ObjFile using Go libraries | |
| // (instead of invoking GNU binutils). | |
| // A file represents a single executable being analyzed. | |
| type file struct { | |
| name string | |
| sym []objfile.Sym | |
| file *objfile.File | |
| pcln *gosym.Table | |
| } | |
| func (f *file) Name() string { | |
| return f.name | |
| } | |
| func (f *file) Base() uint64 { | |
| // No support for shared libraries. | |
| return 0 | |
| } | |
| func (f *file) BuildID() string { | |
| // No support for build ID. | |
| return "" | |
| } | |
| func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) { | |
| if f.pcln == nil { | |
| pcln, err := f.file.PCLineTable() | |
| if err != nil { | |
| return nil, err | |
| } | |
| f.pcln = pcln | |
| } | |
| file, line, fn := f.pcln.PCToLine(addr) | |
| if fn == nil { | |
| return nil, fmt.Errorf("no line information for PC=%#x", addr) | |
| } | |
| frame := []plugin.Frame{ | |
| { | |
| Func: fn.Name, | |
| File: file, | |
| Line: line, | |
| }, | |
| } | |
| return frame, nil | |
| } | |
| func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { | |
| if f.sym == nil { | |
| sym, err := f.file.Symbols() | |
| if err != nil { | |
| return nil, err | |
| } | |
| f.sym = sym | |
| } | |
| var out []*plugin.Sym | |
| for _, s := range f.sym { | |
| if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) { | |
| out = append(out, &plugin.Sym{ | |
| Name: []string{s.Name}, | |
| File: f.name, | |
| Start: s.Addr, | |
| End: s.Addr + uint64(s.Size) - 1, | |
| }) | |
| } | |
| } | |
| return out, nil | |
| } | |
| func (f *file) Close() error { | |
| f.file.Close() | |
| return nil | |
| } |