Source file src/runtime/testdata/testgoroutineleakprofile/stresstests.go

     1  // Copyright 2025 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 main
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  	"runtime"
    11  	"runtime/pprof"
    12  	"sync"
    13  	"time"
    14  )
    15  
    16  const spawnGCMaxDepth = 5
    17  
    18  func init() {
    19  	register("SpawnGC", SpawnGC)
    20  	register("DaisyChain", DaisyChain)
    21  }
    22  
    23  func spawnGC(i int) {
    24  	prof := pprof.Lookup("goroutineleak")
    25  	if i == 0 {
    26  		return
    27  	}
    28  	wg := &sync.WaitGroup{}
    29  	wg.Add(i + 1)
    30  	go func() {
    31  		wg.Done()
    32  		<-make(chan int)
    33  	}()
    34  	for j := 0; j < i; j++ {
    35  		go func() {
    36  			wg.Done()
    37  			spawnGC(i - 1)
    38  		}()
    39  	}
    40  	wg.Wait()
    41  	runtime.Gosched()
    42  	if i == spawnGCMaxDepth {
    43  		prof.WriteTo(os.Stdout, 2)
    44  	} else {
    45  		// We want to concurrently trigger the profile in order to concurrently run
    46  		// the GC, but we don't want to stream all the profiles to standard output.
    47  		//
    48  		// Only output the profile for the root call to spawnGC, and otherwise stream
    49  		// the profile outputs to /dev/null to avoid jumbling.
    50  		prof.WriteTo(io.Discard, 2)
    51  	}
    52  }
    53  
    54  // SpawnGC spawns a tree of goroutine leaks and calls the goroutine leak profiler
    55  // for each node in the tree. It is supposed to stress the goroutine leak profiler
    56  // under a heavily concurrent workload.
    57  func SpawnGC() {
    58  	spawnGC(spawnGCMaxDepth)
    59  }
    60  
    61  // DaisyChain spawns a daisy-chain of runnable goroutines.
    62  //
    63  // Each goroutine in the chain creates a new channel and goroutine.
    64  //
    65  // This illustrates a pathological worstcase for the goroutine leak GC complexity,
    66  // as opposed to the regular GC, which is not negatively affected by this pattern.
    67  func DaisyChain() {
    68  	prof := pprof.Lookup("goroutineleak")
    69  	defer func() {
    70  		time.Sleep(time.Second)
    71  		prof.WriteTo(os.Stdout, 2)
    72  	}()
    73  	var chain func(i int, ch chan struct{})
    74  	chain = func(i int, ch chan struct{}) {
    75  		if i <= 0 {
    76  			go func() {
    77  				time.Sleep(time.Hour)
    78  				ch <- struct{}{}
    79  			}()
    80  			return
    81  		}
    82  		ch2 := make(chan struct{})
    83  		go chain(i-1, ch2)
    84  		<-ch2
    85  		ch <- struct{}{}
    86  	}
    87  	// The channel buffer avoids goroutine leaks.
    88  	go chain(1000, make(chan struct{}, 1))
    89  }
    90  

View as plain text