Geen omschrijving

driver.go 7.4KB

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