diff options
author | Ian Gudger <igudger@google.com> | 2019-06-28 20:06:33 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2019-06-28 20:07:52 -0700 |
commit | 45566fa4e4bc55e870ae1d7f80778be8432bdef5 (patch) | |
tree | 9b9064ef22a6f34ade4714cc90ad0573440c3e1c /pkg/refs | |
parent | 7dae043fecc109d7a0ae1d5d6274e48b1bf04d13 (diff) |
Add finalizer on AtomicRefCount to check for leaks.
PiperOrigin-RevId: 255711454
Diffstat (limited to 'pkg/refs')
-rw-r--r-- | pkg/refs/BUILD | 3 | ||||
-rw-r--r-- | pkg/refs/refcounter.go | 69 |
2 files changed, 72 insertions, 0 deletions
diff --git a/pkg/refs/BUILD b/pkg/refs/BUILD index 0652fbbe6..9c08452fc 100644 --- a/pkg/refs/BUILD +++ b/pkg/refs/BUILD @@ -24,6 +24,9 @@ go_library( ], importpath = "gvisor.dev/gvisor/pkg/refs", visibility = ["//:sandbox"], + deps = [ + "//pkg/log", + ], ) go_test( diff --git a/pkg/refs/refcounter.go b/pkg/refs/refcounter.go index 20f515391..6d6c3a782 100644 --- a/pkg/refs/refcounter.go +++ b/pkg/refs/refcounter.go @@ -18,8 +18,11 @@ package refs import ( "reflect" + "runtime" "sync" "sync/atomic" + + "gvisor.dev/gvisor/pkg/log" ) // RefCounter is the interface to be implemented by objects that are reference @@ -189,6 +192,11 @@ type AtomicRefCount struct { // how these fields are used. refCount int64 + // name is the name of the type which owns this ref count. + // + // name is immutable after EnableLeakCheck is called. + name string + // mu protects the list below. mu sync.Mutex `state:"nosave"` @@ -196,6 +204,67 @@ type AtomicRefCount struct { weakRefs weakRefList `state:"nosave"` } +// LeakMode configures the leak checker. +type LeakMode uint32 + +const ( + // uninitializedLeakChecking indicates that the leak checker has not yet been initialized. + uninitializedLeakChecking LeakMode = iota + + // NoLeakChecking indicates that no effort should be made to check for + // leaks. + NoLeakChecking + + // LeaksLogWarning indicates that a warning should be logged when leaks + // are found. + LeaksLogWarning +) + +// leakMode stores the current mode for the reference leak checker. +// +// Values must be one of the LeakMode values. +// +// leakMode must be accessed atomically. +var leakMode uint32 + +// SetLeakMode configures the reference leak checker. +func SetLeakMode(mode LeakMode) { + atomic.StoreUint32(&leakMode, uint32(mode)) +} + +func (r *AtomicRefCount) finalize() { + var note string + switch LeakMode(atomic.LoadUint32(&leakMode)) { + case NoLeakChecking: + return + case uninitializedLeakChecking: + note = "(Leak checker uninitialized): " + } + if n := r.ReadRefs(); n != 0 { + log.Warningf("%sAtomicRefCount %p owned by %q garbage collected with ref count of %d (want 0)", note, r, r.name, n) + } +} + +// EnableLeakCheck checks for reference leaks when the AtomicRefCount gets +// garbage collected. +// +// This function adds a finalizer to the AtomicRefCount, so the AtomicRefCount +// must be at the beginning of its parent. +// +// name is a friendly name that will be listed as the owner of the +// AtomicRefCount in logs. It should be the name of the parent type, including +// package. +func (r *AtomicRefCount) EnableLeakCheck(name string) { + if name == "" { + panic("invalid name") + } + if LeakMode(atomic.LoadUint32(&leakMode)) == NoLeakChecking { + return + } + r.name = name + runtime.SetFinalizer(r, (*AtomicRefCount).finalize) +} + // ReadRefs returns the current number of references. The returned count is // inherently racy and is unsafe to use without external synchronization. func (r *AtomicRefCount) ReadRefs() int64 { |