golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package driver implements the core pprof functionality. It can be 16 // parameterized with a flag implementation, fetch and symbolize 17 // mechanisms. 18 package driver 19 20 import ( 21 "bytes" 22 "fmt" 23 "os" 24 "path/filepath" 25 "regexp" 26 27 "github.com/google/pprof/internal/plugin" 28 "github.com/google/pprof/internal/report" 29 "github.com/google/pprof/profile" 30 ) 31 32 // PProf acquires a profile, and symbolizes it using a profile 33 // manager. Then it generates a report formatted according to the 34 // options selected through the flags package. 35 func PProf(eo *plugin.Options) error { 36 // Remove any temporary files created during pprof processing. 37 defer cleanupTempFiles() 38 39 o := setDefaults(eo) 40 41 src, cmd, err := parseFlags(o) 42 if err != nil { 43 return err 44 } 45 46 p, err := fetchProfiles(src, o) 47 if err != nil { 48 return err 49 } 50 51 if cmd != nil { 52 return generateReport(p, cmd, pprofVariables, o) 53 } 54 55 return interactive(p, o) 56 } 57 58 func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error { 59 p = p.Copy() // Prevent modification to the incoming profile. 60 61 vars = applyCommandOverrides(cmd, vars) 62 63 // Delay focus after configuring report to get percentages on all samples. 64 relative := vars["relative_percentages"].boolValue() 65 if relative { 66 if err := applyFocus(p, vars, o.UI); err != nil { 67 return err 68 } 69 } 70 ropt, err := reportOptions(p, vars) 71 if err != nil { 72 return err 73 } 74 c := pprofCommands[cmd[0]] 75 if c == nil { 76 panic("unexpected nil command") 77 } 78 ropt.OutputFormat = c.format 79 if len(cmd) == 2 { 80 s, err := regexp.Compile(cmd[1]) 81 if err != nil { 82 return fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err) 83 } 84 ropt.Symbol = s 85 } 86 87 rpt := report.New(p, ropt) 88 if !relative { 89 if err := applyFocus(p, vars, o.UI); err != nil { 90 return err 91 } 92 } 93 if err := aggregate(p, vars); err != nil { 94 return err 95 } 96 97 // Generate the report. 98 dst := new(bytes.Buffer) 99 if err := report.Generate(dst, rpt, o.Obj); err != nil { 100 return err 101 } 102 src := dst 103 104 // If necessary, perform any data post-processing. 105 if c.postProcess != nil { 106 dst = new(bytes.Buffer) 107 if err := c.postProcess(src, dst, o.UI); err != nil { 108 return err 109 } 110 src = dst 111 } 112 113 // If no output is specified, use default visualizer. 114 output := vars["output"].value 115 if output == "" { 116 if c.visualizer != nil { 117 return c.visualizer(src, os.Stdout, o.UI) 118 } 119 _, err := src.WriteTo(os.Stdout) 120 return err 121 } 122 123 // Output to specified file. 124 o.UI.PrintErr("Generating report in ", output) 125 out, err := os.Create(output) 126 if err != nil { 127 return err 128 } 129 if _, err := src.WriteTo(out); err != nil { 130 out.Close() 131 return err 132 } 133 return out.Close() 134 } 135 136 func applyCommandOverrides(cmd []string, v variables) variables { 137 trim, focus, tagfocus, hide := v["trim"].boolValue(), true, true, true 138 139 switch cmd[0] { 140 case "proto", "raw": 141 trim, focus, tagfocus, hide = false, false, false, false 142 v.set("addresses", "t") 143 case "callgrind", "kcachegrind": 144 trim = false 145 v.set("addresses", "t") 146 case "disasm", "weblist": 147 trim = false 148 v.set("addressnoinlines", "t") 149 case "peek": 150 trim, focus, hide = false, false, false 151 case "list": 152 v.set("nodecount", "0") 153 v.set("lines", "t") 154 case "text", "top", "topproto": 155 if v["nodecount"].intValue() == -1 { 156 v.set("nodecount", "0") 157 } 158 default: 159 if v["nodecount"].intValue() == -1 { 160 v.set("nodecount", "80") 161 } 162 } 163 if trim == false { 164 v.set("nodecount", "0") 165 v.set("nodefraction", "0") 166 v.set("edgefraction", "0") 167 } 168 if focus == false { 169 v.set("focus", "") 170 v.set("ignore", "") 171 } 172 if tagfocus == false { 173 v.set("tagfocus", "") 174 v.set("tagignore", "") 175 } 176 if hide == false { 177 v.set("hide", "") 178 v.set("show", "") 179 } 180 return v 181 } 182 183 func aggregate(prof *profile.Profile, v variables) error { 184 var inlines, function, filename, linenumber, address bool 185 switch { 186 case v["addresses"].boolValue(): 187 return nil 188 case v["lines"].boolValue(): 189 inlines = true 190 function = true 191 filename = true 192 linenumber = true 193 case v["files"].boolValue(): 194 inlines = true 195 filename = true 196 case v["functions"].boolValue(): 197 inlines = true 198 function = true 199 filename = true 200 case v["noinlines"].boolValue(): 201 function = true 202 filename = true 203 case v["addressnoinlines"].boolValue(): 204 function = true 205 filename = true 206 linenumber = true 207 address = true 208 case v["functionnameonly"].boolValue(): 209 inlines = true 210 function = true 211 default: 212 return fmt.Errorf("unexpected granularity") 213 } 214 return prof.Aggregate(inlines, function, filename, linenumber, address) 215 } 216 217 func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) { 218 si, mean := vars["sample_index"].value, vars["mean"].boolValue() 219 value, meanDiv, sample, err := sampleFormat(p, si, mean) 220 if err != nil { 221 return nil, err 222 } 223 224 stype := sample.Type 225 if mean { 226 stype = "mean_" + stype 227 } 228 229 if vars["divide_by"].floatValue() == 0 { 230 return nil, fmt.Errorf("zero divisor specified") 231 } 232 233 ropt := &report.Options{ 234 CumSort: vars["cum"].boolValue(), 235 CallTree: vars["call_tree"].boolValue(), 236 DropNegative: vars["drop_negative"].boolValue(), 237 PositivePercentages: vars["positive_percentages"].boolValue(), 238 239 CompactLabels: vars["compact_labels"].boolValue(), 240 Ratio: 1 / vars["divide_by"].floatValue(), 241 242 NodeCount: vars["nodecount"].intValue(), 243 NodeFraction: vars["nodefraction"].floatValue(), 244 EdgeFraction: vars["edgefraction"].floatValue(), 245 246 SampleValue: value, 247 SampleMeanDivisor: meanDiv, 248 SampleType: stype, 249 SampleUnit: sample.Unit, 250 251 OutputUnit: vars["unit"].value, 252 253 SourcePath: vars["source_path"].stringValue(), 254 } 255 256 if len(p.Mapping) > 0 && p.Mapping[0].File != "" { 257 ropt.Title = filepath.Base(p.Mapping[0].File) 258 } 259 260 return ropt, nil 261 } 262 263 type sampleValueFunc func([]int64) int64 264 265 // sampleFormat returns a function to extract values out of a profile.Sample, 266 // and the type/units of those values. 267 func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) { 268 if len(p.SampleType) == 0 { 269 return nil, nil, nil, fmt.Errorf("profile has no samples") 270 } 271 index, err := p.SampleIndexByName(sampleIndex) 272 if err != nil { 273 return nil, nil, nil, err 274 } 275 value = valueExtractor(index) 276 if mean { 277 meanDiv = valueExtractor(0) 278 } 279 v = p.SampleType[index] 280 return 281 } 282 283 func valueExtractor(ix int) sampleValueFunc { 284 return func(v []int64) int64 { 285 return v[ix] 286 } 287 }