blob: 44f4f6cb722a13073049e6520344bba38b694c0d [file] [log] [blame]
Bill Yi7fb3c4c2015-03-23 09:04:07 -07001// 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
5package main
6
7import (
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
26func 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.
36func 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.
69type flags struct {
70}
71
72func (flags) Bool(o string, d bool, c string) *bool {
73 return flag.Bool(o, d, c)
74}
75
76func (flags) Int(o string, d int, c string) *int {
77 return flag.Int(o, d, c)
78}
79
80func (flags) Float64(o string, d float64, c string) *float64 {
81 return flag.Float64(o, d, c)
82}
83
84func (flags) String(o, d, c string) *string {
85 return flag.String(o, d, c)
86}
87
88func (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
98func (flags) ExtraUsage() string {
99 return ""
100}
101
102// objTool implements plugin.ObjTool using Go libraries
103// (instead of invoking GNU binutils).
104type objTool struct {
105 mu sync.Mutex
106 disasmCache map[string]*objfile.Disasm
107}
108
109func (*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
121func (*objTool) Demangle(names []string) (map[string]string, error) {
122 // No C++, nothing to demangle.
123 return make(map[string]string), nil
124}
125
126func (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
138func (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
161func (*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.
169type file struct {
170 name string
171 sym []objfile.Sym
172 file *objfile.File
173 pcln *gosym.Table
174}
175
176func (f *file) Name() string {
177 return f.name
178}
179
180func (f *file) Base() uint64 {
181 // No support for shared libraries.
182 return 0
183}
184
185func (f *file) BuildID() string {
186 // No support for build ID.
187 return ""
188}
189
190func (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
212func (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
234func (f *file) Close() error {
235 f.file.Close()
236 return nil
237}