blob: b5bf380e229b5a3502f055dc1d00d63992ca381b [file] [log] [blame]
/*
* 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);
}
}
}