github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/util/testleak/leaktest.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Copyright 2016 PingCAP, Inc. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package testleak 19 20 import ( 21 "runtime" 22 "sort" 23 "strings" 24 "time" 25 26 "github.com/insionng/yougam/libraries/pingcap/check" 27 ) 28 29 func interestingGoroutines() (gs []string) { 30 buf := make([]byte, 2<<20) 31 buf = buf[:runtime.Stack(buf, true)] 32 for _, g := range strings.Split(string(buf), "\n\n") { 33 sl := strings.SplitN(g, "\n", 2) 34 if len(sl) != 2 { 35 continue 36 } 37 stack := strings.TrimSpace(sl[1]) 38 if stack == "" || 39 strings.Contains(stack, "created by yougam/libraries/pingcap/tidb.init") || 40 strings.Contains(stack, "testing.RunTests") || 41 strings.Contains(stack, "check.(*resultTracker).start") || 42 strings.Contains(stack, "localstore.(*dbStore).scheduler") || 43 strings.Contains(stack, "ddl.(*ddl).start") || 44 strings.Contains(stack, "domain.NewDomain") || 45 strings.Contains(stack, "testing.Main(") || 46 strings.Contains(stack, "runtime.goexit") || 47 strings.Contains(stack, "created by runtime.gc") || 48 strings.Contains(stack, "interestingGoroutines") || 49 strings.Contains(stack, "runtime.MHeap_Scavenger") { 50 continue 51 } 52 gs = append(gs, stack) 53 } 54 sort.Strings(gs) 55 return 56 } 57 58 var beforeTestGorountines = map[string]bool{} 59 60 // BeforeTest gets the current goroutines. 61 // It's used for check.Suite.SetUpSuite() function. 62 // Now it's only used in the tidb_test.go. 63 func BeforeTest() { 64 for _, g := range interestingGoroutines() { 65 beforeTestGorountines[g] = true 66 } 67 } 68 69 // AfterTest gets the current goroutines and runs the returned function to 70 // get the goroutines at that time to contrast wheter any goroutines leaked. 71 // Usage: defer testleak.AfterTest(c)() 72 // It can call with BeforeTest() at the beginning of check.Suite.TearDownSuite() or 73 // call alone at the beginning of each test. 74 func AfterTest(c *check.C) func() { 75 if len(beforeTestGorountines) == 0 { 76 for _, g := range interestingGoroutines() { 77 beforeTestGorountines[g] = true 78 } 79 } 80 81 return func() { 82 defer func() { 83 beforeTestGorountines = map[string]bool{} 84 }() 85 86 var leaked []string 87 for i := 0; i < 50; i++ { 88 for _, g := range interestingGoroutines() { 89 if !beforeTestGorountines[g] { 90 leaked = append(leaked, g) 91 } 92 } 93 // Bad stuff found, but goroutines might just still be 94 // shutting down, so give it some time. 95 if len(leaked) != 0 { 96 leaked = leaked[:0] 97 time.Sleep(50 * time.Millisecond) 98 continue 99 } 100 101 return 102 } 103 for _, g := range leaked { 104 c.Errorf("Test appears to have leaked: %v", g) 105 } 106 } 107 }