暫無描述

driver.go 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 "callgrind", "kcachegrind":
  115. trim = false
  116. v.set("addresses", "t")
  117. case "disasm", "weblist":
  118. trim = false
  119. v.set("addressnoinlines", "t")
  120. case "peek":
  121. trim, focus, hide = false, false, false
  122. case "list":
  123. v.set("nodecount", "0")
  124. v.set("lines", "t")
  125. case "text", "top", "topproto":
  126. if v["nodecount"].intValue() == -1 {
  127. v.set("nodecount", "0")
  128. }
  129. default:
  130. if v["nodecount"].intValue() == -1 {
  131. v.set("nodecount", "80")
  132. }
  133. }
  134. if trim == false {
  135. v.set("nodecount", "0")
  136. v.set("nodefraction", "0")
  137. v.set("edgefraction", "0")
  138. }
  139. if focus == false {
  140. v.set("focus", "")
  141. v.set("ignore", "")
  142. }
  143. if tagfocus == false {
  144. v.set("tagfocus", "")
  145. v.set("tagignore", "")
  146. }
  147. if hide == false {
  148. v.set("hide", "")
  149. v.set("show", "")
  150. }
  151. return v
  152. }
  153. func aggregate(prof *profile.Profile, v variables) error {
  154. var inlines, function, filename, linenumber, address bool
  155. switch {
  156. case v["addresses"].boolValue():
  157. return nil
  158. case v["lines"].boolValue():
  159. inlines = true
  160. function = true
  161. filename = true
  162. linenumber = true
  163. case v["files"].boolValue():
  164. inlines = true
  165. filename = true
  166. case v["functions"].boolValue():
  167. inlines = true
  168. function = true
  169. filename = true
  170. case v["noinlines"].boolValue():
  171. function = true
  172. filename = true
  173. case v["addressnoinlines"].boolValue():
  174. function = true
  175. filename = true
  176. linenumber = true
  177. address = true
  178. case v["functionnameonly"].boolValue():
  179. inlines = true
  180. function = true
  181. default:
  182. return fmt.Errorf("unexpected granularity")
  183. }
  184. return prof.Aggregate(inlines, function, filename, linenumber, address)
  185. }
  186. func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) {
  187. si, mean := vars["sample_index"].value, vars["mean"].boolValue()
  188. value, meanDiv, sample, err := sampleFormat(p, si, mean)
  189. if err != nil {
  190. return nil, err
  191. }
  192. stype := sample.Type
  193. if mean {
  194. stype = "mean_" + stype
  195. }
  196. if vars["divide_by"].floatValue() == 0 {
  197. return nil, fmt.Errorf("zero divisor specified")
  198. }
  199. ropt := &report.Options{
  200. CumSort: vars["cum"].boolValue(),
  201. CallTree: vars["call_tree"].boolValue(),
  202. DropNegative: vars["drop_negative"].boolValue(),
  203. PositivePercentages: vars["positive_percentages"].boolValue(),
  204. CompactLabels: vars["compact_labels"].boolValue(),
  205. Ratio: 1 / vars["divide_by"].floatValue(),
  206. NodeCount: vars["nodecount"].intValue(),
  207. NodeFraction: vars["nodefraction"].floatValue(),
  208. EdgeFraction: vars["edgefraction"].floatValue(),
  209. SampleValue: value,
  210. SampleMeanDivisor: meanDiv,
  211. SampleType: stype,
  212. SampleUnit: sample.Unit,
  213. OutputUnit: vars["unit"].value,
  214. SourcePath: vars["source_path"].stringValue(),
  215. }
  216. if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
  217. ropt.Title = filepath.Base(p.Mapping[0].File)
  218. }
  219. return ropt, nil
  220. }
  221. type sampleValueFunc func([]int64) int64
  222. // sampleFormat returns a function to extract values out of a profile.Sample,
  223. // and the type/units of those values.
  224. func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
  225. if len(p.SampleType) == 0 {
  226. return nil, nil, nil, fmt.Errorf("profile has no samples")
  227. }
  228. index, err := p.SampleIndexByName(sampleIndex)
  229. if err != nil {
  230. return nil, nil, nil, err
  231. }
  232. value = valueExtractor(index)
  233. if mean {
  234. meanDiv = valueExtractor(0)
  235. }
  236. v = p.SampleType[index]
  237. return
  238. }
  239. func valueExtractor(ix int) sampleValueFunc {
  240. return func(v []int64) int64 {
  241. return v[ix]
  242. }
  243. }