Source file src/pkg/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
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, pprofVariables, 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, vars variables, 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 vars = applyCommandOverrides(cmd[0], c.format, vars)
75
76
77 relative := vars["relative_percentages"].boolValue()
78 if relative {
79 if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
80 return nil, nil, err
81 }
82 }
83 ropt, err := reportOptions(p, numLabelUnits, vars)
84 if err != nil {
85 return nil, nil, err
86 }
87 ropt.OutputFormat = c.format
88 if len(cmd) == 2 {
89 s, err := regexp.Compile(cmd[1])
90 if err != nil {
91 return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
92 }
93 ropt.Symbol = s
94 }
95
96 rpt := report.New(p, ropt)
97 if !relative {
98 if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
99 return nil, nil, err
100 }
101 }
102 if err := aggregate(p, vars); err != nil {
103 return nil, nil, err
104 }
105
106 return c, rpt, nil
107 }
108
109 func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
110 c, rpt, err := generateRawReport(p, cmd, vars, o)
111 if err != nil {
112 return err
113 }
114
115
116 dst := new(bytes.Buffer)
117 if err := report.Generate(dst, rpt, o.Obj); err != nil {
118 return err
119 }
120 src := dst
121
122
123 if c.postProcess != nil {
124 dst = new(bytes.Buffer)
125 if err := c.postProcess(src, dst, o.UI); err != nil {
126 return err
127 }
128 src = dst
129 }
130
131
132 output := vars["output"].value
133 if output == "" {
134 if c.visualizer != nil {
135 return c.visualizer(src, os.Stdout, o.UI)
136 }
137 _, err := src.WriteTo(os.Stdout)
138 return err
139 }
140
141
142 o.UI.PrintErr("Generating report in ", output)
143 out, err := o.Writer.Open(output)
144 if err != nil {
145 return err
146 }
147 if _, err := src.WriteTo(out); err != nil {
148 out.Close()
149 return err
150 }
151 return out.Close()
152 }
153
154 func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
155
156
157
158
159
160
161
162
163 trim := v["trim"].boolValue()
164
165 switch cmd {
166 case "disasm", "weblist":
167 trim = false
168 v.set("addresses", "t")
169
170
171
172
173
174 v.set("noinlines", "t")
175 case "peek":
176 trim = false
177 case "list":
178 trim = false
179 v.set("lines", "t")
180
181
182 case "text", "top", "topproto":
183 if v["nodecount"].intValue() == -1 {
184 v.set("nodecount", "0")
185 }
186 default:
187 if v["nodecount"].intValue() == -1 {
188 v.set("nodecount", "80")
189 }
190 }
191
192 switch outputFormat {
193 case report.Proto, report.Raw, report.Callgrind:
194 trim = false
195 v.set("addresses", "t")
196 v.set("noinlines", "f")
197 }
198
199 if !trim {
200 v.set("nodecount", "0")
201 v.set("nodefraction", "0")
202 v.set("edgefraction", "0")
203 }
204 return v
205 }
206
207 func aggregate(prof *profile.Profile, v variables) error {
208 var function, filename, linenumber, address bool
209 inlines := !v["noinlines"].boolValue()
210 switch {
211 case v["addresses"].boolValue():
212 if inlines {
213 return nil
214 }
215 function = true
216 filename = true
217 linenumber = true
218 address = true
219 case v["lines"].boolValue():
220 function = true
221 filename = true
222 linenumber = true
223 case v["files"].boolValue():
224 filename = true
225 case v["functions"].boolValue():
226 function = true
227 case v["filefunctions"].boolValue():
228 function = true
229 filename = true
230 default:
231 return fmt.Errorf("unexpected granularity")
232 }
233 return prof.Aggregate(inlines, function, filename, linenumber, address)
234 }
235
236 func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars variables) (*report.Options, error) {
237 si, mean := vars["sample_index"].value, vars["mean"].boolValue()
238 value, meanDiv, sample, err := sampleFormat(p, si, mean)
239 if err != nil {
240 return nil, err
241 }
242
243 stype := sample.Type
244 if mean {
245 stype = "mean_" + stype
246 }
247
248 if vars["divide_by"].floatValue() == 0 {
249 return nil, fmt.Errorf("zero divisor specified")
250 }
251
252 var filters []string
253 for _, k := range []string{"focus", "ignore", "hide", "show", "show_from", "tagfocus", "tagignore", "tagshow", "taghide"} {
254 v := vars[k].value
255 if v != "" {
256 filters = append(filters, k+"="+v)
257 }
258 }
259
260 ropt := &report.Options{
261 CumSort: vars["cum"].boolValue(),
262 CallTree: vars["call_tree"].boolValue(),
263 DropNegative: vars["drop_negative"].boolValue(),
264
265 CompactLabels: vars["compact_labels"].boolValue(),
266 Ratio: 1 / vars["divide_by"].floatValue(),
267
268 NodeCount: vars["nodecount"].intValue(),
269 NodeFraction: vars["nodefraction"].floatValue(),
270 EdgeFraction: vars["edgefraction"].floatValue(),
271
272 ActiveFilters: filters,
273 NumLabelUnits: numLabelUnits,
274
275 SampleValue: value,
276 SampleMeanDivisor: meanDiv,
277 SampleType: stype,
278 SampleUnit: sample.Unit,
279
280 OutputUnit: vars["unit"].value,
281
282 SourcePath: vars["source_path"].stringValue(),
283 TrimPath: vars["trim_path"].stringValue(),
284 }
285
286 if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
287 ropt.Title = filepath.Base(p.Mapping[0].File)
288 }
289
290 return ropt, nil
291 }
292
293
294
295 func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]string {
296 numLabelUnits, ignoredUnits := p.NumLabelUnits()
297
298
299
300 for k, units := range ignoredUnits {
301 ui.PrintErr(fmt.Sprintf("For tag %s used unit %s, also encountered unit(s) %s", k, numLabelUnits[k], strings.Join(units, ", ")))
302 }
303 return numLabelUnits
304 }
305
306 type sampleValueFunc func([]int64) int64
307
308
309
310 func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
311 if len(p.SampleType) == 0 {
312 return nil, nil, nil, fmt.Errorf("profile has no samples")
313 }
314 index, err := p.SampleIndexByName(sampleIndex)
315 if err != nil {
316 return nil, nil, nil, err
317 }
318 value = valueExtractor(index)
319 if mean {
320 meanDiv = valueExtractor(0)
321 }
322 v = p.SampleType[index]
323 return
324 }
325
326 func valueExtractor(ix int) sampleValueFunc {
327 return func(v []int64) int64 {
328 return v[ix]
329 }
330 }
331
View as plain text