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  }