OSDN Git Service

new repo
[bytom/vapor.git] / vendor / google.golang.org / grpc / test / leakcheck / leakcheck.go
1 /*
2  *
3  * Copyright 2017 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 // Package leakcheck contains functions to check leaked goroutines.
20 //
21 // Call "defer leakcheck.Check(t)" at the beginning of tests.
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 // RegisterIgnoreGoroutine appends s into the ignore goroutine list. The
48 // goroutines whose stack trace contains s will not be identified as leaked
49 // goroutines. Not thread-safe, only call this function in init().
50 func RegisterIgnoreGoroutine(s string) {
51         goroutinesToIgnore = append(goroutinesToIgnore, s)
52 }
53
54 func ignore(g string) bool {
55         sl := strings.SplitN(g, "\n", 2)
56         if len(sl) != 2 {
57                 return true
58         }
59         stack := strings.TrimSpace(sl[1])
60         if strings.HasPrefix(stack, "testing.RunTests") {
61                 return true
62         }
63
64         if stack == "" {
65                 return true
66         }
67
68         for _, s := range goroutinesToIgnore {
69                 if strings.Contains(stack, s) {
70                         return true
71                 }
72         }
73
74         return false
75 }
76
77 // interestingGoroutines returns all goroutines we care about for the purpose of
78 // leak checking. It excludes testing or runtime ones.
79 func interestingGoroutines() (gs []string) {
80         buf := make([]byte, 2<<20)
81         buf = buf[:runtime.Stack(buf, true)]
82         for _, g := range strings.Split(string(buf), "\n\n") {
83                 if !ignore(g) {
84                         gs = append(gs, g)
85                 }
86         }
87         sort.Strings(gs)
88         return
89 }
90
91 // Errorfer is the interface that wraps the Errorf method. It's a subset of
92 // testing.TB to make it easy to use Check.
93 type Errorfer interface {
94         Errorf(format string, args ...interface{})
95 }
96
97 func check(efer Errorfer, timeout time.Duration) {
98         // Loop, waiting for goroutines to shut down.
99         // Wait up to timeout, but finish as quickly as possible.
100         deadline := time.Now().Add(timeout)
101         var leaked []string
102         for time.Now().Before(deadline) {
103                 if leaked = interestingGoroutines(); len(leaked) == 0 {
104                         return
105                 }
106                 time.Sleep(50 * time.Millisecond)
107         }
108         for _, g := range leaked {
109                 efer.Errorf("Leaked goroutine: %v", g)
110         }
111 }
112
113 // Check looks at the currently-running goroutines and checks if there are any
114 // interestring (created by gRPC) goroutines leaked. It waits up to 10 seconds
115 // in the error cases.
116 func Check(efer Errorfer) {
117         check(efer, 10*time.Second)
118 }