github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/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  	}
    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  }