uber-go / goleak

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

Using goleak on Go benchmarks. #77

Closed bwplotka closed 1 year ago

bwplotka commented 2 years ago

It would be amazing to have goleak working on Benchmarks as well. However, I think some functions might needed to be filtered by default. Repro:

func BenchmarkGoroutines(b *testing.B) {
    defer goleak.VerifyNone(b)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        wg := sync.WaitGroup{}
        wg.Add(2)

        go func() {
            defer wg.Done()
        }()
        go func() {
            defer wg.Done()
        }()

        wg.Wait()
    }
}

Output:

BenchmarkGoroutines
    leaks.go:78: found unexpected goroutines:
        [Goroutine 1 in state chan receive, with testing.(*B).run1 on top of the stack:
        goroutine 1 [chan receive]:
        testing.(*B).run1(0xc00016a240)
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/benchmark.go:235 +0xb2
        testing.(*B).Run(0xc00016a480, {0x6f69de?, 0x0?}, 0x722460)
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/benchmark.go:676 +0x445
        testing.runBenchmarks.func1(0xc00016a480?)
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/benchmark.go:550 +0x6e
        testing.(*B).runN(0xc00016a480, 0x1)
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/benchmark.go:193 +0x102
        testing.runBenchmarks({0x6ffdf2, 0x28}, 0x945c20?, {0x909c80, 0x2, 0x40?})
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/benchmark.go:559 +0x3f2
        testing.(*M).Run(0xc000132f00)
            /home/bwplotka/.gvm/gos/go1.18.3/src/testing/testing.go:1726 +0x811
        main.main()
            _testmain.go:57 +0x1aa
        ]
--- FAIL: BenchmarkGoroutines
FAIL

Process finished with the exit code 1

Tested on both Go version 1.18.3 and 1.19

bwplotka commented 2 years ago

Found workaround:

func BenchmarkGoroutines(b *testing.B) {
    defer goleak.VerifyNone(
        b,
        goleak.IgnoreTopFunction("testing.(*B).run1"),
        goleak.IgnoreTopFunction("testing.(*B).doBench"),
    )

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        wg := sync.WaitGroup{}
        wg.Add(2)

        go func() {
            defer wg.Done()
        }()
        go func() {
            defer wg.Done()
        }()

        wg.Wait()
    }
}