inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/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 "inet.af/netstack/log" 21 refs_vfs1 "inet.af/netstack/refs" 22 "inet.af/netstack/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 }