]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/gc_test.go
0c21cd43cd9faccc180840cf1bdc1875e1b7f8b7
[gostls13.git] / src / runtime / gc_test.go
1 // Copyright 2011 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
5 package runtime_test
6
7 import (
8         "fmt"
9         "math/rand"
10         "os"
11         "reflect"
12         "runtime"
13         "runtime/debug"
14         "sort"
15         "strings"
16         "sync"
17         "sync/atomic"
18         "testing"
19         "time"
20         "unsafe"
21 )
22
23 func TestGcSys(t *testing.T) {
24         t.Skip("skipping known-flaky test; golang.org/issue/37331")
25         if os.Getenv("GOGC") == "off" {
26                 t.Skip("skipping test; GOGC=off in environment")
27         }
28         got := runTestProg(t, "testprog", "GCSys")
29         want := "OK\n"
30         if got != want {
31                 t.Fatalf("expected %q, but got %q", want, got)
32         }
33 }
34
35 func TestGcDeepNesting(t *testing.T) {
36         type T [2][2][2][2][2][2][2][2][2][2]*int
37         a := new(T)
38
39         // Prevent the compiler from applying escape analysis.
40         // This makes sure new(T) is allocated on heap, not on the stack.
41         t.Logf("%p", a)
42
43         a[0][0][0][0][0][0][0][0][0][0] = new(int)
44         *a[0][0][0][0][0][0][0][0][0][0] = 13
45         runtime.GC()
46         if *a[0][0][0][0][0][0][0][0][0][0] != 13 {
47                 t.Fail()
48         }
49 }
50
51 func TestGcMapIndirection(t *testing.T) {
52         defer debug.SetGCPercent(debug.SetGCPercent(1))
53         runtime.GC()
54         type T struct {
55                 a [256]int
56         }
57         m := make(map[T]T)
58         for i := 0; i < 2000; i++ {
59                 var a T
60                 a.a[0] = i
61                 m[a] = T{}
62         }
63 }
64
65 func TestGcArraySlice(t *testing.T) {
66         type X struct {
67                 buf     [1]byte
68                 nextbuf []byte
69                 next    *X
70         }
71         var head *X
72         for i := 0; i < 10; i++ {
73                 p := &X{}
74                 p.buf[0] = 42
75                 p.next = head
76                 if head != nil {
77                         p.nextbuf = head.buf[:]
78                 }
79                 head = p
80                 runtime.GC()
81         }
82         for p := head; p != nil; p = p.next {
83                 if p.buf[0] != 42 {
84                         t.Fatal("corrupted heap")
85                 }
86         }
87 }
88
89 func TestGcRescan(t *testing.T) {
90         type X struct {
91                 c     chan error
92                 nextx *X
93         }
94         type Y struct {
95                 X
96                 nexty *Y
97                 p     *int
98         }
99         var head *Y
100         for i := 0; i < 10; i++ {
101                 p := &Y{}
102                 p.c = make(chan error)
103                 if head != nil {
104                         p.nextx = &head.X
105                 }
106                 p.nexty = head
107                 p.p = new(int)
108                 *p.p = 42
109                 head = p
110                 runtime.GC()
111         }
112         for p := head; p != nil; p = p.nexty {
113                 if *p.p != 42 {
114                         t.Fatal("corrupted heap")
115                 }
116         }
117 }
118
119 func TestGcLastTime(t *testing.T) {
120         ms := new(runtime.MemStats)
121         t0 := time.Now().UnixNano()
122         runtime.GC()
123         t1 := time.Now().UnixNano()
124         runtime.ReadMemStats(ms)
125         last := int64(ms.LastGC)
126         if t0 > last || last > t1 {
127                 t.Fatalf("bad last GC time: got %v, want [%v, %v]", last, t0, t1)
128         }
129         pause := ms.PauseNs[(ms.NumGC+255)%256]
130         // Due to timer granularity, pause can actually be 0 on windows
131         // or on virtualized environments.
132         if pause == 0 {
133                 t.Logf("last GC pause was 0")
134         } else if pause > 10e9 {
135                 t.Logf("bad last GC pause: got %v, want [0, 10e9]", pause)
136         }
137 }
138
139 var hugeSink any
140
141 func TestHugeGCInfo(t *testing.T) {
142         // The test ensures that compiler can chew these huge types even on weakest machines.
143         // The types are not allocated at runtime.
144         if hugeSink != nil {
145                 // 400MB on 32 bots, 4TB on 64-bits.
146                 const n = (400 << 20) + (unsafe.Sizeof(uintptr(0))-4)<<40
147                 hugeSink = new([n]*byte)
148                 hugeSink = new([n]uintptr)
149                 hugeSink = new(struct {
150                         x float64
151                         y [n]*byte
152                         z []string
153                 })
154                 hugeSink = new(struct {
155                         x float64
156                         y [n]uintptr
157                         z []string
158                 })
159         }
160 }
161
162 func TestPeriodicGC(t *testing.T) {
163         if runtime.GOARCH == "wasm" {
164                 t.Skip("no sysmon on wasm yet")
165         }
166
167         // Make sure we're not in the middle of a GC.
168         runtime.GC()
169
170         var ms1, ms2 runtime.MemStats
171         runtime.ReadMemStats(&ms1)
172
173         // Make periodic GC run continuously.
174         orig := *runtime.ForceGCPeriod
175         *runtime.ForceGCPeriod = 0
176
177         // Let some periodic GCs happen. In a heavily loaded system,
178         // it's possible these will be delayed, so this is designed to
179         // succeed quickly if things are working, but to give it some
180         // slack if things are slow.
181         var numGCs uint32
182         const want = 2
183         for i := 0; i < 200 && numGCs < want; i++ {
184                 time.Sleep(5 * time.Millisecond)
185
186                 // Test that periodic GC actually happened.
187                 runtime.ReadMemStats(&ms2)
188                 numGCs = ms2.NumGC - ms1.NumGC
189         }
190         *runtime.ForceGCPeriod = orig
191
192         if numGCs < want {
193                 t.Fatalf("no periodic GC: got %v GCs, want >= 2", numGCs)
194         }
195 }
196
197 func TestGcZombieReporting(t *testing.T) {
198         // This test is somewhat sensitive to how the allocator works.
199         // Pointers in zombies slice may cross-span, thus we
200         // add invalidptr=0 for avoiding the badPointer check.
201         // See issue https://golang.org/issues/49613/
202         got := runTestProg(t, "testprog", "GCZombie", "GODEBUG=invalidptr=0")
203         want := "found pointer to free object"
204         if !strings.Contains(got, want) {
205                 t.Fatalf("expected %q in output, but got %q", want, got)
206         }
207 }
208
209 func TestGCTestMoveStackOnNextCall(t *testing.T) {
210         t.Parallel()
211         var onStack int
212         // GCTestMoveStackOnNextCall can fail in rare cases if there's
213         // a preemption. This won't happen many times in quick
214         // succession, so just retry a few times.
215         for retry := 0; retry < 5; retry++ {
216                 runtime.GCTestMoveStackOnNextCall()
217                 if moveStackCheck(t, &onStack, uintptr(unsafe.Pointer(&onStack))) {
218                         // Passed.
219                         return
220                 }
221         }
222         t.Fatal("stack did not move")
223 }
224
225 // This must not be inlined because the point is to force a stack
226 // growth check and move the stack.
227 //
228 //go:noinline
229 func moveStackCheck(t *testing.T, new *int, old uintptr) bool {
230         // new should have been updated by the stack move;
231         // old should not have.
232
233         // Capture new's value before doing anything that could
234         // further move the stack.
235         new2 := uintptr(unsafe.Pointer(new))
236
237         t.Logf("old stack pointer %x, new stack pointer %x", old, new2)
238         if new2 == old {
239                 // Check that we didn't screw up the test's escape analysis.
240                 if cls := runtime.GCTestPointerClass(unsafe.Pointer(new)); cls != "stack" {
241                         t.Fatalf("test bug: new (%#x) should be a stack pointer, not %s", new2, cls)
242                 }
243                 // This was a real failure.
244                 return false
245         }
246         return true
247 }
248
249 func TestGCTestMoveStackRepeatedly(t *testing.T) {
250         // Move the stack repeatedly to make sure we're not doubling
251         // it each time.
252         for i := 0; i < 100; i++ {
253                 runtime.GCTestMoveStackOnNextCall()
254                 moveStack1(false)
255         }
256 }
257
258 //go:noinline
259 func moveStack1(x bool) {
260         // Make sure this function doesn't get auto-nosplit.
261         if x {
262                 println("x")
263         }
264 }
265
266 func TestGCTestIsReachable(t *testing.T) {
267         var all, half []unsafe.Pointer
268         var want uint64
269         for i := 0; i < 16; i++ {
270                 // The tiny allocator muddies things, so we use a
271                 // scannable type.
272                 p := unsafe.Pointer(new(*int))
273                 all = append(all, p)
274                 if i%2 == 0 {
275                         half = append(half, p)
276                         want |= 1 << i
277                 }
278         }
279
280         got := runtime.GCTestIsReachable(all...)
281         if want != got {
282                 t.Fatalf("did not get expected reachable set; want %b, got %b", want, got)
283         }
284         runtime.KeepAlive(half)
285 }
286
287 var pointerClassBSS *int
288 var pointerClassData = 42
289
290 func TestGCTestPointerClass(t *testing.T) {
291         t.Parallel()
292         check := func(p unsafe.Pointer, want string) {
293                 t.Helper()
294                 got := runtime.GCTestPointerClass(p)
295                 if got != want {
296                         // Convert the pointer to a uintptr to avoid
297                         // escaping it.
298                         t.Errorf("for %#x, want class %s, got %s", uintptr(p), want, got)
299                 }
300         }
301         var onStack int
302         var notOnStack int
303         check(unsafe.Pointer(&onStack), "stack")
304         check(unsafe.Pointer(runtime.Escape(&notOnStack)), "heap")
305         check(unsafe.Pointer(&pointerClassBSS), "bss")
306         check(unsafe.Pointer(&pointerClassData), "data")
307         check(nil, "other")
308 }
309
310 func BenchmarkSetTypePtr(b *testing.B) {
311         benchSetType[*byte](b)
312 }
313
314 func BenchmarkSetTypePtr8(b *testing.B) {
315         benchSetType[[8]*byte](b)
316 }
317
318 func BenchmarkSetTypePtr16(b *testing.B) {
319         benchSetType[[16]*byte](b)
320 }
321
322 func BenchmarkSetTypePtr32(b *testing.B) {
323         benchSetType[[32]*byte](b)
324 }
325
326 func BenchmarkSetTypePtr64(b *testing.B) {
327         benchSetType[[64]*byte](b)
328 }
329
330 func BenchmarkSetTypePtr126(b *testing.B) {
331         benchSetType[[126]*byte](b)
332 }
333
334 func BenchmarkSetTypePtr128(b *testing.B) {
335         benchSetType[[128]*byte](b)
336 }
337
338 func BenchmarkSetTypePtrSlice(b *testing.B) {
339         benchSetTypeSlice[*byte](b, 1<<10)
340 }
341
342 type Node1 struct {
343         Value       [1]uintptr
344         Left, Right *byte
345 }
346
347 func BenchmarkSetTypeNode1(b *testing.B) {
348         benchSetType[Node1](b)
349 }
350
351 func BenchmarkSetTypeNode1Slice(b *testing.B) {
352         benchSetTypeSlice[Node1](b, 32)
353 }
354
355 type Node8 struct {
356         Value       [8]uintptr
357         Left, Right *byte
358 }
359
360 func BenchmarkSetTypeNode8(b *testing.B) {
361         benchSetType[Node8](b)
362 }
363
364 func BenchmarkSetTypeNode8Slice(b *testing.B) {
365         benchSetTypeSlice[Node8](b, 32)
366 }
367
368 type Node64 struct {
369         Value       [64]uintptr
370         Left, Right *byte
371 }
372
373 func BenchmarkSetTypeNode64(b *testing.B) {
374         benchSetType[Node64](b)
375 }
376
377 func BenchmarkSetTypeNode64Slice(b *testing.B) {
378         benchSetTypeSlice[Node64](b, 32)
379 }
380
381 type Node64Dead struct {
382         Left, Right *byte
383         Value       [64]uintptr
384 }
385
386 func BenchmarkSetTypeNode64Dead(b *testing.B) {
387         benchSetType[Node64Dead](b)
388 }
389
390 func BenchmarkSetTypeNode64DeadSlice(b *testing.B) {
391         benchSetTypeSlice[Node64Dead](b, 32)
392 }
393
394 type Node124 struct {
395         Value       [124]uintptr
396         Left, Right *byte
397 }
398
399 func BenchmarkSetTypeNode124(b *testing.B) {
400         benchSetType[Node124](b)
401 }
402
403 func BenchmarkSetTypeNode124Slice(b *testing.B) {
404         benchSetTypeSlice[Node124](b, 32)
405 }
406
407 type Node126 struct {
408         Value       [126]uintptr
409         Left, Right *byte
410 }
411
412 func BenchmarkSetTypeNode126(b *testing.B) {
413         benchSetType[Node126](b)
414 }
415
416 func BenchmarkSetTypeNode126Slice(b *testing.B) {
417         benchSetTypeSlice[Node126](b, 32)
418 }
419
420 type Node128 struct {
421         Value       [128]uintptr
422         Left, Right *byte
423 }
424
425 func BenchmarkSetTypeNode128(b *testing.B) {
426         benchSetType[Node128](b)
427 }
428
429 func BenchmarkSetTypeNode128Slice(b *testing.B) {
430         benchSetTypeSlice[Node128](b, 32)
431 }
432
433 type Node130 struct {
434         Value       [130]uintptr
435         Left, Right *byte
436 }
437
438 func BenchmarkSetTypeNode130(b *testing.B) {
439         benchSetType[Node130](b)
440 }
441
442 func BenchmarkSetTypeNode130Slice(b *testing.B) {
443         benchSetTypeSlice[Node130](b, 32)
444 }
445
446 type Node1024 struct {
447         Value       [1024]uintptr
448         Left, Right *byte
449 }
450
451 func BenchmarkSetTypeNode1024(b *testing.B) {
452         benchSetType[Node1024](b)
453 }
454
455 func BenchmarkSetTypeNode1024Slice(b *testing.B) {
456         benchSetTypeSlice[Node1024](b, 32)
457 }
458
459 func benchSetType[T any](b *testing.B) {
460         b.SetBytes(int64(unsafe.Sizeof(*new(T))))
461         runtime.BenchSetType[T](b.N, b.ResetTimer)
462 }
463
464 func benchSetTypeSlice[T any](b *testing.B, len int) {
465         b.SetBytes(int64(unsafe.Sizeof(*new(T)) * uintptr(len)))
466         runtime.BenchSetTypeSlice[T](b.N, b.ResetTimer, len)
467 }
468
469 func BenchmarkAllocation(b *testing.B) {
470         type T struct {
471                 x, y *byte
472         }
473         ngo := runtime.GOMAXPROCS(0)
474         work := make(chan bool, b.N+ngo)
475         result := make(chan *T)
476         for i := 0; i < b.N; i++ {
477                 work <- true
478         }
479         for i := 0; i < ngo; i++ {
480                 work <- false
481         }
482         for i := 0; i < ngo; i++ {
483                 go func() {
484                         var x *T
485                         for <-work {
486                                 for i := 0; i < 1000; i++ {
487                                         x = &T{}
488                                 }
489                         }
490                         result <- x
491                 }()
492         }
493         for i := 0; i < ngo; i++ {
494                 <-result
495         }
496 }
497
498 func TestPrintGC(t *testing.T) {
499         if testing.Short() {
500                 t.Skip("Skipping in short mode")
501         }
502         defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
503         done := make(chan bool)
504         go func() {
505                 for {
506                         select {
507                         case <-done:
508                                 return
509                         default:
510                                 runtime.GC()
511                         }
512                 }
513         }()
514         for i := 0; i < 1e4; i++ {
515                 func() {
516                         defer print("")
517                 }()
518         }
519         close(done)
520 }
521
522 func testTypeSwitch(x any) error {
523         switch y := x.(type) {
524         case nil:
525                 // ok
526         case error:
527                 return y
528         }
529         return nil
530 }
531
532 func testAssert(x any) error {
533         if y, ok := x.(error); ok {
534                 return y
535         }
536         return nil
537 }
538
539 func testAssertVar(x any) error {
540         var y, ok = x.(error)
541         if ok {
542                 return y
543         }
544         return nil
545 }
546
547 var a bool
548
549 //go:noinline
550 func testIfaceEqual(x any) {
551         if x == "abc" {
552                 a = true
553         }
554 }
555
556 func TestPageAccounting(t *testing.T) {
557         // Grow the heap in small increments. This used to drop the
558         // pages-in-use count below zero because of a rounding
559         // mismatch (golang.org/issue/15022).
560         const blockSize = 64 << 10
561         blocks := make([]*[blockSize]byte, (64<<20)/blockSize)
562         for i := range blocks {
563                 blocks[i] = new([blockSize]byte)
564         }
565
566         // Check that the running page count matches reality.
567         pagesInUse, counted := runtime.CountPagesInUse()
568         if pagesInUse != counted {
569                 t.Fatalf("mheap_.pagesInUse is %d, but direct count is %d", pagesInUse, counted)
570         }
571 }
572
573 func TestReadMemStats(t *testing.T) {
574         base, slow := runtime.ReadMemStatsSlow()
575         if base != slow {
576                 logDiff(t, "MemStats", reflect.ValueOf(base), reflect.ValueOf(slow))
577                 t.Fatal("memstats mismatch")
578         }
579 }
580
581 func logDiff(t *testing.T, prefix string, got, want reflect.Value) {
582         typ := got.Type()
583         switch typ.Kind() {
584         case reflect.Array, reflect.Slice:
585                 if got.Len() != want.Len() {
586                         t.Logf("len(%s): got %v, want %v", prefix, got, want)
587                         return
588                 }
589                 for i := 0; i < got.Len(); i++ {
590                         logDiff(t, fmt.Sprintf("%s[%d]", prefix, i), got.Index(i), want.Index(i))
591                 }
592         case reflect.Struct:
593                 for i := 0; i < typ.NumField(); i++ {
594                         gf, wf := got.Field(i), want.Field(i)
595                         logDiff(t, prefix+"."+typ.Field(i).Name, gf, wf)
596                 }
597         case reflect.Map:
598                 t.Fatal("not implemented: logDiff for map")
599         default:
600                 if got.Interface() != want.Interface() {
601                         t.Logf("%s: got %v, want %v", prefix, got, want)
602                 }
603         }
604 }
605
606 func BenchmarkReadMemStats(b *testing.B) {
607         var ms runtime.MemStats
608         const heapSize = 100 << 20
609         x := make([]*[1024]byte, heapSize/1024)
610         for i := range x {
611                 x[i] = new([1024]byte)
612         }
613
614         b.ResetTimer()
615         for i := 0; i < b.N; i++ {
616                 runtime.ReadMemStats(&ms)
617         }
618
619         runtime.KeepAlive(x)
620 }
621
622 func applyGCLoad(b *testing.B) func() {
623         // We’ll apply load to the runtime with maxProcs-1 goroutines
624         // and use one more to actually benchmark. It doesn't make sense
625         // to try to run this test with only 1 P (that's what
626         // BenchmarkReadMemStats is for).
627         maxProcs := runtime.GOMAXPROCS(-1)
628         if maxProcs == 1 {
629                 b.Skip("This benchmark can only be run with GOMAXPROCS > 1")
630         }
631
632         // Code to build a big tree with lots of pointers.
633         type node struct {
634                 children [16]*node
635         }
636         var buildTree func(depth int) *node
637         buildTree = func(depth int) *node {
638                 tree := new(node)
639                 if depth != 0 {
640                         for i := range tree.children {
641                                 tree.children[i] = buildTree(depth - 1)
642                         }
643                 }
644                 return tree
645         }
646
647         // Keep the GC busy by continuously generating large trees.
648         done := make(chan struct{})
649         var wg sync.WaitGroup
650         for i := 0; i < maxProcs-1; i++ {
651                 wg.Add(1)
652                 go func() {
653                         defer wg.Done()
654                         var hold *node
655                 loop:
656                         for {
657                                 hold = buildTree(5)
658                                 select {
659                                 case <-done:
660                                         break loop
661                                 default:
662                                 }
663                         }
664                         runtime.KeepAlive(hold)
665                 }()
666         }
667         return func() {
668                 close(done)
669                 wg.Wait()
670         }
671 }
672
673 func BenchmarkReadMemStatsLatency(b *testing.B) {
674         stop := applyGCLoad(b)
675
676         // Spend this much time measuring latencies.
677         latencies := make([]time.Duration, 0, 1024)
678
679         // Run for timeToBench hitting ReadMemStats continuously
680         // and measuring the latency.
681         b.ResetTimer()
682         var ms runtime.MemStats
683         for i := 0; i < b.N; i++ {
684                 // Sleep for a bit, otherwise we're just going to keep
685                 // stopping the world and no one will get to do anything.
686                 time.Sleep(100 * time.Millisecond)
687                 start := time.Now()
688                 runtime.ReadMemStats(&ms)
689                 latencies = append(latencies, time.Since(start))
690         }
691         // Make sure to stop the timer before we wait! The load created above
692         // is very heavy-weight and not easy to stop, so we could end up
693         // confusing the benchmarking framework for small b.N.
694         b.StopTimer()
695         stop()
696
697         // Disable the default */op metrics.
698         // ns/op doesn't mean anything because it's an average, but we
699         // have a sleep in our b.N loop above which skews this significantly.
700         b.ReportMetric(0, "ns/op")
701         b.ReportMetric(0, "B/op")
702         b.ReportMetric(0, "allocs/op")
703
704         // Sort latencies then report percentiles.
705         sort.Slice(latencies, func(i, j int) bool {
706                 return latencies[i] < latencies[j]
707         })
708         b.ReportMetric(float64(latencies[len(latencies)*50/100]), "p50-ns")
709         b.ReportMetric(float64(latencies[len(latencies)*90/100]), "p90-ns")
710         b.ReportMetric(float64(latencies[len(latencies)*99/100]), "p99-ns")
711 }
712
713 func TestUserForcedGC(t *testing.T) {
714         // Test that runtime.GC() triggers a GC even if GOGC=off.
715         defer debug.SetGCPercent(debug.SetGCPercent(-1))
716
717         var ms1, ms2 runtime.MemStats
718         runtime.ReadMemStats(&ms1)
719         runtime.GC()
720         runtime.ReadMemStats(&ms2)
721         if ms1.NumGC == ms2.NumGC {
722                 t.Fatalf("runtime.GC() did not trigger GC")
723         }
724         if ms1.NumForcedGC == ms2.NumForcedGC {
725                 t.Fatalf("runtime.GC() was not accounted in NumForcedGC")
726         }
727 }
728
729 func writeBarrierBenchmark(b *testing.B, f func()) {
730         runtime.GC()
731         var ms runtime.MemStats
732         runtime.ReadMemStats(&ms)
733         //b.Logf("heap size: %d MB", ms.HeapAlloc>>20)
734
735         // Keep GC running continuously during the benchmark, which in
736         // turn keeps the write barrier on continuously.
737         var stop uint32
738         done := make(chan bool)
739         go func() {
740                 for atomic.LoadUint32(&stop) == 0 {
741                         runtime.GC()
742                 }
743                 close(done)
744         }()
745         defer func() {
746                 atomic.StoreUint32(&stop, 1)
747                 <-done
748         }()
749
750         b.ResetTimer()
751         f()
752         b.StopTimer()
753 }
754
755 func BenchmarkWriteBarrier(b *testing.B) {
756         if runtime.GOMAXPROCS(-1) < 2 {
757                 // We don't want GC to take our time.
758                 b.Skip("need GOMAXPROCS >= 2")
759         }
760
761         // Construct a large tree both so the GC runs for a while and
762         // so we have a data structure to manipulate the pointers of.
763         type node struct {
764                 l, r *node
765         }
766         var wbRoots []*node
767         var mkTree func(level int) *node
768         mkTree = func(level int) *node {
769                 if level == 0 {
770                         return nil
771                 }
772                 n := &node{mkTree(level - 1), mkTree(level - 1)}
773                 if level == 10 {
774                         // Seed GC with enough early pointers so it
775                         // doesn't start termination barriers when it
776                         // only has the top of the tree.
777                         wbRoots = append(wbRoots, n)
778                 }
779                 return n
780         }
781         const depth = 22 // 64 MB
782         root := mkTree(22)
783
784         writeBarrierBenchmark(b, func() {
785                 var stack [depth]*node
786                 tos := -1
787
788                 // There are two write barriers per iteration, so i+=2.
789                 for i := 0; i < b.N; i += 2 {
790                         if tos == -1 {
791                                 stack[0] = root
792                                 tos = 0
793                         }
794
795                         // Perform one step of reversing the tree.
796                         n := stack[tos]
797                         if n.l == nil {
798                                 tos--
799                         } else {
800                                 n.l, n.r = n.r, n.l
801                                 stack[tos] = n.l
802                                 stack[tos+1] = n.r
803                                 tos++
804                         }
805
806                         if i%(1<<12) == 0 {
807                                 // Avoid non-preemptible loops (see issue #10958).
808                                 runtime.Gosched()
809                         }
810                 }
811         })
812
813         runtime.KeepAlive(wbRoots)
814 }
815
816 func BenchmarkBulkWriteBarrier(b *testing.B) {
817         if runtime.GOMAXPROCS(-1) < 2 {
818                 // We don't want GC to take our time.
819                 b.Skip("need GOMAXPROCS >= 2")
820         }
821
822         // Construct a large set of objects we can copy around.
823         const heapSize = 64 << 20
824         type obj [16]*byte
825         ptrs := make([]*obj, heapSize/unsafe.Sizeof(obj{}))
826         for i := range ptrs {
827                 ptrs[i] = new(obj)
828         }
829
830         writeBarrierBenchmark(b, func() {
831                 const blockSize = 1024
832                 var pos int
833                 for i := 0; i < b.N; i += blockSize {
834                         // Rotate block.
835                         block := ptrs[pos : pos+blockSize]
836                         first := block[0]
837                         copy(block, block[1:])
838                         block[blockSize-1] = first
839
840                         pos += blockSize
841                         if pos+blockSize > len(ptrs) {
842                                 pos = 0
843                         }
844
845                         runtime.Gosched()
846                 }
847         })
848
849         runtime.KeepAlive(ptrs)
850 }
851
852 func BenchmarkScanStackNoLocals(b *testing.B) {
853         var ready sync.WaitGroup
854         teardown := make(chan bool)
855         for j := 0; j < 10; j++ {
856                 ready.Add(1)
857                 go func() {
858                         x := 100000
859                         countpwg(&x, &ready, teardown)
860                 }()
861         }
862         ready.Wait()
863         b.ResetTimer()
864         for i := 0; i < b.N; i++ {
865                 b.StartTimer()
866                 runtime.GC()
867                 runtime.GC()
868                 b.StopTimer()
869         }
870         close(teardown)
871 }
872
873 func BenchmarkMSpanCountAlloc(b *testing.B) {
874         // Allocate one dummy mspan for the whole benchmark.
875         s := runtime.AllocMSpan()
876         defer runtime.FreeMSpan(s)
877
878         // n is the number of bytes to benchmark against.
879         // n must always be a multiple of 8, since gcBits is
880         // always rounded up 8 bytes.
881         for _, n := range []int{8, 16, 32, 64, 128} {
882                 b.Run(fmt.Sprintf("bits=%d", n*8), func(b *testing.B) {
883                         // Initialize a new byte slice with pseduo-random data.
884                         bits := make([]byte, n)
885                         rand.Read(bits)
886
887                         b.ResetTimer()
888                         for i := 0; i < b.N; i++ {
889                                 runtime.MSpanCountAlloc(s, bits)
890                         }
891                 })
892         }
893 }
894
895 func countpwg(n *int, ready *sync.WaitGroup, teardown chan bool) {
896         if *n == 0 {
897                 ready.Done()
898                 <-teardown
899                 return
900         }
901         *n--
902         countpwg(n, ready, teardown)
903 }
904
905 func TestMemoryLimit(t *testing.T) {
906         if testing.Short() {
907                 t.Skip("stress test that takes time to run")
908         }
909         if runtime.NumCPU() < 4 {
910                 t.Skip("want at least 4 CPUs for this test")
911         }
912         got := runTestProg(t, "testprog", "GCMemoryLimit")
913         want := "OK\n"
914         if got != want {
915                 t.Fatalf("expected %q, but got %q", want, got)
916         }
917 }
918
919 func TestMemoryLimitNoGCPercent(t *testing.T) {
920         if testing.Short() {
921                 t.Skip("stress test that takes time to run")
922         }
923         if runtime.NumCPU() < 4 {
924                 t.Skip("want at least 4 CPUs for this test")
925         }
926         got := runTestProg(t, "testprog", "GCMemoryLimitNoGCPercent")
927         want := "OK\n"
928         if got != want {
929                 t.Fatalf("expected %q, but got %q", want, got)
930         }
931 }
932
933 func TestMyGenericFunc(t *testing.T) {
934         runtime.MyGenericFunc[int]()
935 }