Source file
src/cmd/pprof/pprof.go
1
2
3
4
5
6
7
8
9
10 package main
11
12 import (
13 "crypto/tls"
14 "debug/dwarf"
15 "flag"
16 "fmt"
17 "io"
18 "net/http"
19 "net/url"
20 "os"
21 "regexp"
22 "strconv"
23 "strings"
24 "sync"
25 "time"
26
27 "cmd/internal/disasm"
28 "cmd/internal/objfile"
29 "cmd/internal/telemetry/counter"
30
31 "github.com/google/pprof/driver"
32 "github.com/google/pprof/profile"
33 )
34
35 func main() {
36 counter.Open()
37 counter.Inc("pprof/invocations")
38 options := &driver.Options{
39 Fetch: new(fetcher),
40 Obj: new(objTool),
41 UI: newUI(),
42 }
43 err := driver.PProf(options)
44 counter.CountFlags("pprof/flag:", *flag.CommandLine)
45 if err != nil {
46 fmt.Fprintf(os.Stderr, "%v\n", err)
47 os.Exit(2)
48 }
49 }
50
51 type fetcher struct {
52 }
53
54 func (f *fetcher) Fetch(src string, duration, timeout time.Duration) (*profile.Profile, string, error) {
55
56
57
58
59
60
61
62 if _, openErr := os.Stat(src); openErr == nil {
63 return nil, "", nil
64 }
65 sourceURL, timeout := adjustURL(src, duration, timeout)
66 if sourceURL == "" {
67
68 return nil, "", nil
69 }
70 fmt.Fprintln(os.Stderr, "Fetching profile over HTTP from", sourceURL)
71 if duration > 0 {
72 fmt.Fprintf(os.Stderr, "Please wait... (%v)\n", duration)
73 }
74 p, err := getProfile(sourceURL, timeout)
75 return p, sourceURL, err
76 }
77
78 func getProfile(source string, timeout time.Duration) (*profile.Profile, error) {
79 url, err := url.Parse(source)
80 if err != nil {
81 return nil, err
82 }
83
84 var tlsConfig *tls.Config
85 if url.Scheme == "https+insecure" {
86 tlsConfig = &tls.Config{
87 InsecureSkipVerify: true,
88 }
89 url.Scheme = "https"
90 source = url.String()
91 }
92
93 client := &http.Client{
94 Transport: &http.Transport{
95 ResponseHeaderTimeout: timeout + 5*time.Second,
96 Proxy: http.ProxyFromEnvironment,
97 TLSClientConfig: tlsConfig,
98 },
99 }
100 resp, err := client.Get(source)
101 if err != nil {
102 return nil, err
103 }
104 defer resp.Body.Close()
105 if resp.StatusCode != http.StatusOK {
106 return nil, statusCodeError(resp)
107 }
108 return profile.Parse(resp.Body)
109 }
110
111 func statusCodeError(resp *http.Response) error {
112 if resp.Header.Get("X-Go-Pprof") != "" && strings.Contains(resp.Header.Get("Content-Type"), "text/plain") {
113
114 if body, err := io.ReadAll(resp.Body); err == nil {
115 return fmt.Errorf("server response: %s - %s", resp.Status, body)
116 }
117 }
118 return fmt.Errorf("server response: %s", resp.Status)
119 }
120
121
122 const cpuProfileHandler = "/debug/pprof/profile"
123
124
125 func adjustURL(source string, duration, timeout time.Duration) (string, time.Duration) {
126 u, err := url.Parse(source)
127 if err != nil || (u.Host == "" && u.Scheme != "" && u.Scheme != "file") {
128
129
130 u, err = url.Parse("http://" + source)
131 }
132 if err != nil || u.Host == "" {
133 return "", 0
134 }
135
136 if u.Path == "" || u.Path == "/" {
137 u.Path = cpuProfileHandler
138 }
139
140
141 values := u.Query()
142 if duration > 0 {
143 values.Set("seconds", fmt.Sprint(int(duration.Seconds())))
144 } else {
145 if urlSeconds := values.Get("seconds"); urlSeconds != "" {
146 if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil {
147 duration = time.Duration(us) * time.Second
148 }
149 }
150 }
151 if timeout <= 0 {
152 if duration > 0 {
153 timeout = duration + duration/2
154 } else {
155 timeout = 60 * time.Second
156 }
157 }
158 u.RawQuery = values.Encode()
159 return u.String(), timeout
160 }
161
162
163
164 type objTool struct {
165 mu sync.Mutex
166 disasmCache map[string]*disasm.Disasm
167 }
168
169 func (*objTool) Open(name string, start, limit, offset uint64, relocationSymbol string) (driver.ObjFile, error) {
170 of, err := objfile.Open(name)
171 if err != nil {
172 return nil, err
173 }
174 f := &file{
175 name: name,
176 file: of,
177 }
178 if start != 0 {
179 if load, err := of.LoadAddress(); err == nil {
180 f.offset = start - load
181 }
182 }
183 return f, nil
184 }
185
186 func (*objTool) Demangle(names []string) (map[string]string, error) {
187
188 return make(map[string]string), nil
189 }
190
191 func (t *objTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]driver.Inst, error) {
192 if intelSyntax {
193 return nil, fmt.Errorf("printing assembly in Intel syntax is not supported")
194 }
195 d, err := t.cachedDisasm(file)
196 if err != nil {
197 return nil, err
198 }
199 var asm []driver.Inst
200 d.Decode(start, end, nil, false, func(pc, size uint64, file string, line int, text string) {
201 asm = append(asm, driver.Inst{Addr: pc, File: file, Line: line, Text: text})
202 })
203 return asm, nil
204 }
205
206 func (t *objTool) cachedDisasm(file string) (*disasm.Disasm, error) {
207 t.mu.Lock()
208 defer t.mu.Unlock()
209 if t.disasmCache == nil {
210 t.disasmCache = make(map[string]*disasm.Disasm)
211 }
212 d := t.disasmCache[file]
213 if d != nil {
214 return d, nil
215 }
216 f, err := objfile.Open(file)
217 if err != nil {
218 return nil, err
219 }
220 d, err = disasm.DisasmForFile(f)
221 f.Close()
222 if err != nil {
223 return nil, err
224 }
225 t.disasmCache[file] = d
226 return d, nil
227 }
228
229 func (*objTool) SetConfig(config string) {
230
231
232 }
233
234
235
236
237 type file struct {
238 name string
239 offset uint64
240 sym []objfile.Sym
241 file *objfile.File
242 pcln objfile.Liner
243
244 triedDwarf bool
245 dwarf *dwarf.Data
246 }
247
248 func (f *file) Name() string {
249 return f.name
250 }
251
252 func (f *file) ObjAddr(addr uint64) (uint64, error) {
253 return addr - f.offset, nil
254 }
255
256 func (f *file) BuildID() string {
257
258 return ""
259 }
260
261 func (f *file) SourceLine(addr uint64) ([]driver.Frame, error) {
262 if f.pcln == nil {
263 pcln, err := f.file.PCLineTable()
264 if err != nil {
265 return nil, err
266 }
267 f.pcln = pcln
268 }
269 addr -= f.offset
270 file, line, fn := f.pcln.PCToLine(addr)
271 if fn != nil {
272 frame := []driver.Frame{
273 {
274 Func: fn.Name,
275 File: file,
276 Line: line,
277 },
278 }
279 return frame, nil
280 }
281
282 frames := f.dwarfSourceLine(addr)
283 if frames != nil {
284 return frames, nil
285 }
286
287 return nil, fmt.Errorf("no line information for PC=%#x", addr)
288 }
289
290
291
292
293 func (f *file) dwarfSourceLine(addr uint64) []driver.Frame {
294 if f.dwarf == nil && !f.triedDwarf {
295
296
297 f.dwarf, _ = f.file.DWARF()
298 f.triedDwarf = true
299 }
300
301 if f.dwarf != nil {
302 r := f.dwarf.Reader()
303 unit, err := r.SeekPC(addr)
304 if err == nil {
305 if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil {
306 return frames
307 }
308 }
309 }
310
311 return nil
312 }
313
314
315
316 func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []driver.Frame {
317 lines, err := f.dwarf.LineReader(entry)
318 if err != nil {
319 return nil
320 }
321 var lentry dwarf.LineEntry
322 if err := lines.SeekPC(addr, &lentry); err != nil {
323 return nil
324 }
325
326
327 name := ""
328 FindName:
329 for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
330 if entry.Tag == dwarf.TagSubprogram {
331 ranges, err := f.dwarf.Ranges(entry)
332 if err != nil {
333 return nil
334 }
335 for _, pcs := range ranges {
336 if pcs[0] <= addr && addr < pcs[1] {
337 var ok bool
338
339 name, ok = entry.Val(dwarf.AttrName).(string)
340 if ok {
341 break FindName
342 }
343 }
344 }
345 }
346 }
347
348
349
350 frames := []driver.Frame{
351 {
352 Func: name,
353 File: lentry.File.Name,
354 Line: lentry.Line,
355 },
356 }
357
358 return frames
359 }
360
361 func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*driver.Sym, error) {
362 if f.sym == nil {
363 sym, err := f.file.Symbols()
364 if err != nil {
365 return nil, err
366 }
367 f.sym = sym
368 }
369 var out []*driver.Sym
370 for _, s := range f.sym {
371
372
373 if s.Addr == 0 && s.Size == 0 {
374 continue
375 }
376 if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
377 out = append(out, &driver.Sym{
378 Name: []string{s.Name},
379 File: f.name,
380 Start: s.Addr,
381 End: s.Addr + uint64(s.Size) - 1,
382 })
383 }
384 }
385 return out, nil
386 }
387
388 func (f *file) Close() error {
389 f.file.Close()
390 return nil
391 }
392
393
394
395 var newUI = func() driver.UI { return nil }
396
View as plain text