| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package dalvik.system; |
| |
| import dalvik.system.CloseGuard.Reporter; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.ref.WeakReference; |
| import java.util.List; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| |
| /** |
| * Provides support for detecting issues found by {@link CloseGuard} from within tests. |
| * |
| * <p>This is a best effort as it relies on both {@link CloseGuard} being enabled and being able to |
| * force a GC and finalization, none of which are directly controllable by this. |
| * |
| * <p>This is loaded using reflection by the AbstractResourceLeakageDetectorTestCase class as that |
| * class needs to run on the reference implementation which does not have this class. It implements |
| * {@link Runnable} because that is simpler than trying to manage a specialized interface. |
| * |
| * @hide |
| */ |
| public class CloseGuardMonitor implements Runnable { |
| /** |
| * The {@link Reporter} instance used to receive warnings from {@link CloseGuard}. |
| */ |
| private final Reporter closeGuardReporter; |
| |
| /** |
| * The list of allocation sites that {@link CloseGuard} has reported as not being released. |
| * |
| * <p>Is thread safe as this will be called during finalization and so there are no guarantees |
| * as to whether it will be called concurrently or not. |
| */ |
| private final List<Throwable> closeGuardAllocationSites = new CopyOnWriteArrayList<>(); |
| |
| /** |
| * Default constructor required for reflection. |
| */ |
| public CloseGuardMonitor() { |
| System.logI("Creating CloseGuard monitor"); |
| |
| // Save current reporter. |
| closeGuardReporter = CloseGuard.getReporter(); |
| |
| // Override the reporter with our own which collates the allocation sites. |
| CloseGuard.setReporter(new Reporter() { |
| @Override |
| public void report(String message, Throwable allocationSite) { |
| // Ignore message as it's always the same. |
| closeGuardAllocationSites.add(allocationSite); |
| } |
| }); |
| } |
| |
| /** |
| * Check to see whether any resources monitored by {@link CloseGuard} were not released before |
| * they were garbage collected. |
| */ |
| @Override |
| public void run() { |
| // Create a weak reference to an object so that we can detect when it is garbage collected. |
| WeakReference<Object> reference = new WeakReference<>(new Object()); |
| |
| try { |
| // 'Force' a GC and finalize to cause CloseGuards to report warnings. Doesn't loop |
| // forever as there are no guarantees that the following code does anything at all so |
| // don't want a potential infinite loop. |
| Runtime runtime = Runtime.getRuntime(); |
| for (int i = 0; i < 20; ++i) { |
| runtime.gc(); |
| System.runFinalization(); |
| try { |
| Thread.sleep(1); |
| } catch (InterruptedException e) { |
| throw new AssertionError(e); |
| } |
| |
| // Check to see if the weak reference has been garbage collected. |
| if (reference.get() == null) { |
| System.logI("Sentry object has been freed so assuming CloseGuards have reported" |
| + " any resource leakages"); |
| break; |
| } |
| } |
| } finally { |
| // Restore the reporter. |
| CloseGuard.setReporter(closeGuardReporter); |
| } |
| |
| if (!closeGuardAllocationSites.isEmpty()) { |
| StringWriter writer = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(writer); |
| int i = 0; |
| for (Throwable allocationSite : closeGuardAllocationSites) { |
| printWriter.print(++i); |
| printWriter.print(") "); |
| allocationSite.printStackTrace(printWriter); |
| printWriter.println(" --------------------------------"); |
| } |
| throw new AssertionError("Potential resource leakage detected:\n" + writer); |
| } |
| } |
| } |