refcounter.go raw

   1  // Copyright 2018 The gVisor Authors.
   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 refs defines an interface for reference counted objects.
  16  package refs
  17  
  18  import (
  19  	"bytes"
  20  	"fmt"
  21  	"runtime"
  22  
  23  	"gvisor.dev/gvisor/pkg/atomicbitops"
  24  	"gvisor.dev/gvisor/pkg/context"
  25  	"gvisor.dev/gvisor/pkg/sync"
  26  )
  27  
  28  // RefCounter is the interface to be implemented by objects that are reference
  29  // counted.
  30  type RefCounter interface {
  31  	// IncRef increments the reference counter on the object.
  32  	IncRef()
  33  
  34  	// DecRef decrements the object's reference count. Users of refs_template.Refs
  35  	// may specify a destructor to be called once the reference count reaches zero.
  36  	DecRef(ctx context.Context)
  37  }
  38  
  39  // TryRefCounter is like RefCounter but allow the ref increment to be tried.
  40  type TryRefCounter interface {
  41  	RefCounter
  42  
  43  	// TryIncRef attempts to increment the reference count, but may fail if all
  44  	// references have already been dropped, in which case it returns false. If
  45  	// true is returned, then a valid reference is now held on the object.
  46  	TryIncRef() bool
  47  }
  48  
  49  // LeakMode configures the leak checker.
  50  type LeakMode uint32
  51  
  52  const (
  53  	// NoLeakChecking indicates that no effort should be made to check for
  54  	// leaks.
  55  	NoLeakChecking LeakMode = iota
  56  
  57  	// LeaksLogWarning indicates that a warning should be logged when leaks
  58  	// are found.
  59  	LeaksLogWarning
  60  
  61  	// LeaksPanic indidcates that a panic should be issued when leaks are found.
  62  	LeaksPanic
  63  )
  64  
  65  // Set implements flag.Value.
  66  func (l *LeakMode) Set(v string) error {
  67  	switch v {
  68  	case "disabled":
  69  		*l = NoLeakChecking
  70  	case "log-names":
  71  		*l = LeaksLogWarning
  72  	case "panic":
  73  		*l = LeaksPanic
  74  	default:
  75  		return fmt.Errorf("invalid ref leak mode %q", v)
  76  	}
  77  	return nil
  78  }
  79  
  80  // Get implements flag.Value.
  81  func (l *LeakMode) Get() any {
  82  	return *l
  83  }
  84  
  85  // String implements flag.Value.
  86  func (l LeakMode) String() string {
  87  	switch l {
  88  	case NoLeakChecking:
  89  		return "disabled"
  90  	case LeaksLogWarning:
  91  		return "log-names"
  92  	case LeaksPanic:
  93  		return "panic"
  94  	default:
  95  		panic(fmt.Sprintf("invalid ref leak mode %d", l))
  96  	}
  97  }
  98  
  99  // leakMode stores the current mode for the reference leak checker.
 100  //
 101  // Values must be one of the LeakMode values.
 102  //
 103  // leakMode must be accessed atomically.
 104  var leakMode atomicbitops.Uint32
 105  
 106  // SetLeakMode configures the reference leak checker.
 107  func SetLeakMode(mode LeakMode) {
 108  	leakMode.Store(uint32(mode))
 109  }
 110  
 111  // GetLeakMode returns the current leak mode.
 112  func GetLeakMode() LeakMode {
 113  	return LeakMode(leakMode.Load())
 114  }
 115  
 116  const maxStackFrames = 40
 117  
 118  type fileLine struct {
 119  	file string
 120  	line int
 121  }
 122  
 123  // A stackKey is a representation of a stack frame for use as a map key.
 124  //
 125  // The fileLine type is used as PC values seem to vary across collections, even
 126  // for the same call stack.
 127  type stackKey [maxStackFrames]fileLine
 128  
 129  var stackCache = struct {
 130  	sync.Mutex
 131  	entries map[stackKey][]uintptr
 132  }{entries: map[stackKey][]uintptr{}}
 133  
 134  func makeStackKey(pcs []uintptr) stackKey {
 135  	frames := runtime.CallersFrames(pcs)
 136  	var key stackKey
 137  	keySlice := key[:0]
 138  	for {
 139  		frame, more := frames.Next()
 140  		keySlice = append(keySlice, fileLine{frame.File, frame.Line})
 141  
 142  		if !more || len(keySlice) == len(key) {
 143  			break
 144  		}
 145  	}
 146  	return key
 147  }
 148  
 149  // RecordStack constructs and returns the PCs on the current stack.
 150  func RecordStack() []uintptr {
 151  	pcs := make([]uintptr, maxStackFrames)
 152  	n := runtime.Callers(1, pcs)
 153  	if n == 0 {
 154  		// No pcs available. Stop now.
 155  		//
 156  		// This can happen if the first argument to runtime.Callers
 157  		// is large.
 158  		return nil
 159  	}
 160  	pcs = pcs[:n]
 161  	key := makeStackKey(pcs)
 162  	stackCache.Lock()
 163  	v, ok := stackCache.entries[key]
 164  	if !ok {
 165  		// Reallocate to prevent pcs from escaping.
 166  		v = append([]uintptr(nil), pcs...)
 167  		stackCache.entries[key] = v
 168  	}
 169  	stackCache.Unlock()
 170  	return v
 171  }
 172  
 173  // FormatStack converts the given stack into a readable format.
 174  func FormatStack(pcs []uintptr) string {
 175  	frames := runtime.CallersFrames(pcs)
 176  	var trace bytes.Buffer
 177  	for {
 178  		frame, more := frames.Next()
 179  		fmt.Fprintf(&trace, "%s:%d: %s\n", frame.File, frame.Line, frame.Function)
 180  
 181  		if !more {
 182  			break
 183  		}
 184  	}
 185  	return trace.String()
 186  }
 187  
 188  // OnExit is called on sandbox exit. It runs GC to enqueue refcount finalizers,
 189  // which check for reference leaks. There is no way to guarantee that every
 190  // finalizer will run before exiting, but this at least ensures that they will
 191  // be discovered/enqueued by GC.
 192  func OnExit() {
 193  	if LeakMode(leakMode.Load()) != NoLeakChecking {
 194  		runtime.GC()
 195  	}
 196  }
 197