Home | History | Annotate | Download | only in pprof
      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/gosym"
      9 	"flag"
     10 	"fmt"
     11 	"os"
     12 	"regexp"
     13 	"strings"
     14 	"sync"
     15 
     16 	"cmd/internal/objfile"
     17 	"cmd/pprof/internal/commands"
     18 	"cmd/pprof/internal/driver"
     19 	"cmd/pprof/internal/fetch"
     20 	"cmd/pprof/internal/plugin"
     21 	"cmd/pprof/internal/profile"
     22 	"cmd/pprof/internal/symbolizer"
     23 	"cmd/pprof/internal/symbolz"
     24 )
     25 
     26 func main() {
     27 	var extraCommands map[string]*commands.Command // no added Go-specific commands
     28 	if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil {
     29 		fmt.Fprintf(os.Stderr, "%v\n", err)
     30 	}
     31 }
     32 
     33 // symbolize attempts to symbolize profile p.
     34 // If the source is a local binary, it tries using symbolizer and obj.
     35 // If the source is a URL, it fetches symbol information using symbolz.
     36 func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
     37 	remote, local := true, true
     38 	for _, o := range strings.Split(strings.ToLower(mode), ":") {
     39 		switch o {
     40 		case "none", "no":
     41 			return nil
     42 		case "local":
     43 			remote, local = false, true
     44 		case "remote":
     45 			remote, local = true, false
     46 		default:
     47 			ui.PrintErr("ignoring unrecognized symbolization option: " + mode)
     48 			ui.PrintErr("expecting -symbolize=[local|remote|none][:force]")
     49 			fallthrough
     50 		case "", "force":
     51 			// Ignore these options, -force is recognized by symbolizer.Symbolize
     52 		}
     53 	}
     54 
     55 	var err error
     56 	if local {
     57 		// Symbolize using binutils.
     58 		if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil {
     59 			return nil
     60 		}
     61 	}
     62 	if remote {
     63 		err = symbolz.Symbolize(source, fetch.PostURL, p)
     64 	}
     65 	return err
     66 }
     67 
     68 // flags implements the driver.FlagPackage interface using the builtin flag package.
     69 type flags struct {
     70 }
     71 
     72 func (flags) Bool(o string, d bool, c string) *bool {
     73 	return flag.Bool(o, d, c)
     74 }
     75 
     76 func (flags) Int(o string, d int, c string) *int {
     77 	return flag.Int(o, d, c)
     78 }
     79 
     80 func (flags) Float64(o string, d float64, c string) *float64 {
     81 	return flag.Float64(o, d, c)
     82 }
     83 
     84 func (flags) String(o, d, c string) *string {
     85 	return flag.String(o, d, c)
     86 }
     87 
     88 func (flags) Parse(usage func()) []string {
     89 	flag.Usage = usage
     90 	flag.Parse()
     91 	args := flag.Args()
     92 	if len(args) == 0 {
     93 		usage()
     94 	}
     95 	return args
     96 }
     97 
     98 func (flags) ExtraUsage() string {
     99 	return ""
    100 }
    101 
    102 // objTool implements plugin.ObjTool using Go libraries
    103 // (instead of invoking GNU binutils).
    104 type objTool struct {
    105 	mu          sync.Mutex
    106 	disasmCache map[string]*objfile.Disasm
    107 }
    108 
    109 func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) {
    110 	of, err := objfile.Open(name)
    111 	if err != nil {
    112 		return nil, err
    113 	}
    114 	f := &file{
    115 		name: name,
    116 		file: of,
    117 	}
    118 	return f, nil
    119 }
    120 
    121 func (*objTool) Demangle(names []string) (map[string]string, error) {
    122 	// No C++, nothing to demangle.
    123 	return make(map[string]string), nil
    124 }
    125 
    126 func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
    127 	d, err := t.cachedDisasm(file)
    128 	if err != nil {
    129 		return nil, err
    130 	}
    131 	var asm []plugin.Inst
    132 	d.Decode(start, end, func(pc, size uint64, file string, line int, text string) {
    133 		asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
    134 	})
    135 	return asm, nil
    136 }
    137 
    138 func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) {
    139 	t.mu.Lock()
    140 	defer t.mu.Unlock()
    141 	if t.disasmCache == nil {
    142 		t.disasmCache = make(map[string]*objfile.Disasm)
    143 	}
    144 	d := t.disasmCache[file]
    145 	if d != nil {
    146 		return d, nil
    147 	}
    148 	f, err := objfile.Open(file)
    149 	if err != nil {
    150 		return nil, err
    151 	}
    152 	d, err = f.Disasm()
    153 	f.Close()
    154 	if err != nil {
    155 		return nil, err
    156 	}
    157 	t.disasmCache[file] = d
    158 	return d, nil
    159 }
    160 
    161 func (*objTool) SetConfig(config string) {
    162 	// config is usually used to say what binaries to invoke.
    163 	// Ignore entirely.
    164 }
    165 
    166 // file implements plugin.ObjFile using Go libraries
    167 // (instead of invoking GNU binutils).
    168 // A file represents a single executable being analyzed.
    169 type file struct {
    170 	name string
    171 	sym  []objfile.Sym
    172 	file *objfile.File
    173 	pcln *gosym.Table
    174 }
    175 
    176 func (f *file) Name() string {
    177 	return f.name
    178 }
    179 
    180 func (f *file) Base() uint64 {
    181 	// No support for shared libraries.
    182 	return 0
    183 }
    184 
    185 func (f *file) BuildID() string {
    186 	// No support for build ID.
    187 	return ""
    188 }
    189 
    190 func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
    191 	if f.pcln == nil {
    192 		pcln, err := f.file.PCLineTable()
    193 		if err != nil {
    194 			return nil, err
    195 		}
    196 		f.pcln = pcln
    197 	}
    198 	file, line, fn := f.pcln.PCToLine(addr)
    199 	if fn == nil {
    200 		return nil, fmt.Errorf("no line information for PC=%#x", addr)
    201 	}
    202 	frame := []plugin.Frame{
    203 		{
    204 			Func: fn.Name,
    205 			File: file,
    206 			Line: line,
    207 		},
    208 	}
    209 	return frame, nil
    210 }
    211 
    212 func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
    213 	if f.sym == nil {
    214 		sym, err := f.file.Symbols()
    215 		if err != nil {
    216 			return nil, err
    217 		}
    218 		f.sym = sym
    219 	}
    220 	var out []*plugin.Sym
    221 	for _, s := range f.sym {
    222 		if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
    223 			out = append(out, &plugin.Sym{
    224 				Name:  []string{s.Name},
    225 				File:  f.name,
    226 				Start: s.Addr,
    227 				End:   s.Addr + uint64(s.Size) - 1,
    228 			})
    229 		}
    230 	}
    231 	return out, nil
    232 }
    233 
    234 func (f *file) Close() error {
    235 	f.file.Close()
    236 	return nil
    237 }
    238