1fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin/* 2fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Copyright (C) 2016 The Android Open Source Project 3fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 4fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Licensed under the Apache License, Version 2.0 (the "License"); 5fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * you may not use this file except in compliance with the License. 6fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * You may obtain a copy of the License at 7fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 8fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * http://www.apache.org/licenses/LICENSE-2.0 9fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 10fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Unless required by applicable law or agreed to in writing, software 11fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * distributed under the License is distributed on an "AS IS" BASIS, 12fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * See the License for the specific language governing permissions and 14fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * limitations under the License. 15fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 16fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinpackage dalvik.system; 17fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 18fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.lang.reflect.Method; 19fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.ArrayList; 20fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.Collection; 21fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.Collections; 22fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.List; 23fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.Set; 24fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.concurrent.ConcurrentHashMap; 25fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport java.util.function.BiConsumer; 26fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport org.junit.rules.TestRule; 27fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport org.junit.runner.Description; 28fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinimport org.junit.runners.model.Statement; 29fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 30fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin/** 31fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Provides support for testing classes that use {@link CloseGuard} in order to detect resource 32fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * leakages. 33fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 34fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>This class should not be used directly by tests as that will prevent them from being 35fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * compilable and testable on OpenJDK platform. Instead they should use 36fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * {@code libcore.junit.util.ResourceLeakageDetector} which accesses the capabilities of this using 37fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * reflection and if it cannot find it (because it is running on OpenJDK) then it will just skip 38fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * leakage detection. 39fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 40fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>This provides two entry points that are accessed reflectively: 41fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <ul> 42fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <li> 43fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>The {@link #getRule()} method. This returns a {@link TestRule} that will fail a test if it 44fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * detects any resources that were allocated during the test but were not released. 45fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 46fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>This only tracks resources that were allocated on the test thread, although it does not care 47fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * what thread they were released on. This avoids flaky false positives where a background thread 48fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * allocates a resource during a test but releases it after the test. 49fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 50fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>It is still possible to have a false positive in the case where the test causes a caching 51fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * mechanism to open a resource and hold it open past the end of the test. In that case if there is 52fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * no way to clear the cached data then it should be relatively simple to move the code that invokes 53fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * the caching mechanism to outside the scope of this rule. i.e. 54fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 55fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <pre>{@code 56fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * @Rule 57fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * public final TestRule ruleChain = org.junit.rules.RuleChain 58fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * .outerRule(new ...invoke caching mechanism...) 59fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * .around(CloseGuardSupport.getRule()); 60fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * }</pre> 61fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * </li> 62fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <li> 63fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>The {@link #getFinalizerChecker()} method. This returns a {@link BiConsumer} that takes an 64fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * object that owns resources and an expected number of unreleased resources. It will call the 65fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * {@link Object#finalize()} method on the object using reflection and throw an 66fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * {@link AssertionError} if the number of reported unreleased resources does not match the 67fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * expected number. 68fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * </li> 69fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * </ul> 70fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 71fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffinpublic class CloseGuardSupport { 72fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 73fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static final TestRule CLOSE_GUARD_RULE = new FailTestWhenResourcesNotClosedRule(); 74fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 75fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 76fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Get a {@link TestRule} that will detect when resources that use the {@link CloseGuard} 77fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * mechanism are not cleaned up properly by a test. 78fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 79fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>If the {@link CloseGuard} mechanism is not supported, e.g. on OpenJDK, then the returned 80fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * rule does nothing. 81fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 82fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public static TestRule getRule() { 83fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin return CLOSE_GUARD_RULE; 84fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 85fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 86fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private CloseGuardSupport() { 87fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 88fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 89fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 90fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Fails a test when resources are not cleaned up properly. 91fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 92fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static class FailTestWhenResourcesNotClosedRule implements TestRule { 93fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 94fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Returns a {@link Statement} that will fail the test if it ends with unreleased resources. 95fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * @param base the test to be run. 96fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 97fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public Statement apply(Statement base, Description description) { 98fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin return new Statement() { 99fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin @Override 100fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public void evaluate() throws Throwable { 101fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Get the previous tracker so that it can be restored afterwards. 102fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.Tracker previousTracker = CloseGuard.getTracker(); 103fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Get the previous enabled state so that it can be restored afterwards. 104fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin boolean previousEnabled = CloseGuard.isEnabled(); 105fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin TestCloseGuardTracker tracker = new TestCloseGuardTracker(); 106fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Throwable thrown = null; 107fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin try { 108fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Set the test tracker and enable close guard detection. 109fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setTracker(tracker); 110fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setEnabled(true); 111fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin base.evaluate(); 112fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } catch (Throwable throwable) { 113fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Catch and remember the throwable so that it can be rethrown in the 114fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // finally block. 115fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin thrown = throwable; 116fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } finally { 117fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Restore the previous tracker and enabled state. 118fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setEnabled(previousEnabled); 119fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setTracker(previousTracker); 120fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 121fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Collection<Throwable> allocationSites = 122fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin tracker.getAllocationSitesForUnreleasedResources(); 123fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (!allocationSites.isEmpty()) { 124fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (thrown == null) { 125fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin thrown = new IllegalStateException( 126fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin "Unreleased resources found in test"); 127fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 128fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin for (Throwable allocationSite : allocationSites) { 129fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin thrown.addSuppressed(allocationSite); 130fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 131fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 132fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (thrown != null) { 133fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin throw thrown; 134fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 135fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 136fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 137fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin }; 138fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 139fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 140fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 141fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 142fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * A tracker that keeps a record of the allocation sites for all resources allocated but not 143fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * yet released. 144fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 145fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>It only tracks resources allocated for the test thread. 146fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 147fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static class TestCloseGuardTracker implements CloseGuard.Tracker { 148fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 149fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 150fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * A set would be preferable but this is the closest that matches the concurrency 151fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * requirements for the use case which prioritise speed of addition and removal over 152fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * iteration and access. 153fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 154fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private final Set<Throwable> allocationSites = 155fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Collections.newSetFromMap(new ConcurrentHashMap<>()); 156fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 157fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private final Thread testThread = Thread.currentThread(); 158fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 159fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin @Override 160fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public void open(Throwable allocationSite) { 161fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (Thread.currentThread() == testThread) { 162fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin allocationSites.add(allocationSite); 163fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 164fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 165fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 166fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin @Override 167fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public void close(Throwable allocationSite) { 168fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Closing the resource twice could pass null into here. 169fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (allocationSite != null) { 170fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin allocationSites.remove(allocationSite); 171fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 172fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 173fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 174fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 175fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Get the collection of allocation sites for any unreleased resources. 176fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 177fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Collection<Throwable> getAllocationSitesForUnreleasedResources() { 178fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin return new ArrayList<>(allocationSites); 179fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 180fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 181fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 182fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static final BiConsumer<Object, Integer> FINALIZER_CHECKER 183fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin = new BiConsumer<Object, Integer>() { 184fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin @Override 185fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public void accept(Object resourceOwner, Integer expectedCount) { 186fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin finalizerChecker(resourceOwner, expectedCount); 187fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 188fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin }; 189fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 190fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 191fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Get access to a {@link BiConsumer} that will determine how many unreleased resources the 192fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * first parameter owns and throw a {@link AssertionError} if that does not match the 193fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * expected number of resources specified by the second parameter. 194fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 195fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * <p>This uses a {@link BiConsumer} as it is a standard interface that is available in all 196fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * environments. That helps avoid the caller from having compile time dependencies on this 197fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * class which will not be available on OpenJDK. 198fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 199fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public static BiConsumer<Object, Integer> getFinalizerChecker() { 200fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin return FINALIZER_CHECKER; 201fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 202fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 203fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 204fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * Checks that the supplied {@code resourceOwner} has overridden the {@link Object#finalize()} 205fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * method and uses {@link CloseGuard#warnIfOpen()} correctly to detect when the resource is 206fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * not released. 207fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 208fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * @param resourceOwner the owner of the resource protected by {@link CloseGuard}. 209fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * @param expectedCount the expected number of unreleased resources to be held by the owner. 210fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * 211fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 212fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static void finalizerChecker(Object resourceOwner, int expectedCount) { 213fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Class<?> clazz = resourceOwner.getClass(); 214fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin Method finalizer = null; 215fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin while (clazz != null && clazz != Object.class) { 216fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin try { 217fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin finalizer = clazz.getDeclaredMethod("finalize"); 218fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin break; 219fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } catch (NoSuchMethodException e) { 220fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Carry on up the class hierarchy. 221fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin clazz = clazz.getSuperclass(); 222fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 223fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 224fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 225fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (finalizer == null) { 226fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // No finalizer method could be found. 227fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin throw new AssertionError("Class " + resourceOwner.getClass().getName() 228fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin + " does not have a finalize() method"); 229fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 230fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 231fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Make the method accessible. 232fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin finalizer.setAccessible(true); 233fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 234fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.Reporter oldReporter = CloseGuard.getReporter(); 235fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin try { 236fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CollectingReporter reporter = new CollectingReporter(); 237fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setReporter(reporter); 238fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 239fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Invoke the finalizer to cause it to get CloseGuard to report a problem if it has 240fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // not yet been closed. 241fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin try { 242fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin finalizer.invoke(resourceOwner); 243fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } catch (ReflectiveOperationException e) { 244fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin throw new AssertionError( 245fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin "Could not invoke the finalizer() method on " + resourceOwner, e); 246fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 247fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 248fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin reporter.assertUnreleasedResources(expectedCount); 249fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } finally { 250fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin CloseGuard.setReporter(oldReporter); 251fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 252fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 253fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 254fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin /** 255fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin * A {@link CloseGuard.Reporter} that collects any reports about unreleased resources. 256fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin */ 257fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private static class CollectingReporter implements CloseGuard.Reporter { 258fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 259fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private final Thread callingThread = Thread.currentThread(); 260fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 261fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin private final List<Throwable> unreleasedResourceAllocationSites = new ArrayList<>(); 262fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 263fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin @Override 264fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin public void report(String message, Throwable allocationSite) { 265fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin // Only care about resources that are not reported on this thread. 266fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (callingThread == Thread.currentThread()) { 267fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin unreleasedResourceAllocationSites.add(allocationSite); 268fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 269fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 270fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 271fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin void assertUnreleasedResources(int expectedCount) { 272fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin int unreleasedResourceCount = unreleasedResourceAllocationSites.size(); 273fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin if (unreleasedResourceCount == expectedCount) { 274fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin return; 275fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 276fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin 277fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin AssertionError error = new AssertionError( 278fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin "Expected " + expectedCount + " unreleased resources, found " 279fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin + unreleasedResourceCount + "; see suppressed exceptions for details"); 280fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin for (Throwable unreleasedResourceAllocationSite : unreleasedResourceAllocationSites) { 281fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin error.addSuppressed(unreleasedResourceAllocationSite); 282fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 283fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin throw error; 284fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 285fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin } 286fe0ee8ef8870338ad67ebfb6b62785e0cbdb325bPaul Duffin} 287