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.
 
 
 

230 lines
7.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. "testing"
  18. "cloud.google.com/go/internal/testutil"
  19. "github.com/google/go-cmp/cmp/cmpopts"
  20. "github.com/google/pprof/profile"
  21. )
  22. type fakeFunc struct {
  23. name string
  24. file string
  25. lineno int
  26. }
  27. func (f *fakeFunc) Name() string {
  28. return f.name
  29. }
  30. func (f *fakeFunc) FileLine(_ uintptr) (string, int) {
  31. return f.file, f.lineno
  32. }
  33. var cmpOpt = cmpopts.IgnoreUnexported(profile.Profile{}, profile.Function{},
  34. profile.Line{}, profile.Location{}, profile.Sample{}, profile.ValueType{})
  35. // TestRuntimeFunctionTrimming tests if symbolize trims runtime functions as intended.
  36. func TestRuntimeFunctionTrimming(t *testing.T) {
  37. fakeFuncMap := map[uintptr]*fakeFunc{
  38. 0x10: {"runtime.goexit", "runtime.go", 10},
  39. 0x20: {"runtime.other", "runtime.go", 20},
  40. 0x30: {"foo", "foo.go", 30},
  41. 0x40: {"bar", "bar.go", 40},
  42. }
  43. backupFuncForPC := funcForPC
  44. funcForPC = func(pc uintptr) function {
  45. return fakeFuncMap[pc]
  46. }
  47. defer func() {
  48. funcForPC = backupFuncForPC
  49. }()
  50. testLoc := []*profile.Location{
  51. {ID: 1, Address: 0x10},
  52. {ID: 2, Address: 0x20},
  53. {ID: 3, Address: 0x30},
  54. {ID: 4, Address: 0x40},
  55. }
  56. testProfile := &profile.Profile{
  57. Sample: []*profile.Sample{
  58. {Location: []*profile.Location{testLoc[0], testLoc[1], testLoc[3], testLoc[2]}},
  59. {Location: []*profile.Location{testLoc[1], testLoc[3], testLoc[2]}},
  60. {Location: []*profile.Location{testLoc[3], testLoc[2], testLoc[1]}},
  61. {Location: []*profile.Location{testLoc[3], testLoc[2], testLoc[0]}},
  62. {Location: []*profile.Location{testLoc[0], testLoc[1], testLoc[3], testLoc[0]}},
  63. {Location: []*profile.Location{testLoc[1], testLoc[0]}},
  64. },
  65. Location: testLoc,
  66. }
  67. testProfiles := make([]*profile.Profile, 2)
  68. testProfiles[0] = testProfile.Copy()
  69. testProfiles[1] = testProfile.Copy()
  70. // Test case for CPU profile.
  71. testProfiles[0].PeriodType = &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}
  72. // Test case for heap profile.
  73. testProfiles[1].PeriodType = &profile.ValueType{Type: "space", Unit: "bytes"}
  74. wantFunc := []*profile.Function{
  75. {ID: 1, Name: "runtime.goexit", SystemName: "runtime.goexit", Filename: "runtime.go"},
  76. {ID: 2, Name: "runtime.other", SystemName: "runtime.other", Filename: "runtime.go"},
  77. {ID: 3, Name: "foo", SystemName: "foo", Filename: "foo.go"},
  78. {ID: 4, Name: "bar", SystemName: "bar", Filename: "bar.go"},
  79. }
  80. wantLoc := []*profile.Location{
  81. {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}},
  82. {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}},
  83. {ID: 3, Address: 0x30, Line: []profile.Line{{Function: wantFunc[2], Line: 30}}},
  84. {ID: 4, Address: 0x40, Line: []profile.Line{{Function: wantFunc[3], Line: 40}}},
  85. }
  86. wantProfiles := []*profile.Profile{
  87. {
  88. PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"},
  89. Sample: []*profile.Sample{
  90. {Location: []*profile.Location{wantLoc[1], wantLoc[3], wantLoc[2]}},
  91. {Location: []*profile.Location{wantLoc[1], wantLoc[3], wantLoc[2]}},
  92. {Location: []*profile.Location{wantLoc[3], wantLoc[2], wantLoc[1]}},
  93. {Location: []*profile.Location{wantLoc[3], wantLoc[2]}},
  94. {Location: []*profile.Location{wantLoc[1], wantLoc[3]}},
  95. {Location: []*profile.Location{wantLoc[1]}},
  96. },
  97. Location: wantLoc,
  98. Function: wantFunc,
  99. },
  100. {
  101. PeriodType: &profile.ValueType{Type: "space", Unit: "bytes"},
  102. Sample: []*profile.Sample{
  103. {Location: []*profile.Location{wantLoc[3], wantLoc[2]}},
  104. {Location: []*profile.Location{wantLoc[3], wantLoc[2]}},
  105. {Location: []*profile.Location{wantLoc[3], wantLoc[2], wantLoc[1]}},
  106. {Location: []*profile.Location{wantLoc[3], wantLoc[2]}},
  107. {Location: []*profile.Location{wantLoc[3]}},
  108. {Location: []*profile.Location{wantLoc[0]}},
  109. },
  110. Location: wantLoc,
  111. Function: wantFunc,
  112. },
  113. }
  114. for i := 0; i < 2; i++ {
  115. symbolize(testProfiles[i])
  116. if !testutil.Equal(testProfiles[i], wantProfiles[i], cmpOpt) {
  117. t.Errorf("incorrect trimming (testcase = %d): got {%v}, want {%v}", i, testProfiles[i], wantProfiles[i])
  118. }
  119. }
  120. }
  121. // TestParseAndSymbolize tests if parseAndSymbolize parses and symbolizes
  122. // profiles as intended.
  123. func TestParseAndSymbolize(t *testing.T) {
  124. fakeFuncMap := map[uintptr]*fakeFunc{
  125. 0x10: {"foo", "foo.go", 10},
  126. 0x20: {"bar", "bar.go", 20},
  127. }
  128. backupFuncForPC := funcForPC
  129. funcForPC = func(pc uintptr) function {
  130. return fakeFuncMap[pc]
  131. }
  132. defer func() {
  133. funcForPC = backupFuncForPC
  134. }()
  135. testLoc := []*profile.Location{
  136. {ID: 1, Address: 0x10},
  137. {ID: 2, Address: 0x20},
  138. }
  139. testProfile := &profile.Profile{
  140. SampleType: []*profile.ValueType{
  141. {Type: "cpu", Unit: "nanoseconds"},
  142. },
  143. PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"},
  144. Sample: []*profile.Sample{
  145. {Location: []*profile.Location{testLoc[0], testLoc[1]}, Value: []int64{1}},
  146. {Location: []*profile.Location{testLoc[1]}, Value: []int64{1}},
  147. },
  148. Location: testLoc,
  149. }
  150. testProfiles := make([]*profile.Profile, 2)
  151. testProfiles[0] = testProfile.Copy()
  152. testProfiles[1] = testProfile.Copy()
  153. wantFunc := []*profile.Function{
  154. {ID: 1, Name: "foo", SystemName: "foo", Filename: "foo.go"},
  155. {ID: 2, Name: "bar", SystemName: "bar", Filename: "bar.go"},
  156. }
  157. wantLoc := []*profile.Location{
  158. {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}},
  159. {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}},
  160. }
  161. wantProfile := &profile.Profile{
  162. SampleType: []*profile.ValueType{
  163. {Type: "cpu", Unit: "nanoseconds"},
  164. },
  165. PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"},
  166. Sample: []*profile.Sample{
  167. {Location: []*profile.Location{wantLoc[0], wantLoc[1]}, Value: []int64{1}},
  168. {Location: []*profile.Location{wantLoc[1]}, Value: []int64{1}},
  169. },
  170. Location: wantLoc,
  171. Function: wantFunc,
  172. }
  173. // Profile already symbolized.
  174. testProfiles[1].Location = []*profile.Location{
  175. {ID: 1, Address: 0x10, Line: []profile.Line{{Function: wantFunc[0], Line: 10}}},
  176. {ID: 2, Address: 0x20, Line: []profile.Line{{Function: wantFunc[1], Line: 20}}},
  177. }
  178. testProfiles[1].Function = []*profile.Function{
  179. {ID: 1, Name: "foo", SystemName: "foo", Filename: "foo.go"},
  180. {ID: 2, Name: "bar", SystemName: "bar", Filename: "bar.go"},
  181. }
  182. for i := 0; i < 2; i++ {
  183. var prof bytes.Buffer
  184. testProfiles[i].Write(&prof)
  185. parseAndSymbolize(&prof)
  186. gotProfile, err := profile.ParseData(prof.Bytes())
  187. if err != nil {
  188. t.Errorf("parsing symbolized profile (testcase = %d) got err: %v, want no error", i, err)
  189. }
  190. if !testutil.Equal(gotProfile, wantProfile, cmpOpt) {
  191. t.Errorf("incorrect symbolization (testcase = %d): got {%v}, want {%v}", i, gotProfile, wantProfile)
  192. }
  193. }
  194. }
  195. func TestIsSymbolizedGoVersion(t *testing.T) {
  196. for _, tc := range []struct {
  197. input string
  198. want bool
  199. }{
  200. {"go1.9beta2", true},
  201. {"go1.9", true},
  202. {"go1.9.1", true},
  203. {"go1.10", true},
  204. {"go1.10.1", true},
  205. {"go2.0", true},
  206. {"go3.1", true},
  207. {"go1.8", false},
  208. {"go1.8.1", false},
  209. {"go1.7", false},
  210. {"devel ", false},
  211. } {
  212. if got := isSymbolizedGoVersion(tc.input); got != tc.want {
  213. t.Errorf("isSymbolizedGoVersion(%v) got %v, want %v", tc.input, got, tc.want)
  214. }
  215. }
  216. }