/libgo/go/runtime/pprof/mprof_test.go

https://gitlab.com/adotout/gcc · Go · 179 lines · 140 code · 26 blank · 13 comment · 24 complexity · 3bb0c12700bed968c9aeb547498ca716 MD5 · raw file

  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build !js
  5. package pprof
  6. import (
  7. "bytes"
  8. "fmt"
  9. "internal/profile"
  10. "reflect"
  11. "regexp"
  12. "runtime"
  13. "testing"
  14. "unsafe"
  15. )
  16. var memSink any
  17. func allocateTransient1M() {
  18. for i := 0; i < 1024; i++ {
  19. memSink = &struct{ x [1024]byte }{}
  20. }
  21. }
  22. //go:noinline
  23. func allocateTransient2M() {
  24. memSink = make([]byte, 2<<20)
  25. }
  26. func allocateTransient2MInline() {
  27. memSink = make([]byte, 4<<20)
  28. }
  29. type Obj32 struct {
  30. link *Obj32
  31. pad [32 - unsafe.Sizeof(uintptr(0))]byte
  32. }
  33. var persistentMemSink *Obj32
  34. func allocatePersistent1K() {
  35. for i := 0; i < 32; i++ {
  36. // Can't use slice because that will introduce implicit allocations.
  37. obj := &Obj32{link: persistentMemSink}
  38. persistentMemSink = obj
  39. }
  40. }
  41. // Allocate transient memory using reflect.Call.
  42. func allocateReflectTransient() {
  43. memSink = make([]byte, 3<<20)
  44. }
  45. func allocateReflect() {
  46. rv := reflect.ValueOf(allocateReflectTransient)
  47. rv.Call(nil)
  48. }
  49. var memoryProfilerRun = 0
  50. func TestMemoryProfiler(t *testing.T) {
  51. // Disable sampling, otherwise it's difficult to assert anything.
  52. oldRate := runtime.MemProfileRate
  53. runtime.MemProfileRate = 1
  54. defer func() {
  55. runtime.MemProfileRate = oldRate
  56. }()
  57. // Allocate a meg to ensure that mcache.nextSample is updated to 1.
  58. for i := 0; i < 1024; i++ {
  59. memSink = make([]byte, 1024)
  60. }
  61. // Do the interesting allocations.
  62. allocateTransient1M()
  63. allocateTransient2M()
  64. allocateTransient2MInline()
  65. allocatePersistent1K()
  66. allocateReflect()
  67. memSink = nil
  68. runtime.GC() // materialize stats
  69. memoryProfilerRun++
  70. tests := []struct {
  71. stk []string
  72. legacy string
  73. }{{
  74. stk: []string{"runtime/pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"},
  75. legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f x]+
  76. # 0x[0-9,a-f]+ runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*/mprof_test\.go:47
  77. # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test\.go:82
  78. `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
  79. }, {
  80. stk: []string{"runtime/pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"},
  81. legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
  82. # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*/mprof_test.go:24
  83. # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:79
  84. `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
  85. }, {
  86. stk: []string{"runtime/pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"},
  87. // This should start with "0: 0" but gccgo's imprecise
  88. // GC means that sometimes the value is not collected.
  89. legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
  90. # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*/mprof_test.go:30
  91. # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:80
  92. `, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun),
  93. }, {
  94. stk: []string{"runtime/pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"},
  95. legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
  96. # 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*/mprof_test.go:34
  97. # 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/mprof_test.go:81
  98. `, memoryProfilerRun, (4<<20)*memoryProfilerRun, memoryProfilerRun, (4<<20)*memoryProfilerRun),
  99. }, {
  100. stk: []string{"runtime/pprof.allocateReflectTransient"},
  101. legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @( 0x[0-9,a-f]+)+
  102. # 0x[0-9,a-f]+ runtime/pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*/mprof_test.go:55
  103. `, memoryProfilerRun, (3<<20)*memoryProfilerRun, memoryProfilerRun, (3<<20)*memoryProfilerRun),
  104. }}
  105. t.Run("debug=1", func(t *testing.T) {
  106. var buf bytes.Buffer
  107. if err := Lookup("heap").WriteTo(&buf, 1); err != nil {
  108. t.Fatalf("failed to write heap profile: %v", err)
  109. }
  110. for _, test := range tests {
  111. if !regexp.MustCompile(test.legacy).Match(buf.Bytes()) {
  112. t.Fatalf("The entry did not match:\n%v\n\nProfile:\n%v\n", test.legacy, buf.String())
  113. }
  114. }
  115. })
  116. t.Run("proto", func(t *testing.T) {
  117. var buf bytes.Buffer
  118. if err := Lookup("heap").WriteTo(&buf, 0); err != nil {
  119. t.Fatalf("failed to write heap profile: %v", err)
  120. }
  121. p, err := profile.Parse(&buf)
  122. if err != nil {
  123. t.Fatalf("failed to parse heap profile: %v", err)
  124. }
  125. t.Logf("Profile = %v", p)
  126. stks := stacks(p)
  127. for _, test := range tests {
  128. if !containsStack(stks, test.stk) {
  129. t.Logf("stks:\n%v", stks)
  130. t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p)
  131. }
  132. }
  133. if !containsInlinedCall(TestMemoryProfiler, 4<<10) {
  134. t.Logf("Can't determine whether allocateTransient2MInline was inlined into TestMemoryProfiler.")
  135. return
  136. }
  137. // Check the inlined function location is encoded correctly.
  138. for _, loc := range p.Location {
  139. inlinedCaller, inlinedCallee := false, false
  140. for _, line := range loc.Line {
  141. if line.Function.Name == "runtime/pprof.allocateTransient2MInline" {
  142. inlinedCallee = true
  143. }
  144. if inlinedCallee && line.Function.Name == "runtime/pprof.TestMemoryProfiler" {
  145. inlinedCaller = true
  146. }
  147. }
  148. if inlinedCallee != inlinedCaller {
  149. t.Errorf("want allocateTransient2MInline after TestMemoryProfiler in one location, got separate location entries:\n%v", loc)
  150. }
  151. }
  152. })
  153. }