Нет описания

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