Нема описа

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