...

Source file src/cloud.google.com/go/internal/leakcheck/leakcheck.go

Documentation: cloud.google.com/go/internal/leakcheck

     1  // Copyright 2018 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package leakcheck contains functions to check leaked goroutines.
    16  //
    17  // Call "defer leakcheck.Check(t)" at the beginning of tests.
    18  //
    19  // This is very closely based on grpc-go's leakcheck.
    20  package leakcheck
    21  
    22  import (
    23  	"runtime"
    24  	"sort"
    25  	"strings"
    26  	"time"
    27  )
    28  
    29  var goroutinesToIgnore = []string{
    30  	"net/http/transport.go",
    31  	"net/http/h2_bundle.go",
    32  	"src/go.opencensus.io/stats/view/worker.go",
    33  	"testing.Main(",
    34  	"testing.tRunner(",
    35  	"testing.(*M).",
    36  	"runtime.goexit",
    37  	"created by runtime.gc",
    38  	"created by runtime/trace.Start",
    39  	"interestingGoroutines",
    40  	"runtime.MHeap_Scavenger",
    41  	"signal.signal_recv",
    42  	"sigterm.handler",
    43  	"runtime_mcall",
    44  	"(*loggingT).flushDaemon",
    45  	"goroutine in C code",
    46  }
    47  
    48  // RegisterIgnoreGoroutine appends s into the ignore goroutine list. The
    49  // goroutines whose stack trace contains s will not be identified as leaked
    50  // goroutines. Not thread-safe, only call this function in init().
    51  func RegisterIgnoreGoroutine(s string) {
    52  	goroutinesToIgnore = append(goroutinesToIgnore, s)
    53  }
    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  
    65  	if stack == "" {
    66  		return true
    67  	}
    68  
    69  	for _, s := range goroutinesToIgnore {
    70  		if strings.Contains(stack, s) {
    71  			return true
    72  		}
    73  	}
    74  
    75  	return false
    76  }
    77  
    78  // interestingGoroutines returns all goroutines we care about for the purpose of
    79  // leak checking. It excludes testing or runtime ones.
    80  func interestingGoroutines() (gs []string) {
    81  	buf := make([]byte, 2<<20)
    82  	buf = buf[:runtime.Stack(buf, true)]
    83  	for _, g := range strings.Split(string(buf), "\n\n") {
    84  		if !ignore(g) {
    85  			gs = append(gs, g)
    86  		}
    87  	}
    88  	sort.Strings(gs)
    89  	return
    90  }
    91  
    92  // Errorfer is the interface that wraps the Errorf method. It's a subset of
    93  // testing.TB to make it easy to use Check.
    94  type Errorfer interface {
    95  	Errorf(format string, args ...interface{})
    96  }
    97  
    98  func check(efer Errorfer, timeout time.Duration) {
    99  	// Loop, waiting for goroutines to shut down.
   100  	// Wait up to timeout, but finish as quickly as possible.
   101  	deadline := time.Now().Add(timeout)
   102  	var leaked []string
   103  	for time.Now().Before(deadline) {
   104  		if leaked = interestingGoroutines(); len(leaked) == 0 {
   105  			return
   106  		}
   107  		time.Sleep(50 * time.Millisecond)
   108  	}
   109  	for _, g := range leaked {
   110  		efer.Errorf("Leaked goroutine: %v", g)
   111  	}
   112  }
   113  
   114  // Check looks at the currently-running goroutines and checks if there are any
   115  // interestring (created by gRPC) goroutines leaked. It waits up to 10 seconds
   116  // in the error cases.
   117  func Check(efer Errorfer) {
   118  	check(efer, 10*time.Second)
   119  }
   120  

View as plain text