You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

141 lines
3.6 KiB

  1. // Copyright 2017 Google LLC
  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 profiler
  15. import (
  16. "bytes"
  17. "regexp"
  18. "runtime"
  19. "strings"
  20. "github.com/google/pprof/profile"
  21. )
  22. var shouldAssumeSymbolized = isSymbolizedGoVersion(runtime.Version())
  23. type function interface {
  24. Name() string
  25. FileLine(pc uintptr) (string, int)
  26. }
  27. // funcForPC is a wrapper for runtime.FuncForPC. Defined as var for testing.
  28. var funcForPC = func(pc uintptr) function {
  29. return runtime.FuncForPC(pc)
  30. }
  31. // parseAndSymbolize parses a profile from a buffer, symbolizes it
  32. // if it's not yet symbolized, and writes the profile back as a
  33. // gzip-compressed marshaled protobuf.
  34. func parseAndSymbolize(data *bytes.Buffer) error {
  35. p, err := profile.ParseData(data.Bytes())
  36. if err != nil {
  37. return err
  38. }
  39. // Do nothing if the profile is already symbolized.
  40. if symbolized(p) {
  41. return nil
  42. }
  43. // Clear the profile functions to avoid creating duplicates.
  44. p.Function = nil
  45. symbolize(p)
  46. data.Reset()
  47. return p.Write(data)
  48. }
  49. // isSymbolizedGoVersion returns true if Go version equals to or is
  50. // higher than Go 1.9. Starting Go 1.9 the profiles are symbolized
  51. // by runtime/pprof.
  52. func isSymbolizedGoVersion(goVersion string) bool {
  53. r, err := regexp.Compile(`go(1\.9|1\.[1-9][0-9]|[2-9]).*`)
  54. if err == nil && r.MatchString(goVersion) {
  55. return true
  56. }
  57. return false
  58. }
  59. // symbolized checks if all locations have symbolized function
  60. // information.
  61. func symbolized(p *profile.Profile) bool {
  62. for _, l := range p.Location {
  63. if len(l.Line) == 0 || l.Line[0].Function == nil {
  64. return false
  65. }
  66. }
  67. return true
  68. }
  69. func symbolize(p *profile.Profile) {
  70. fns := profileFunctionMap{}
  71. for _, l := range p.Location {
  72. pc := uintptr(l.Address)
  73. f := funcForPC(pc)
  74. if f == nil {
  75. continue
  76. }
  77. file, lineno := f.FileLine(pc)
  78. l.Line = []profile.Line{
  79. {
  80. Function: fns.findOrAddFunction(f.Name(), file, p),
  81. Line: int64(lineno),
  82. },
  83. }
  84. }
  85. // Trim runtime functions. Always hide runtime.goexit. Other runtime
  86. // functions are only hidden for heap profile when they appear at the beginning.
  87. isHeapProfile := p.PeriodType != nil && p.PeriodType.Type == "space"
  88. for _, s := range p.Sample {
  89. show := !isHeapProfile
  90. var i int
  91. for _, l := range s.Location {
  92. if len(l.Line) > 0 && l.Line[0].Function != nil {
  93. name := l.Line[0].Function.Name
  94. if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") {
  95. continue
  96. }
  97. }
  98. show = true
  99. s.Location[i] = l
  100. i++
  101. }
  102. // If all locations of a sample are trimmed, keep the root location.
  103. if i == 0 && len(s.Location) > 0 {
  104. s.Location[0] = s.Location[len(s.Location)-1]
  105. i = 1
  106. }
  107. s.Location = s.Location[:i]
  108. }
  109. }
  110. type profileFunctionMap map[profile.Function]*profile.Function
  111. func (fns profileFunctionMap) findOrAddFunction(name, filename string, p *profile.Profile) *profile.Function {
  112. f := profile.Function{
  113. Name: name,
  114. SystemName: name,
  115. Filename: filename,
  116. }
  117. if fp := fns[f]; fp != nil {
  118. return fp
  119. }
  120. fp := new(profile.Function)
  121. fns[f] = fp
  122. *fp = f
  123. fp.ID = uint64(len(p.Function) + 1)
  124. p.Function = append(p.Function, fp)
  125. return fp
  126. }