Nenhuma descrição

driver.go 7.0KB

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