...
1
18
19
20
21
22 package leakcheck
23
24 import (
25 "runtime"
26 "sort"
27 "strings"
28 "time"
29 )
30
31 var goroutinesToIgnore = []string{
32 "testing.Main(",
33 "testing.tRunner(",
34 "testing.(*M).",
35 "runtime.goexit",
36 "created by runtime.gc",
37 "created by runtime/trace.Start",
38 "interestingGoroutines",
39 "runtime.MHeap_Scavenger",
40 "signal.signal_recv",
41 "sigterm.handler",
42 "runtime_mcall",
43 "(*loggingT).flushDaemon",
44 "goroutine in C code",
45
46
47
48
49
50 "created by net/http.(*Transport).dialConn",
51 }
52
53
54
55
56 func RegisterIgnoreGoroutine(s string) {
57 goroutinesToIgnore = append(goroutinesToIgnore, s)
58 }
59
60 func ignore(g string) bool {
61 sl := strings.SplitN(g, "\n", 2)
62 if len(sl) != 2 {
63 return true
64 }
65 stack := strings.TrimSpace(sl[1])
66 if strings.HasPrefix(stack, "testing.RunTests") {
67 return true
68 }
69
70 if stack == "" {
71 return true
72 }
73
74 for _, s := range goroutinesToIgnore {
75 if strings.Contains(stack, s) {
76 return true
77 }
78 }
79
80 return false
81 }
82
83
84
85 func interestingGoroutines() (gs []string) {
86 buf := make([]byte, 2<<20)
87 buf = buf[:runtime.Stack(buf, true)]
88 for _, g := range strings.Split(string(buf), "\n\n") {
89 if !ignore(g) {
90 gs = append(gs, g)
91 }
92 }
93 sort.Strings(gs)
94 return
95 }
96
97
98
99 type Errorfer interface {
100 Errorf(format string, args ...interface{})
101 }
102
103 func check(efer Errorfer, timeout time.Duration) {
104
105
106 deadline := time.Now().Add(timeout)
107 var leaked []string
108 for time.Now().Before(deadline) {
109 if leaked = interestingGoroutines(); len(leaked) == 0 {
110 return
111 }
112 time.Sleep(50 * time.Millisecond)
113 }
114 for _, g := range leaked {
115 efer.Errorf("Leaked goroutine: %v", g)
116 }
117 }
118
119
120
121
122 func Check(efer Errorfer) {
123 check(efer, 10*time.Second)
124 }
125
View as plain text