uber-go / goleak

Goroutine leak detector
MIT License
4.5k stars 148 forks source link

Support tracebackancestors? #70

Open MichaelSnowden opened 2 years ago

MichaelSnowden commented 2 years ago

I'd like to verify that there are no goroutines leaked in my test, but only for goroutines where my test itself is an ancestor goroutine. AFAIK, this is not what goleak currently does. It looks at all goroutines, which means it doesn't work in parallel, and it doesn't work for many globally initialized goroutines like those created in the init function of some imported package.

There is a tracebackancestors option that can be passed to the GODEBUG environment variable (documented here, withe the original accepted proposal here. This option adds ancestry metadata to goroutines, which should make the functionality I'm looking for possible.

Are there any plans to add this functionality to goleak?

prashantv commented 2 years ago

I'm all for investing in a solution that can work with tests being run in parallel!

To understand how tracebackancestors works, I created a small example: https://go.dev/play/p/XBvJ9nS4Zgy

In this case, all goroutines are rooted in the "main" goroutine, but there's 2 paths:

Imagine main is the test goroutine. In this case, we want to try and trace the running baz goroutines back to main. If we run this with ancestry enabled, we get the following stacktrace:

goroutine 1 [running]:
main.main()
        /home/prashant/tmp/goancestry.go:18 +0x5c

goroutine 7 [select (no cases)]:
main.baz()
        /home/prashant/tmp/goancestry.go:31 +0x17
created by main.bar
        /home/prashant/tmp/goancestry.go:27 +0x25
[originating from goroutine 1]:
main.bar(...)
        /home/prashant/tmp/goancestry.go:28 +0x25
main.main(...)
        /home/prashant/tmp/goancestry.go:15 +0x25

goroutine 8 [select (no cases)]:
main.baz()
        /home/prashant/tmp/goancestry.go:31 +0x17
created by main.bar
        /home/prashant/tmp/goancestry.go:27 +0x25
[originating from goroutine 6]:
main.bar(...)
        /home/prashant/tmp/goancestry.go:28 +0x25
created by main.foo
        /home/prashant/tmp/goancestry.go:23 +0x25

We can trace the main --> bar --> baz back, but we can't trace main --> foo --> bar --> baz back, since the goroutine running foo has exited. So we see "originating from goroutine 6", but we don't know what goroutine 6 is, or where it originated from.

It looks like even with support for traceback ancestors, it wouldn't be possible to track leaks back to tests reliably -- it would have false negatives, missing leaks if the test starts G1, and G1 starts G2 which leaks, but G1 is no longer running.

I wasn't aware of this GODEBUG option previously, so may be missing something.