暫無描述

driver.go 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 == false {
  145. v.set("nodecount", "0")
  146. v.set("nodefraction", "0")
  147. v.set("edgefraction", "0")
  148. }
  149. if focus == false {
  150. v.set("focus", "")
  151. v.set("ignore", "")
  152. }
  153. if tagfocus == false {
  154. v.set("tagfocus", "")
  155. v.set("tagignore", "")
  156. }
  157. if hide == false {
  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. filename = true
  180. case v["noinlines"].boolValue():
  181. function = true
  182. filename = true
  183. case v["addressnoinlines"].boolValue():
  184. function = true
  185. filename = true
  186. linenumber = true
  187. address = true
  188. case v["functionnameonly"].boolValue():
  189. inlines = true
  190. function = true
  191. default:
  192. return fmt.Errorf("unexpected granularity")
  193. }
  194. return prof.Aggregate(inlines, function, filename, linenumber, address)
  195. }
  196. func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) {
  197. si, mean := vars["sample_index"].value, vars["mean"].boolValue()
  198. value, meanDiv, sample, err := sampleFormat(p, si, mean)
  199. if err != nil {
  200. return nil, err
  201. }
  202. stype := sample.Type
  203. if mean {
  204. stype = "mean_" + stype
  205. }
  206. if vars["divide_by"].floatValue() == 0 {
  207. return nil, fmt.Errorf("zero divisor specified")
  208. }
  209. ropt := &report.Options{
  210. CumSort: vars["cum"].boolValue(),
  211. CallTree: vars["call_tree"].boolValue(),
  212. DropNegative: vars["drop_negative"].boolValue(),
  213. PositivePercentages: vars["positive_percentages"].boolValue(),
  214. CompactLabels: vars["compact_labels"].boolValue(),
  215. Ratio: 1 / vars["divide_by"].floatValue(),
  216. NodeCount: vars["nodecount"].intValue(),
  217. NodeFraction: vars["nodefraction"].floatValue(),
  218. EdgeFraction: vars["edgefraction"].floatValue(),
  219. SampleValue: value,
  220. SampleMeanDivisor: meanDiv,
  221. SampleType: stype,
  222. SampleUnit: sample.Unit,
  223. OutputUnit: vars["unit"].value,
  224. SourcePath: vars["source_path"].stringValue(),
  225. }
  226. if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
  227. ropt.Title = filepath.Base(p.Mapping[0].File)
  228. }
  229. return ropt, nil
  230. }
  231. type sampleValueFunc func([]int64) int64
  232. // sampleFormat returns a function to extract values out of a profile.Sample,
  233. // and the type/units of those values.
  234. func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
  235. if len(p.SampleType) == 0 {
  236. return nil, nil, nil, fmt.Errorf("profile has no samples")
  237. }
  238. index, err := p.SampleIndexByName(sampleIndex)
  239. if err != nil {
  240. return nil, nil, nil, err
  241. }
  242. value = valueExtractor(index)
  243. if mean {
  244. meanDiv = valueExtractor(0)
  245. }
  246. v = p.SampleType[index]
  247. return
  248. }
  249. func valueExtractor(ix int) sampleValueFunc {
  250. return func(v []int64) int64 {
  251. return v[ix]
  252. }
  253. }