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