leakcheck.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. // Package leakcheck contains functions to check leaked goroutines.
  19. //
  20. // Call "defer leakcheck.Check(t)" at the beginning of tests.
  21. package leakcheck
  22. import (
  23. "runtime"
  24. "sort"
  25. "strings"
  26. "time"
  27. )
  28. var goroutinesToIgnore = []string{
  29. "testing.Main(",
  30. "testing.tRunner(",
  31. "testing.(*M).",
  32. "runtime.goexit",
  33. "created by runtime.gc",
  34. "created by runtime/trace.Start",
  35. "interestingGoroutines",
  36. "runtime.MHeap_Scavenger",
  37. "signal.signal_recv",
  38. "sigterm.handler",
  39. "runtime_mcall",
  40. "(*loggingT).flushDaemon",
  41. "goroutine in C code",
  42. // Ignore the http read/write goroutines. gce metadata.OnGCE() was leaking
  43. // these, root cause unknown.
  44. //
  45. // https://github.com/grpc/grpc-go/issues/5171
  46. // https://github.com/grpc/grpc-go/issues/5173
  47. "created by net/http.(*Transport).dialConn",
  48. }
  49. // RegisterIgnoreGoroutine appends s into the ignore goroutine list. The
  50. // goroutines whose stack trace contains s will not be identified as leaked
  51. // goroutines. Not thread-safe, only call this function in init().
  52. func RegisterIgnoreGoroutine(s string) {
  53. goroutinesToIgnore = append(goroutinesToIgnore, s)
  54. }
  55. func ignore(g string) bool {
  56. sl := strings.SplitN(g, "\n", 2)
  57. if len(sl) != 2 {
  58. return true
  59. }
  60. stack := strings.TrimSpace(sl[1])
  61. if strings.HasPrefix(stack, "testing.RunTests") {
  62. return true
  63. }
  64. if stack == "" {
  65. return true
  66. }
  67. for _, s := range goroutinesToIgnore {
  68. if strings.Contains(stack, s) {
  69. return true
  70. }
  71. }
  72. return false
  73. }
  74. // interestingGoroutines returns all goroutines we care about for the purpose of
  75. // leak checking. It excludes testing or runtime ones.
  76. func interestingGoroutines() (gs []string) {
  77. buf := make([]byte, 2<<20)
  78. buf = buf[:runtime.Stack(buf, true)]
  79. for _, g := range strings.Split(string(buf), "\n\n") {
  80. if !ignore(g) {
  81. gs = append(gs, g)
  82. }
  83. }
  84. sort.Strings(gs)
  85. return
  86. }
  87. // Errorfer is the interface that wraps the Errorf method. It's a subset of
  88. // testing.TB to make it easy to use Check.
  89. type Errorfer interface {
  90. Errorf(format string, args ...interface{})
  91. }
  92. func check(efer Errorfer, timeout time.Duration) {
  93. // Loop, waiting for goroutines to shut down.
  94. // Wait up to timeout, but finish as quickly as possible.
  95. deadline := time.Now().Add(timeout)
  96. var leaked []string
  97. for time.Now().Before(deadline) {
  98. if leaked = interestingGoroutines(); len(leaked) == 0 {
  99. return
  100. }
  101. time.Sleep(50 * time.Millisecond)
  102. }
  103. for _, g := range leaked {
  104. efer.Errorf("Leaked goroutine: %v", g)
  105. }
  106. }
  107. // Check looks at the currently-running goroutines and checks if there are any
  108. // interesting (created by gRPC) goroutines leaked. It waits up to 10 seconds
  109. // in the error cases.
  110. func Check(efer Errorfer) {
  111. check(efer, 10*time.Second)
  112. }