1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package driver
19
20 import (
21 "bytes"
22 "fmt"
23 "os"
24 "path/filepath"
25 "regexp"
26 "strings"
27
28 "github.com/google/pprof/internal/plugin"
29 "github.com/google/pprof/internal/report"
30 "github.com/google/pprof/profile"
31 )
32
33
34
35
36 func PProf(eo *plugin.Options) error {
37
38 defer cleanupTempFiles()
39
40 o := setDefaults(eo)
41
42 src, cmd, err := parseFlags(o)
43 if err != nil {
44 return err
45 }
46
47 p, err := fetchProfiles(src, o)
48 if err != nil {
49 return err
50 }
51
52 if cmd != nil {
53 return generateReport(p, cmd, currentConfig(), o)
54 }
55
56 if src.HTTPHostport != "" {
57 return serveWebInterface(src.HTTPHostport, p, o, src.HTTPDisableBrowser)
58 }
59 return interactive(p, o)
60 }
61
62
63 func generateRawReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) (*command, *report.Report, error) {
64
65 numLabelUnits := identifyNumLabelUnits(p, o.UI)
66
67
68 c := pprofCommands[cmd[0]]
69 if c == nil {
70 panic("unexpected nil command")
71 }
72
73 cfg = applyCommandOverrides(cmd[0], c.format, cfg)
74
75
76
77 generateTagRootsLeaves(p, cfg, o.UI)
78
79
80 relative := cfg.RelativePercentages
81 if relative {
82 if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
83 return nil, nil, err
84 }
85 }
86 ropt, err := reportOptions(p, numLabelUnits, cfg)
87 if err != nil {
88 return nil, nil, err
89 }
90 ropt.OutputFormat = c.format
91 if len(cmd) == 2 {
92 s, err := regexp.Compile(cmd[1])
93 if err != nil {
94 return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
95 }
96 ropt.Symbol = s
97 }
98
99 rpt := report.New(p, ropt)
100 if !relative {
101 if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
102 return nil, nil, err
103 }
104 }
105 if err := aggregate(p, cfg); err != nil {
106 return nil, nil, err
107 }
108
109 return c, rpt, nil
110 }
111
112
113 func generateReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) error {
114 c, rpt, err := generateRawReport(p, cmd, cfg, o)
115 if err != nil {
116 return err
117 }
118
119
120 dst := new(bytes.Buffer)
121 if err := report.Generate(dst, rpt, o.Obj); err != nil {
122 return err
123 }
124 src := dst
125
126
127 if c.postProcess != nil {
128 dst = new(bytes.Buffer)
129 if err := c.postProcess(src, dst, o.UI); err != nil {
130 return err
131 }
132 src = dst
133 }
134
135
136 output := cfg.Output
137 if output == "" {
138 if c.visualizer != nil {
139 return c.visualizer(src, os.Stdout, o.UI)
140 }
141 _, err := src.WriteTo(os.Stdout)
142 return err
143 }
144
145
146 o.UI.PrintErr("Generating report in ", output)
147 out, err := o.Writer.Open(output)
148 if err != nil {
149 return err
150 }
151 if _, err := src.WriteTo(out); err != nil {
152 out.Close()
153 return err
154 }
155 return out.Close()
156 }
157
158 func applyCommandOverrides(cmd string, outputFormat int, cfg config) config {
159
160
161
162
163
164
165
166
167 trim := cfg.Trim
168
169 switch cmd {
170 case "disasm":
171 trim = false
172 cfg.Granularity = "addresses"
173
174
175
176
177
178 cfg.NoInlines = true
179 case "weblist":
180 trim = false
181 cfg.Granularity = "addresses"
182 cfg.NoInlines = false
183 case "peek":
184 trim = false
185 case "list":
186 trim = false
187 cfg.Granularity = "lines"
188
189
190 case "text", "top", "topproto":
191 if cfg.NodeCount == -1 {
192 cfg.NodeCount = 0
193 }
194 default:
195 if cfg.NodeCount == -1 {
196 cfg.NodeCount = 80
197 }
198 }
199
200 switch outputFormat {
201 case report.Proto, report.Raw, report.Callgrind:
202 trim = false
203 cfg.Granularity = "addresses"
204 }
205
206 if !trim {
207 cfg.NodeCount = 0
208 cfg.NodeFraction = 0
209 cfg.EdgeFraction = 0
210 }
211 return cfg
212 }
213
214
215 func generateTagRootsLeaves(prof *profile.Profile, cfg config, ui plugin.UI) {
216 tagRootLabelKeys := dropEmptyStrings(strings.Split(cfg.TagRoot, ","))
217 tagLeafLabelKeys := dropEmptyStrings(strings.Split(cfg.TagLeaf, ","))
218 rootm, leafm := addLabelNodes(prof, tagRootLabelKeys, tagLeafLabelKeys, cfg.Unit)
219 warnNoMatches(cfg.TagRoot == "" || rootm, "TagRoot", ui)
220 warnNoMatches(cfg.TagLeaf == "" || leafm, "TagLeaf", ui)
221 }
222
223
224 func dropEmptyStrings(in []string) (out []string) {
225 for _, s := range in {
226 if s != "" {
227 out = append(out, s)
228 }
229 }
230 return
231 }
232
233 func aggregate(prof *profile.Profile, cfg config) error {
234 var function, filename, linenumber, address bool
235 inlines := !cfg.NoInlines
236 switch cfg.Granularity {
237 case "addresses":
238 if inlines {
239 return nil
240 }
241 function = true
242 filename = true
243 linenumber = true
244 address = true
245 case "lines":
246 function = true
247 filename = true
248 linenumber = true
249 case "files":
250 filename = true
251 case "functions":
252 function = true
253 case "filefunctions":
254 function = true
255 filename = true
256 default:
257 return fmt.Errorf("unexpected granularity")
258 }
259 return prof.Aggregate(inlines, function, filename, linenumber, address)
260 }
261
262 func reportOptions(p *profile.Profile, numLabelUnits map[string]string, cfg config) (*report.Options, error) {
263 si, mean := cfg.SampleIndex, cfg.Mean
264 value, meanDiv, sample, err := sampleFormat(p, si, mean)
265 if err != nil {
266 return nil, err
267 }
268
269 stype := sample.Type
270 if mean {
271 stype = "mean_" + stype
272 }
273
274 if cfg.DivideBy == 0 {
275 return nil, fmt.Errorf("zero divisor specified")
276 }
277
278 var filters []string
279 addFilter := func(k string, v string) {
280 if v != "" {
281 filters = append(filters, k+"="+v)
282 }
283 }
284 addFilter("focus", cfg.Focus)
285 addFilter("ignore", cfg.Ignore)
286 addFilter("hide", cfg.Hide)
287 addFilter("show", cfg.Show)
288 addFilter("show_from", cfg.ShowFrom)
289 addFilter("tagfocus", cfg.TagFocus)
290 addFilter("tagignore", cfg.TagIgnore)
291 addFilter("tagshow", cfg.TagShow)
292 addFilter("taghide", cfg.TagHide)
293
294 ropt := &report.Options{
295 CumSort: cfg.Sort == "cum",
296 CallTree: cfg.CallTree,
297 DropNegative: cfg.DropNegative,
298
299 CompactLabels: cfg.CompactLabels,
300 Ratio: 1 / cfg.DivideBy,
301
302 NodeCount: cfg.NodeCount,
303 NodeFraction: cfg.NodeFraction,
304 EdgeFraction: cfg.EdgeFraction,
305
306 ActiveFilters: filters,
307 NumLabelUnits: numLabelUnits,
308
309 SampleValue: value,
310 SampleMeanDivisor: meanDiv,
311 SampleType: stype,
312 SampleUnit: sample.Unit,
313
314 OutputUnit: cfg.Unit,
315
316 SourcePath: cfg.SourcePath,
317 TrimPath: cfg.TrimPath,
318
319 IntelSyntax: cfg.IntelSyntax,
320 }
321
322 if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
323 ropt.Title = filepath.Base(p.Mapping[0].File)
324 }
325
326 return ropt, nil
327 }
328
329
330
331 func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]string {
332 numLabelUnits, ignoredUnits := p.NumLabelUnits()
333
334
335
336 for k, units := range ignoredUnits {
337 ui.PrintErr(fmt.Sprintf("For tag %s used unit %s, also encountered unit(s) %s", k, numLabelUnits[k], strings.Join(units, ", ")))
338 }
339 return numLabelUnits
340 }
341
342 type sampleValueFunc func([]int64) int64
343
344
345
346 func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
347 if len(p.SampleType) == 0 {
348 return nil, nil, nil, fmt.Errorf("profile has no samples")
349 }
350 index, err := p.SampleIndexByName(sampleIndex)
351 if err != nil {
352 return nil, nil, nil, err
353 }
354 value = valueExtractor(index)
355 if mean {
356 meanDiv = valueExtractor(0)
357 }
358 v = p.SampleType[index]
359 return
360 }
361
362 func valueExtractor(ix int) sampleValueFunc {
363 return func(v []int64) int64 {
364 return v[ix]
365 }
366 }
367
368
369
370 type profileCopier []byte
371
372 func makeProfileCopier(src *profile.Profile) profileCopier {
373
374 var buf bytes.Buffer
375 src.WriteUncompressed(&buf)
376 return profileCopier(buf.Bytes())
377 }
378
379
380 func (c profileCopier) newCopy() *profile.Profile {
381 p, err := profile.ParseUncompressed([]byte(c))
382 if err != nil {
383 panic(err)
384 }
385 return p
386 }
387
View as plain text