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