Skip to content

Instantly share code, notes, and snippets.

@komuw
Last active February 19, 2020 09:27
Show Gist options
  • Save komuw/0252dd7cd660edce4b483829dddfebc3 to your computer and use it in GitHub Desktop.
Save komuw/0252dd7cd660edce4b483829dddfebc3 to your computer and use it in GitHub Desktop.
A Goroutines leak detector
package main
// or just use:
// 1. https://github.com/uber-go/goleak
// 2. github.com/cockroachdb/cockroach/pkg/util/leaktest
import (
// "bytes"
"fmt"
"os"
"runtime"
"sort"
"strings"
"time"
)
func doWork() {
for {
<-time.After(1 * time.Second)
}
x := 1 + 1
fmt.Println("x::", x)
}
func main() {
for i := 0; i < 10; i++ {
go doWork()
}
fmt.Println("work done")
if goroutineLeaked() {
fmt.Println("LEAAKED GOROUTINES")
} else {
fmt.Println("no leak")
}
}
// This code is taken from: https://github.com/golang/go/blob/d82e51a11973714708ddc7f9f055ae8ea3d509f1/src/net/http/main_test.go#L30-L141
func goroutineLeaked() bool {
var stackCount map[string]int
for i := 0; i < 5; i++ {
n := 0
stackCount = make(map[string]int)
gs := interestingGoroutines()
for _, g := range gs {
stackCount[g]++
n++
}
if n == 0 {
return false
}
// Wait for goroutines to schedule and die off:
time.Sleep(100 * time.Millisecond)
}
fmt.Fprintf(os.Stderr, "\n\t Too many goroutines running.\n")
for stack, count := range stackCount {
fmt.Fprintf(os.Stderr, "\n\t %d instances of: %s \n\n", count, stack)
}
return true
}
func interestingGoroutines() (gs []string) {
buf := make([]byte, 2<<20)
buf = buf[:runtime.Stack(buf, true)]
for _, g := range strings.Split(string(buf), "\n\n") {
sl := strings.SplitN(g, "\n", 2)
if len(sl) != 2 {
continue
}
stack := strings.TrimSpace(sl[1])
if stack == "" ||
strings.Contains(stack, "main.interestingGoroutines") ||
strings.Contains(stack, "os/signal.signal_recv") ||
// We may need to remove this ones since they only apply to the net/http package
// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
strings.Contains(stack, "runtime.goexit") ||
strings.Contains(stack, "created by runtime.gc") ||
strings.Contains(stack, "net/http_test.interestingGoroutines") ||
strings.Contains(stack, "runtime.MHeap_Scavenger") {
continue
}
gs = append(gs, stack)
}
sort.Strings(gs)
return
}
go run main.go
work done
Too many goroutines running.
10 instances of: main.doWork()
/Users/komuw/mystuff/naz/main.go:15 +0x41
created by main.main
/Users/komuw/mystuff/naz/main.go:23 +0x42
LEAAKED GOROUTINES
@komuw
Copy link
Author

komuw commented Oct 24, 2018

We should actually reduce the if to;

if stack == "" || strings.Contains(stack, "main.interestingGoroutines") {
			continue
		}

we should add more things to that OR(||) as we need

@komuw
Copy link
Author

komuw commented Sep 2, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment