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.
 
 
 

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