• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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