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 func generateRawReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) (*command, *report.Report, error) {
63 p = p.Copy()
64
65
66 numLabelUnits := identifyNumLabelUnits(p, o.UI)
67
68
69 c := pprofCommands[cmd[0]]
70 if c == nil {
71 panic("unexpected nil command")
72 }
73
74 cfg = applyCommandOverrides(cmd[0], c.format, cfg)
75
76
77
78 generateTagRootsLeaves(p, cfg, o.UI)
79
80
81 relative := cfg.RelativePercentages
82 if relative {
83 if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
84 return nil, nil, err
85 }
86 }
87 ropt, err := reportOptions(p, numLabelUnits, cfg)
88 if err != nil {
89 return nil, nil, err
90 }
91 ropt.OutputFormat = c.format
92 if len(cmd) == 2 {
93 s, err := regexp.Compile(cmd[1])
94 if err != nil {
95 return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
96 }
97 ropt.Symbol = s
98 }
99
100 rpt := report.New(p, ropt)
101 if !relative {
102 if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
103 return nil, nil, err
104 }
105 }
106 if err := aggregate(p, cfg); err != nil {
107 return nil, nil, err
108 }
109
110 return c, rpt, nil
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 cfg.NoInlines = false
205 }
206
207 if !trim {
208 cfg.NodeCount = 0
209 cfg.NodeFraction = 0
210 cfg.EdgeFraction = 0
211 }
212 return cfg
213 }
214
215
216 func generateTagRootsLeaves(prof *profile.Profile, cfg config, ui plugin.UI) {
217 tagRootLabelKeys := dropEmptyStrings(strings.Split(cfg.TagRoot, ","))
218 tagLeafLabelKeys := dropEmptyStrings(strings.Split(cfg.TagLeaf, ","))
219 rootm, leafm := addLabelNodes(prof, tagRootLabelKeys, tagLeafLabelKeys, cfg.Unit)
220 warnNoMatches(cfg.TagRoot == "" || rootm, "TagRoot", ui)
221 warnNoMatches(cfg.TagLeaf == "" || leafm, "TagLeaf", ui)
222 }
223
224
225 func dropEmptyStrings(in []string) (out []string) {
226 for _, s := range in {
227 if s != "" {
228 out = append(out, s)
229 }
230 }
231 return
232 }
233
234 func aggregate(prof *profile.Profile, cfg config) error {
235 var function, filename, linenumber, address bool
236 inlines := !cfg.NoInlines
237 switch cfg.Granularity {
238 case "addresses":
239 if inlines {
240 return nil
241 }
242 function = true
243 filename = true
244 linenumber = true
245 address = true
246 case "lines":
247 function = true
248 filename = true
249 linenumber = true
250 case "files":
251 filename = true
252 case "functions":
253 function = true
254 case "filefunctions":
255 function = true
256 filename = true
257 default:
258 return fmt.Errorf("unexpected granularity")
259 }
260 return prof.Aggregate(inlines, function, filename, linenumber, address)
261 }
262
263 func reportOptions(p *profile.Profile, numLabelUnits map[string]string, cfg config) (*report.Options, error) {
264 si, mean := cfg.SampleIndex, cfg.Mean
265 value, meanDiv, sample, err := sampleFormat(p, si, mean)
266 if err != nil {
267 return nil, err
268 }
269
270 stype := sample.Type
271 if mean {
272 stype = "mean_" + stype
273 }
274
275 if cfg.DivideBy == 0 {
276 return nil, fmt.Errorf("zero divisor specified")
277 }
278
279 var filters []string
280 addFilter := func(k string, v string) {
281 if v != "" {
282 filters = append(filters, k+"="+v)
283 }
284 }
285 addFilter("focus", cfg.Focus)
286 addFilter("ignore", cfg.Ignore)
287 addFilter("hide", cfg.Hide)
288 addFilter("show", cfg.Show)
289 addFilter("show_from", cfg.ShowFrom)
290 addFilter("tagfocus", cfg.TagFocus)
291 addFilter("tagignore", cfg.TagIgnore)
292 addFilter("tagshow", cfg.TagShow)
293 addFilter("taghide", cfg.TagHide)
294
295 ropt := &report.Options{
296 CumSort: cfg.Sort == "cum",
297 CallTree: cfg.CallTree,
298 DropNegative: cfg.DropNegative,
299
300 CompactLabels: cfg.CompactLabels,
301 Ratio: 1 / cfg.DivideBy,
302
303 NodeCount: cfg.NodeCount,
304 NodeFraction: cfg.NodeFraction,
305 EdgeFraction: cfg.EdgeFraction,
306
307 ActiveFilters: filters,
308 NumLabelUnits: numLabelUnits,
309
310 SampleValue: value,
311 SampleMeanDivisor: meanDiv,
312 SampleType: stype,
313 SampleUnit: sample.Unit,
314
315 OutputUnit: cfg.Unit,
316
317 SourcePath: cfg.SourcePath,
318 TrimPath: cfg.TrimPath,
319
320 IntelSyntax: cfg.IntelSyntax,
321 }
322
323 if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
324 ropt.Title = filepath.Base(p.Mapping[0].File)
325 }
326
327 return ropt, nil
328 }
329
330
331
332 func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]string {
333 numLabelUnits, ignoredUnits := p.NumLabelUnits()
334
335
336
337 for k, units := range ignoredUnits {
338 ui.PrintErr(fmt.Sprintf("For tag %s used unit %s, also encountered unit(s) %s", k, numLabelUnits[k], strings.Join(units, ", ")))
339 }
340 return numLabelUnits
341 }
342
343 type sampleValueFunc func([]int64) int64
344
345
346
347 func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
348 if len(p.SampleType) == 0 {
349 return nil, nil, nil, fmt.Errorf("profile has no samples")
350 }
351 index, err := p.SampleIndexByName(sampleIndex)
352 if err != nil {
353 return nil, nil, nil, err
354 }
355 value = valueExtractor(index)
356 if mean {
357 meanDiv = valueExtractor(0)
358 }
359 v = p.SampleType[index]
360 return
361 }
362
363 func valueExtractor(ix int) sampleValueFunc {
364 return func(v []int64) int64 {
365 return v[ix]
366 }
367 }
368
View as plain text