1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dalvik.system; 18 19 import dalvik.annotation.compat.UnsupportedAppUsage; 20 21 /** 22 * CloseGuard is a mechanism for flagging implicit finalizer cleanup of 23 * resources that should have been cleaned up by explicit close 24 * methods (aka "explicit termination methods" in Effective Java). 25 * <p> 26 * A simple example: <pre> {@code 27 * class Foo { 28 * 29 * {@literal @}ReachabilitySensitive 30 * private final CloseGuard guard = CloseGuard.get(); 31 * 32 * ... 33 * 34 * public Foo() { 35 * ...; 36 * guard.open("cleanup"); 37 * } 38 * 39 * public void cleanup() { 40 * guard.close(); 41 * ...; 42 * } 43 * 44 * protected void finalize() throws Throwable { 45 * try { 46 * // Note that guard could be null if the constructor threw. 47 * if (guard != null) { 48 * guard.warnIfOpen(); 49 * } 50 * cleanup(); 51 * } finally { 52 * super.finalize(); 53 * } 54 * } 55 * } 56 * }</pre> 57 * 58 * In usage where the resource to be explicitly cleaned up is 59 * allocated after object construction, CloseGuard protection can 60 * be deferred. For example: <pre> {@code 61 * class Bar { 62 * 63 * {@literal @}ReachabilitySensitive 64 * private final CloseGuard guard = CloseGuard.get(); 65 * 66 * ... 67 * 68 * public Bar() { 69 * ...; 70 * } 71 * 72 * public void connect() { 73 * ...; 74 * guard.open("cleanup"); 75 * } 76 * 77 * public void cleanup() { 78 * guard.close(); 79 * ...; 80 * } 81 * 82 * protected void finalize() throws Throwable { 83 * try { 84 * // Note that guard could be null if the constructor threw. 85 * if (guard != null) { 86 * guard.warnIfOpen(); 87 * } 88 * cleanup(); 89 * } finally { 90 * super.finalize(); 91 * } 92 * } 93 * } 94 * }</pre> 95 * 96 * When used in a constructor, calls to {@code open} should occur at 97 * the end of the constructor since an exception that would cause 98 * abrupt termination of the constructor will mean that the user will 99 * not have a reference to the object to cleanup explicitly. When used 100 * in a method, the call to {@code open} should occur just after 101 * resource acquisition. 102 * 103 * The @ReachabilitySensitive annotation ensures that finalize() cannot be 104 * called during the explicit call to cleanup(), prior to the guard.close call. 105 * There is an extremely small chance that, for code that neglects to call 106 * cleanup(), finalize() and thus cleanup() will be called while a method on 107 * the object is still active, but the "this" reference is no longer required. 108 * If missing cleanup() calls are expected, additional @ReachabilitySensitive 109 * annotations or reachabilityFence() calls may be required. 110 * 111 * @hide 112 */ 113 @libcore.api.CorePlatformApi 114 @libcore.api.IntraCoreApi 115 public final class CloseGuard { 116 117 /** 118 * True if collection of call-site information (the expensive operation 119 * here) and tracking via a Tracker (see below) are enabled. 120 * Enabled by default so we can diagnose issues early in VM startup. 121 * Note, however, that Android disables this early in its startup, 122 * but enables it with DropBoxing for system apps on debug builds. 123 */ 124 private static volatile boolean stackAndTrackingEnabled = true; 125 126 /** 127 * Hook for customizing how CloseGuard issues are reported. 128 * Bypassed if stackAndTrackingEnabled was false when open was called. 129 */ 130 private static volatile Reporter reporter = new DefaultReporter(); 131 132 /** 133 * Hook for customizing how CloseGuard issues are tracked. 134 */ 135 private static volatile Tracker currentTracker = null; // Disabled by default. 136 137 /** 138 * Returns a CloseGuard instance. {@code #open(String)} can be used to set 139 * up the instance to warn on failure to close. 140 */ 141 @UnsupportedAppUsage 142 @libcore.api.CorePlatformApi 143 @libcore.api.IntraCoreApi get()144 public static CloseGuard get() { 145 return new CloseGuard(); 146 } 147 148 /** 149 * Enables/disables stack capture and tracking. A call stack is captured 150 * during open(), and open/close events are reported to the Tracker, only 151 * if enabled is true. If a stack trace was captured, the {@link 152 * #getReporter() reporter} is informed of unclosed resources; otherwise a 153 * one-line warning is logged. 154 */ 155 @UnsupportedAppUsage 156 @libcore.api.CorePlatformApi setEnabled(boolean enabled)157 public static void setEnabled(boolean enabled) { 158 CloseGuard.stackAndTrackingEnabled = enabled; 159 } 160 161 /** 162 * True if CloseGuard stack capture and tracking are enabled. 163 */ isEnabled()164 public static boolean isEnabled() { 165 return stackAndTrackingEnabled; 166 } 167 168 /** 169 * Used to replace default Reporter used to warn of CloseGuard 170 * violations when stack tracking is enabled. Must be non-null. 171 */ 172 @UnsupportedAppUsage 173 @libcore.api.CorePlatformApi setReporter(Reporter rep)174 public static void setReporter(Reporter rep) { 175 if (rep == null) { 176 throw new NullPointerException("reporter == null"); 177 } 178 CloseGuard.reporter = rep; 179 } 180 181 /** 182 * Returns non-null CloseGuard.Reporter. 183 */ 184 @libcore.api.CorePlatformApi getReporter()185 public static Reporter getReporter() { 186 return reporter; 187 } 188 189 /** 190 * Sets the {@link Tracker} that is notified when resources are allocated and released. 191 * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()} 192 * was called. A null argument disables tracking. 193 * 194 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so 195 * MUST NOT be used for any other purposes. 196 */ setTracker(Tracker tracker)197 public static void setTracker(Tracker tracker) { 198 currentTracker = tracker; 199 } 200 201 /** 202 * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate 203 * there is none. 204 * 205 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so 206 * MUST NOT be used for any other purposes. 207 */ getTracker()208 public static Tracker getTracker() { 209 return currentTracker; 210 } 211 212 @UnsupportedAppUsage CloseGuard()213 private CloseGuard() {} 214 215 /** 216 * {@code open} initializes the instance with a warning that the caller 217 * should have explicitly called the {@code closer} method instead of 218 * relying on finalization. 219 * 220 * @param closer non-null name of explicit termination method. Printed by warnIfOpen. 221 * @throws NullPointerException if closer is null. 222 */ 223 @UnsupportedAppUsage 224 @libcore.api.CorePlatformApi 225 @libcore.api.IntraCoreApi open(String closer)226 public void open(String closer) { 227 // always perform the check for valid API usage... 228 if (closer == null) { 229 throw new NullPointerException("closer == null"); 230 } 231 // ...but avoid allocating an allocation stack if "disabled" 232 if (!stackAndTrackingEnabled) { 233 closerNameOrAllocationInfo = closer; 234 return; 235 } 236 String message = "Explicit termination method '" + closer + "' not called"; 237 Throwable stack = new Throwable(message); 238 closerNameOrAllocationInfo = stack; 239 Tracker tracker = currentTracker; 240 if (tracker != null) { 241 tracker.open(stack); 242 } 243 } 244 245 // We keep either an allocation stack containing the closer String or, when 246 // in disabled state, just the closer String. 247 // We keep them in a single field only to minimize overhead. 248 private Object /* String or Throwable */ closerNameOrAllocationInfo; 249 250 /** 251 * Marks this CloseGuard instance as closed to avoid warnings on 252 * finalization. 253 */ 254 @UnsupportedAppUsage 255 @libcore.api.CorePlatformApi 256 @libcore.api.IntraCoreApi close()257 public void close() { 258 Tracker tracker = currentTracker; 259 if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) { 260 // Invoke tracker on close only if we invoked it on open. Tracker may have changed. 261 tracker.close((Throwable) closerNameOrAllocationInfo); 262 } 263 closerNameOrAllocationInfo = null; 264 } 265 266 /** 267 * Logs a warning if the caller did not properly cleanup by calling an 268 * explicit close method before finalization. If CloseGuard was enabled 269 * when the CloseGuard was created, passes the stacktrace associated with 270 * the allocation to the current reporter. If it was not enabled, it just 271 * directly logs a brief message. 272 */ 273 @UnsupportedAppUsage 274 @libcore.api.CorePlatformApi 275 @libcore.api.IntraCoreApi warnIfOpen()276 public void warnIfOpen() { 277 if (closerNameOrAllocationInfo != null) { 278 if (closerNameOrAllocationInfo instanceof String) { 279 System.logW("A resource failed to call " 280 + (String) closerNameOrAllocationInfo + ". "); 281 } else { 282 String message = 283 "A resource was acquired at attached stack trace but never released. "; 284 message += "See java.io.Closeable for information on avoiding resource leaks."; 285 Throwable stack = (Throwable) closerNameOrAllocationInfo; 286 reporter.report(message, stack); 287 } 288 } 289 } 290 291 /** 292 * Interface to allow customization of tracking behaviour. 293 * 294 * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so 295 * MUST NOT be used for any other purposes. 296 */ 297 public interface Tracker { open(Throwable allocationSite)298 void open(Throwable allocationSite); close(Throwable allocationSite)299 void close(Throwable allocationSite); 300 } 301 302 /** 303 * Interface to allow customization of reporting behavior. 304 * @hide 305 */ 306 @libcore.api.CorePlatformApi 307 public interface Reporter { 308 @UnsupportedAppUsage 309 @libcore.api.CorePlatformApi report(String message, Throwable allocationSite)310 void report(String message, Throwable allocationSite); 311 } 312 313 /** 314 * Default Reporter which reports CloseGuard violations to the log. 315 */ 316 private static final class DefaultReporter implements Reporter { 317 @UnsupportedAppUsage DefaultReporter()318 private DefaultReporter() {} 319 report(String message, Throwable allocationSite)320 @Override public void report (String message, Throwable allocationSite) { 321 System.logW(message, allocationSite); 322 } 323 } 324 } 325