github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/refsvfs2/refs_map.go (about)

     1  // Copyright 2020 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 refsvfs2
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/log"
    21  	refs_vfs1 "github.com/SagerNet/gvisor/pkg/refs"
    22  	"github.com/SagerNet/gvisor/pkg/sync"
    23  )
    24  
    25  var (
    26  	// liveObjects is a global map of reference-counted objects. Objects are
    27  	// inserted when leak check is enabled, and they are removed when they are
    28  	// destroyed. It is protected by liveObjectsMu.
    29  	liveObjects   map[CheckedObject]struct{}
    30  	liveObjectsMu sync.Mutex
    31  )
    32  
    33  // CheckedObject represents a reference-counted object with an informative
    34  // leak detection message.
    35  type CheckedObject interface {
    36  	// RefType is the type of the reference-counted object.
    37  	RefType() string
    38  
    39  	// LeakMessage supplies a warning to be printed upon leak detection.
    40  	LeakMessage() string
    41  
    42  	// LogRefs indicates whether reference-related events should be logged.
    43  	LogRefs() bool
    44  }
    45  
    46  func init() {
    47  	liveObjects = make(map[CheckedObject]struct{})
    48  }
    49  
    50  // leakCheckEnabled returns whether leak checking is enabled. The following
    51  // functions should only be called if it returns true.
    52  func leakCheckEnabled() bool {
    53  	return refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking
    54  }
    55  
    56  // Register adds obj to the live object map.
    57  func Register(obj CheckedObject) {
    58  	if leakCheckEnabled() {
    59  		liveObjectsMu.Lock()
    60  		if _, ok := liveObjects[obj]; ok {
    61  			panic(fmt.Sprintf("Unexpected entry in leak checking map: reference %p already added", obj))
    62  		}
    63  		liveObjects[obj] = struct{}{}
    64  		liveObjectsMu.Unlock()
    65  		if leakCheckEnabled() && obj.LogRefs() {
    66  			logEvent(obj, "registered")
    67  		}
    68  	}
    69  }
    70  
    71  // Unregister removes obj from the live object map.
    72  func Unregister(obj CheckedObject) {
    73  	if leakCheckEnabled() {
    74  		liveObjectsMu.Lock()
    75  		defer liveObjectsMu.Unlock()
    76  		if _, ok := liveObjects[obj]; !ok {
    77  			panic(fmt.Sprintf("Expected to find entry in leak checking map for reference %p", obj))
    78  		}
    79  		delete(liveObjects, obj)
    80  		if leakCheckEnabled() && obj.LogRefs() {
    81  			logEvent(obj, "unregistered")
    82  		}
    83  	}
    84  }
    85  
    86  // LogIncRef logs a reference increment.
    87  func LogIncRef(obj CheckedObject, refs int64) {
    88  	if leakCheckEnabled() && obj.LogRefs() {
    89  		logEvent(obj, fmt.Sprintf("IncRef to %d", refs))
    90  	}
    91  }
    92  
    93  // LogTryIncRef logs a successful TryIncRef call.
    94  func LogTryIncRef(obj CheckedObject, refs int64) {
    95  	if leakCheckEnabled() && obj.LogRefs() {
    96  		logEvent(obj, fmt.Sprintf("TryIncRef to %d", refs))
    97  	}
    98  }
    99  
   100  // LogDecRef logs a reference decrement.
   101  func LogDecRef(obj CheckedObject, refs int64) {
   102  	if leakCheckEnabled() && obj.LogRefs() {
   103  		logEvent(obj, fmt.Sprintf("DecRef to %d", refs))
   104  	}
   105  }
   106  
   107  // logEvent logs a message for the given reference-counted object.
   108  //
   109  // obj.LogRefs() should be checked before calling logEvent, in order to avoid
   110  // calling any text processing needed to evaluate msg.
   111  func logEvent(obj CheckedObject, msg string) {
   112  	log.Infof("[%s %p] %s:\n%s", obj.RefType(), obj, msg, refs_vfs1.FormatStack(refs_vfs1.RecordStack()))
   113  }
   114  
   115  // checkOnce makes sure that leak checking is only done once. DoLeakCheck is
   116  // called from multiple places (which may overlap) to cover different sandbox
   117  // exit scenarios.
   118  var checkOnce sync.Once
   119  
   120  // DoLeakCheck iterates through the live object map and logs a message for each
   121  // object. It is called once no reference-counted objects should be reachable
   122  // anymore, at which point anything left in the map is considered a leak.
   123  func DoLeakCheck() {
   124  	if leakCheckEnabled() {
   125  		checkOnce.Do(func() {
   126  			liveObjectsMu.Lock()
   127  			defer liveObjectsMu.Unlock()
   128  			leaked := len(liveObjects)
   129  			if leaked > 0 {
   130  				msg := fmt.Sprintf("Leak checking detected %d leaked objects:\n", leaked)
   131  				for obj := range liveObjects {
   132  					msg += obj.LeakMessage() + "\n"
   133  				}
   134  				log.Warningf(msg)
   135  			}
   136  		})
   137  	}
   138  }