暫無描述

driver.go 6.7KB

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