mirror of
https://github.com/golang/go.git
synced 2026-06-27 03:11:23 +00:00
runtime: exclude main goroutine blocked on select{} from goroutine leak profile
The main goroutine is no longer included
in the goroutine leak profile if blocked
at select without cases.
The main goroutine is still treated as a
leak during the analysis to avoid degrading
analysis precision (see test), but has its status changed
from leaked back to waiting before the profile is written.
Based on feedback in https://github.com/golang/go/issues/74609#issuecomment-4297980817
Change-Id: I0fa2a93227006d99c2872c503a1eb67ad606a034
GitHub-Last-Rev: 84458d2e63
GitHub-Pull-Request: golang/go#78922
Reviewed-on: https://go-review.googlesource.com/c/go/+/770020
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Keith Randall <khr@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
19f8047c26
commit
d81ba6c35d
3 changed files with 62 additions and 0 deletions
|
|
@ -194,6 +194,12 @@ func TestGoroutineLeakProfile(t *testing.T) {
|
|||
`Mixed\.func1.1\(.* \[chan send\]`,
|
||||
),
|
||||
makeTest("NoLeakGlobal"),
|
||||
// If the main goroutine is waiting on select{}, it should not be reported as a leak.
|
||||
makeTest("SelectNoCasesMain",
|
||||
// However, any goroutine leaking on a channel that is reachable from the main
|
||||
// goroutine should be reported.
|
||||
`SelectNoCasesMain\.func1\(.* \[chan receive\]`,
|
||||
),
|
||||
}
|
||||
|
||||
// Stress tests are flaky and we do not strictly care about their output.
|
||||
|
|
|
|||
|
|
@ -1315,6 +1315,40 @@ func findGoroutineLeaks() bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not report the main goroutine if it is waiting on select{}.
|
||||
//
|
||||
// NOTE: We still treat the main goroutine as leaked during the analysis,
|
||||
// but revert its status to _Gwaiting after the analysis to not include
|
||||
// it in the goroutine leak profile.
|
||||
// This preserves the effectiveness of goroutine leak detection
|
||||
// if the main goroutine holds references to concurrency primitives causing
|
||||
// other leaks.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ```go
|
||||
// func main() {
|
||||
// ch := make(chan int)
|
||||
// go func() {
|
||||
// ...
|
||||
// <-ch // Leaks
|
||||
// }()
|
||||
//
|
||||
// select {}
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// The main goroutine is blocked by select{}, but holds a reference to "ch".
|
||||
// Not treating the main goroutine as leaked would cause the analysis to
|
||||
// miss the legitimate leak at the child goroutine.
|
||||
//
|
||||
// The main goroutine should always be allgs[0], but double check
|
||||
// in case that invariant changes in the future.
|
||||
if gp0 := allgs[0]; gp0.goid == 1 && gp0.waitreason == waitReasonSelectNoCases {
|
||||
casgstatus(gp0, _Gleaked, _Gwaiting)
|
||||
}
|
||||
|
||||
// Put the remaining roots as ready for marking and drain them.
|
||||
work.markrootJobs.Add(int32(work.nStackRoots - work.nMaybeRunnableStackRoots))
|
||||
work.nMaybeRunnableStackRoots = work.nStackRoots
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ func init() {
|
|||
register("NilRecv", NilRecv)
|
||||
register("NilSend", NilSend)
|
||||
register("SelectNoCases", SelectNoCases)
|
||||
register("SelectNoCasesMain", SelectNoCasesMain)
|
||||
register("ChanRecv", ChanRecv)
|
||||
register("ChanSend", ChanSend)
|
||||
register("Select", Select)
|
||||
|
|
@ -90,6 +91,27 @@ func SelectNoCases() {
|
|||
prof.WriteTo(os.Stdout, 2)
|
||||
}
|
||||
|
||||
func SelectNoCasesMain() {
|
||||
prof := pprof.Lookup("goroutineleak")
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
// Should be reported as a leak.
|
||||
<-ch
|
||||
}()
|
||||
go func() {
|
||||
for i := 0; i < yieldCount; i++ {
|
||||
runtime.Gosched()
|
||||
}
|
||||
// Write a goroutine leak profile from
|
||||
// the child goroutine.
|
||||
prof.WriteTo(os.Stdout, 2)
|
||||
// Forcefully exit the program.
|
||||
os.Exit(0)
|
||||
}()
|
||||
// Should not be reported as a leak.
|
||||
select {}
|
||||
}
|
||||
|
||||
func ChanSend() {
|
||||
prof := pprof.Lookup("goroutineleak")
|
||||
go func() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue