Source file src/runtime/testdata/testgoroutineleakprofile/simple.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  	"os"
     9  	"runtime"
    10  	"runtime/pprof"
    11  	"sync"
    12  )
    13  
    14  // This is a set of micro-tests with obvious goroutine leaks that
    15  // ensures goroutine leak detection works.
    16  //
    17  // Tests in this file are not flaky iff. run with GOMAXPROCS=1.
    18  // The main goroutine forcefully yields via `runtime.Gosched()` before
    19  // running the profiler. This moves them to the back of the run queue,
    20  // allowing the leaky goroutines to be scheduled beforehand and get stuck.
    21  
    22  func init() {
    23  	register("NilRecv", NilRecv)
    24  	register("NilSend", NilSend)
    25  	register("SelectNoCases", SelectNoCases)
    26  	register("ChanRecv", ChanRecv)
    27  	register("ChanSend", ChanSend)
    28  	register("Select", Select)
    29  	register("WaitGroup", WaitGroup)
    30  	register("MutexStack", MutexStack)
    31  	register("MutexHeap", MutexHeap)
    32  	register("RWMutexRLock", RWMutexRLock)
    33  	register("RWMutexLock", RWMutexLock)
    34  	register("Cond", Cond)
    35  	register("Mixed", Mixed)
    36  	register("NoLeakGlobal", NoLeakGlobal)
    37  }
    38  
    39  func NilRecv() {
    40  	prof := pprof.Lookup("goroutineleak")
    41  	go func() {
    42  		var c chan int
    43  		<-c
    44  		panic("should not be reached")
    45  	}()
    46  	// Yield several times to allow the child goroutine to run.
    47  	for i := 0; i < yieldCount; i++ {
    48  		runtime.Gosched()
    49  	}
    50  	prof.WriteTo(os.Stdout, 2)
    51  }
    52  
    53  func NilSend() {
    54  	prof := pprof.Lookup("goroutineleak")
    55  	go func() {
    56  		var c chan int
    57  		c <- 0
    58  		panic("should not be reached")
    59  	}()
    60  	// Yield several times to allow the child goroutine to run.
    61  	for i := 0; i < yieldCount; i++ {
    62  		runtime.Gosched()
    63  	}
    64  	prof.WriteTo(os.Stdout, 2)
    65  }
    66  
    67  func ChanRecv() {
    68  	prof := pprof.Lookup("goroutineleak")
    69  	go func() {
    70  		<-make(chan int)
    71  		panic("should not be reached")
    72  	}()
    73  	// Yield several times to allow the child goroutine to run.
    74  	for i := 0; i < yieldCount; i++ {
    75  		runtime.Gosched()
    76  	}
    77  	prof.WriteTo(os.Stdout, 2)
    78  }
    79  
    80  func SelectNoCases() {
    81  	prof := pprof.Lookup("goroutineleak")
    82  	go func() {
    83  		select {}
    84  		panic("should not be reached")
    85  	}()
    86  	// Yield several times to allow the child goroutine to run.
    87  	for i := 0; i < yieldCount; i++ {
    88  		runtime.Gosched()
    89  	}
    90  	prof.WriteTo(os.Stdout, 2)
    91  }
    92  
    93  func ChanSend() {
    94  	prof := pprof.Lookup("goroutineleak")
    95  	go func() {
    96  		make(chan int) <- 0
    97  		panic("should not be reached")
    98  	}()
    99  	// Yield several times to allow the child goroutine to run.
   100  	for i := 0; i < yieldCount; i++ {
   101  		runtime.Gosched()
   102  	}
   103  	prof.WriteTo(os.Stdout, 2)
   104  }
   105  
   106  func Select() {
   107  	prof := pprof.Lookup("goroutineleak")
   108  	go func() {
   109  		select {
   110  		case make(chan int) <- 0:
   111  		case <-make(chan int):
   112  		}
   113  		panic("should not be reached")
   114  	}()
   115  	// Yield several times to allow the child goroutine to run.
   116  	for i := 0; i < yieldCount; i++ {
   117  		runtime.Gosched()
   118  	}
   119  	prof.WriteTo(os.Stdout, 2)
   120  }
   121  
   122  func WaitGroup() {
   123  	prof := pprof.Lookup("goroutineleak")
   124  	go func() {
   125  		var wg sync.WaitGroup
   126  		wg.Add(1)
   127  		wg.Wait()
   128  		panic("should not be reached")
   129  	}()
   130  	// Yield several times to allow the child goroutine to run.
   131  	for i := 0; i < yieldCount; i++ {
   132  		runtime.Gosched()
   133  	}
   134  	prof.WriteTo(os.Stdout, 2)
   135  }
   136  
   137  func MutexStack() {
   138  	prof := pprof.Lookup("goroutineleak")
   139  	for i := 0; i < 1000; i++ {
   140  		go func() {
   141  			var mu sync.Mutex
   142  			mu.Lock()
   143  			mu.Lock()
   144  			panic("should not be reached")
   145  		}()
   146  	}
   147  	// Yield several times to allow the child goroutine to run.
   148  	for i := 0; i < yieldCount; i++ {
   149  		runtime.Gosched()
   150  	}
   151  	prof.WriteTo(os.Stdout, 2)
   152  }
   153  
   154  func MutexHeap() {
   155  	prof := pprof.Lookup("goroutineleak")
   156  	for i := 0; i < 1000; i++ {
   157  		go func() {
   158  			mu := &sync.Mutex{}
   159  			go func() {
   160  				mu.Lock()
   161  				mu.Lock()
   162  				panic("should not be reached")
   163  			}()
   164  		}()
   165  	}
   166  	// Yield several times to allow the child goroutine to run.
   167  	for i := 0; i < yieldCount; i++ {
   168  		runtime.Gosched()
   169  	}
   170  	prof.WriteTo(os.Stdout, 2)
   171  }
   172  
   173  func RWMutexRLock() {
   174  	prof := pprof.Lookup("goroutineleak")
   175  	go func() {
   176  		mu := &sync.RWMutex{}
   177  		mu.Lock()
   178  		mu.RLock()
   179  		panic("should not be reached")
   180  	}()
   181  	// Yield several times to allow the child goroutine to run.
   182  	for i := 0; i < yieldCount; i++ {
   183  		runtime.Gosched()
   184  	}
   185  	prof.WriteTo(os.Stdout, 2)
   186  }
   187  
   188  func RWMutexLock() {
   189  	prof := pprof.Lookup("goroutineleak")
   190  	go func() {
   191  		mu := &sync.RWMutex{}
   192  		mu.Lock()
   193  		mu.Lock()
   194  		panic("should not be reached")
   195  	}()
   196  	// Yield several times to allow the child goroutine to run.
   197  	for i := 0; i < yieldCount; i++ {
   198  		runtime.Gosched()
   199  	}
   200  	prof.WriteTo(os.Stdout, 2)
   201  }
   202  
   203  func Cond() {
   204  	prof := pprof.Lookup("goroutineleak")
   205  	go func() {
   206  		cond := sync.NewCond(&sync.Mutex{})
   207  		cond.L.Lock()
   208  		cond.Wait()
   209  		panic("should not be reached")
   210  	}()
   211  	// Yield several times to allow the child goroutine to run.
   212  	for i := 0; i < yieldCount; i++ {
   213  		runtime.Gosched()
   214  	}
   215  	prof.WriteTo(os.Stdout, 2)
   216  }
   217  
   218  func Mixed() {
   219  	prof := pprof.Lookup("goroutineleak")
   220  	go func() {
   221  		ch := make(chan int)
   222  		wg := sync.WaitGroup{}
   223  		wg.Add(1)
   224  		go func() {
   225  			ch <- 0
   226  			wg.Done()
   227  			panic("should not be reached")
   228  		}()
   229  		wg.Wait()
   230  		<-ch
   231  		panic("should not be reached")
   232  	}()
   233  	// Yield several times to allow the child goroutine to run.
   234  	for i := 0; i < yieldCount; i++ {
   235  		runtime.Gosched()
   236  	}
   237  	prof.WriteTo(os.Stdout, 2)
   238  }
   239  
   240  var ch = make(chan int)
   241  
   242  // No leak should be reported by this test
   243  func NoLeakGlobal() {
   244  	prof := pprof.Lookup("goroutineleak")
   245  	go func() {
   246  		<-ch
   247  	}()
   248  	// Yield several times to allow the child goroutine to run.
   249  	for i := 0; i < yieldCount; i++ {
   250  		runtime.Gosched()
   251  	}
   252  	prof.WriteTo(os.Stdout, 2)
   253  }
   254  

View as plain text