1package dns 2 3import ( 4 "fmt" 5 "os" 6 "runtime" 7 "sort" 8 "strings" 9 "testing" 10 "time" 11) 12 13// copied from net/http/main_test.go 14 15func interestingGoroutines() (gs []string) { 16 buf := make([]byte, 2<<20) 17 buf = buf[:runtime.Stack(buf, true)] 18 for _, g := range strings.Split(string(buf), "\n\n") { 19 sl := strings.SplitN(g, "\n", 2) 20 if len(sl) != 2 { 21 continue 22 } 23 stack := strings.TrimSpace(sl[1]) 24 if stack == "" || 25 strings.Contains(stack, "testing.(*M).before.func1") || 26 strings.Contains(stack, "os/signal.signal_recv") || 27 strings.Contains(stack, "created by net.startServer") || 28 strings.Contains(stack, "created by testing.RunTests") || 29 strings.Contains(stack, "closeWriteAndWait") || 30 strings.Contains(stack, "testing.Main(") || 31 strings.Contains(stack, "testing.(*T).Run(") || 32 strings.Contains(stack, "created by net/http.(*http2Transport).newClientConn") || 33 // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) 34 strings.Contains(stack, "runtime.goexit") || 35 strings.Contains(stack, "created by runtime.gc") || 36 strings.Contains(stack, "dns.interestingGoroutines") || 37 strings.Contains(stack, "runtime.MHeap_Scavenger") { 38 continue 39 } 40 gs = append(gs, stack) 41 } 42 sort.Strings(gs) 43 return 44} 45 46func goroutineLeaked() error { 47 if testing.Short() { 48 // Don't worry about goroutine leaks in -short mode or in 49 // benchmark mode. Too distracting when there are false positives. 50 return nil 51 } 52 53 var stackCount map[string]int 54 for i := 0; i < 5; i++ { 55 n := 0 56 stackCount = make(map[string]int) 57 gs := interestingGoroutines() 58 for _, g := range gs { 59 stackCount[g]++ 60 n++ 61 } 62 if n == 0 { 63 return nil 64 } 65 // Wait for goroutines to schedule and die off: 66 time.Sleep(100 * time.Millisecond) 67 } 68 for stack, count := range stackCount { 69 fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) 70 } 71 return fmt.Errorf("too many goroutines running after dns test(s)") 72} 73