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("SelectNoCasesMain", SelectNoCasesMain)
    27  	register("ChanRecv", ChanRecv)
    28  	register("ChanSend", ChanSend)
    29  	register("Select", Select)
    30  	register("WaitGroup", WaitGroup)
    31  	register("MutexStack", MutexStack)
    32  	register("MutexHeap", MutexHeap)
    33  	register("RWMutexRLock", RWMutexRLock)
    34  	register("RWMutexLock", RWMutexLock)
    35  	register("Cond", Cond)
    36  	register("Mixed", Mixed)
    37  	register("NoLeakGlobal", NoLeakGlobal)
    38  }
    39  
    40  func NilRecv() {
    41  	prof := pprof.Lookup("goroutineleak")
    42  	go func() {
    43  		var c chan int
    44  		<-c
    45  		panic("should not be reached")
    46  	}()
    47  	// Yield several times to allow the child goroutine to run.
    48  	for i := 0; i < yieldCount; i++ {
    49  		runtime.Gosched()
    50  	}
    51  	prof.WriteTo(os.Stdout, 2)
    52  }
    53  
    54  func NilSend() {
    55  	prof := pprof.Lookup("goroutineleak")
    56  	go func() {
    57  		var c chan int
    58  		c <- 0
    59  		panic("should not be reached")
    60  	}()
    61  	// Yield several times to allow the child goroutine to run.
    62  	for i := 0; i < yieldCount; i++ {
    63  		runtime.Gosched()
    64  	}
    65  	prof.WriteTo(os.Stdout, 2)
    66  }
    67  
    68  func ChanRecv() {
    69  	prof := pprof.Lookup("goroutineleak")
    70  	go func() {
    71  		<-make(chan int)
    72  		panic("should not be reached")
    73  	}()
    74  	// Yield several times to allow the child goroutine to run.
    75  	for i := 0; i < yieldCount; i++ {
    76  		runtime.Gosched()
    77  	}
    78  	prof.WriteTo(os.Stdout, 2)
    79  }
    80  
    81  func SelectNoCases() {
    82  	prof := pprof.Lookup("goroutineleak")
    83  	go func() {
    84  		select {}
    85  		panic("should not be reached")
    86  	}()
    87  	// Yield several times to allow the child goroutine to run.
    88  	for i := 0; i < yieldCount; i++ {
    89  		runtime.Gosched()
    90  	}
    91  	prof.WriteTo(os.Stdout, 2)
    92  }
    93  
    94  func SelectNoCasesMain() {
    95  	prof := pprof.Lookup("goroutineleak")
    96  	ch := make(chan int)
    97  	go func() {
    98  		// Should be reported as a leak.
    99  		<-ch
   100  	}()
   101  	go func() {
   102  		for i := 0; i < yieldCount; i++ {
   103  			runtime.Gosched()
   104  		}
   105  		// Write a goroutine leak profile from
   106  		// the child goroutine.
   107  		prof.WriteTo(os.Stdout, 2)
   108  		// Forcefully exit the program.
   109  		os.Exit(0)
   110  	}()
   111  	// Should not be reported as a leak.
   112  	select {}
   113  }
   114  
   115  func ChanSend() {
   116  	prof := pprof.Lookup("goroutineleak")
   117  	go func() {
   118  		make(chan int) <- 0
   119  		panic("should not be reached")
   120  	}()
   121  	// Yield several times to allow the child goroutine to run.
   122  	for i := 0; i < yieldCount; i++ {
   123  		runtime.Gosched()
   124  	}
   125  	prof.WriteTo(os.Stdout, 2)
   126  }
   127  
   128  func Select() {
   129  	prof := pprof.Lookup("goroutineleak")
   130  	go func() {
   131  		select {
   132  		case make(chan int) <- 0:
   133  		case <-make(chan int):
   134  		}
   135  		panic("should not be reached")
   136  	}()
   137  	// Yield several times to allow the child goroutine to run.
   138  	for i := 0; i < yieldCount; i++ {
   139  		runtime.Gosched()
   140  	}
   141  	prof.WriteTo(os.Stdout, 2)
   142  }
   143  
   144  func WaitGroup() {
   145  	prof := pprof.Lookup("goroutineleak")
   146  	go func() {
   147  		var wg sync.WaitGroup
   148  		wg.Add(1)
   149  		wg.Wait()
   150  		panic("should not be reached")
   151  	}()
   152  	// Yield several times to allow the child goroutine to run.
   153  	for i := 0; i < yieldCount; i++ {
   154  		runtime.Gosched()
   155  	}
   156  	prof.WriteTo(os.Stdout, 2)
   157  }
   158  
   159  func MutexStack() {
   160  	prof := pprof.Lookup("goroutineleak")
   161  	for i := 0; i < 1000; i++ {
   162  		go func() {
   163  			var mu sync.Mutex
   164  			mu.Lock()
   165  			mu.Lock()
   166  			panic("should not be reached")
   167  		}()
   168  	}
   169  	// Yield several times to allow the child goroutine to run.
   170  	for i := 0; i < yieldCount; i++ {
   171  		runtime.Gosched()
   172  	}
   173  	prof.WriteTo(os.Stdout, 2)
   174  }
   175  
   176  func MutexHeap() {
   177  	prof := pprof.Lookup("goroutineleak")
   178  	for i := 0; i < 1000; i++ {
   179  		go func() {
   180  			mu := &sync.Mutex{}
   181  			go func() {
   182  				mu.Lock()
   183  				mu.Lock()
   184  				panic("should not be reached")
   185  			}()
   186  		}()
   187  	}
   188  	// Yield several times to allow the child goroutine to run.
   189  	for i := 0; i < yieldCount; i++ {
   190  		runtime.Gosched()
   191  	}
   192  	prof.WriteTo(os.Stdout, 2)
   193  }
   194  
   195  func RWMutexRLock() {
   196  	prof := pprof.Lookup("goroutineleak")
   197  	go func() {
   198  		mu := &sync.RWMutex{}
   199  		mu.Lock()
   200  		mu.RLock()
   201  		panic("should not be reached")
   202  	}()
   203  	// Yield several times to allow the child goroutine to run.
   204  	for i := 0; i < yieldCount; i++ {
   205  		runtime.Gosched()
   206  	}
   207  	prof.WriteTo(os.Stdout, 2)
   208  }
   209  
   210  func RWMutexLock() {
   211  	prof := pprof.Lookup("goroutineleak")
   212  	go func() {
   213  		mu := &sync.RWMutex{}
   214  		mu.Lock()
   215  		mu.Lock()
   216  		panic("should not be reached")
   217  	}()
   218  	// Yield several times to allow the child goroutine to run.
   219  	for i := 0; i < yieldCount; i++ {
   220  		runtime.Gosched()
   221  	}
   222  	prof.WriteTo(os.Stdout, 2)
   223  }
   224  
   225  func Cond() {
   226  	prof := pprof.Lookup("goroutineleak")
   227  	go func() {
   228  		cond := sync.NewCond(&sync.Mutex{})
   229  		cond.L.Lock()
   230  		cond.Wait()
   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  func Mixed() {
   241  	prof := pprof.Lookup("goroutineleak")
   242  	go func() {
   243  		ch := make(chan int)
   244  		wg := sync.WaitGroup{}
   245  		wg.Add(1)
   246  		go func() {
   247  			ch <- 0
   248  			wg.Done()
   249  			panic("should not be reached")
   250  		}()
   251  		wg.Wait()
   252  		<-ch
   253  		panic("should not be reached")
   254  	}()
   255  	// Yield several times to allow the child goroutine to run.
   256  	for i := 0; i < yieldCount; i++ {
   257  		runtime.Gosched()
   258  	}
   259  	prof.WriteTo(os.Stdout, 2)
   260  }
   261  
   262  var ch = make(chan int)
   263  
   264  // No leak should be reported by this test
   265  func NoLeakGlobal() {
   266  	prof := pprof.Lookup("goroutineleak")
   267  	go func() {
   268  		<-ch
   269  	}()
   270  	// Yield several times to allow the child goroutine to run.
   271  	for i := 0; i < yieldCount; i++ {
   272  		runtime.Gosched()
   273  	}
   274  	prof.WriteTo(os.Stdout, 2)
   275  }
   276  

View as plain text