1 /* 2 * Copyright (C) 2016 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 libcore.libcore.util; 18 19 import android.platform.test.annotations.RequiresFlagsEnabled; 20 21 import java.util.Collection; 22 import junit.framework.TestCase; 23 24 import libcore.util.NativeAllocationRegistry; 25 26 import org.junit.Ignore; 27 28 public class NativeAllocationRegistryTest extends TestCase { 29 30 static { 31 System.loadLibrary("javacoretests"); 32 } 33 34 private ClassLoader classLoader = NativeAllocationRegistryTest.class.getClassLoader(); 35 36 private static class TestConfig { 37 public boolean treatAsMalloced; 38 public boolean shareRegistry; 39 public boolean testMetrics; 40 TestConfig(boolean treatAsMalloced, boolean shareRegistry, boolean testMetrics)41 public TestConfig(boolean treatAsMalloced, boolean shareRegistry, boolean testMetrics) { 42 this.treatAsMalloced = treatAsMalloced; 43 this.shareRegistry = shareRegistry; 44 this.testMetrics = testMetrics; 45 } 46 } 47 48 private static class TestClass { 49 } 50 51 private static class Allocation { 52 public byte[] javaAllocation; 53 public long nativeAllocation; 54 } 55 56 // Verify that NativeAllocations and their referents are freed before we run 57 // out of space for new allocations. testNativeAllocation(TestConfig config)58 private void testNativeAllocation(TestConfig config) { 59 if (isNativeBridgedABI()) { 60 // 1. This test is intended to test platform internals, not public API. 61 // 2. The test would fail under native bridge as a side effect of how the tests work: 62 // - The tests run using the app architecture instead of the platform architecture 63 // - That scenario will never happen in practice due to (1) 64 // 3. This leaves a hole in testing for the case of native bridge, due to limitations 65 // in the testing infrastructure from (2). 66 System.logI("Skipping test for native bridged ABI"); 67 return; 68 } 69 Runtime.getRuntime().gc(); 70 System.runFinalization(); 71 long nativeBytes = getNumNativeBytesAllocated(); 72 assertEquals("Native bytes already allocated", 0, nativeBytes); 73 long max = Runtime.getRuntime().maxMemory(); 74 long total = Runtime.getRuntime().totalMemory(); 75 int size = 1024 * 1024; 76 final int nativeSize = size / 2; 77 int javaSize = size / 2; 78 int expectedMaxNumAllocations = (int)(max-total) / javaSize; 79 int numSavedAllocations = expectedMaxNumAllocations / 2; 80 Allocation[] saved = new Allocation[numSavedAllocations]; 81 82 NativeAllocationRegistry registry = null; 83 int numAllocationsToSimulate = 10 * expectedMaxNumAllocations; 84 85 // Allocate more native allocations than will fit in memory. This should 86 // not throw OutOfMemoryError because the few allocations we save 87 // references to should easily fit. 88 for (int i = 0; i < numAllocationsToSimulate; i++) { 89 if (!config.shareRegistry || registry == null) { 90 if (config.treatAsMalloced) { 91 registry = config.testMetrics 92 ? NativeAllocationRegistry.createMalloced( 93 TestClass.class, getNativeFinalizer(), nativeSize) 94 : NativeAllocationRegistry.createMalloced( 95 classLoader, getNativeFinalizer(), nativeSize); 96 } else { 97 registry = config.testMetrics 98 ? NativeAllocationRegistry.createNonmalloced( 99 TestClass.class, getNativeFinalizer(), nativeSize) 100 : NativeAllocationRegistry.createNonmalloced( 101 classLoader, getNativeFinalizer(), nativeSize); 102 } 103 } 104 105 final Allocation alloc = new Allocation(); 106 alloc.javaAllocation = new byte[javaSize]; 107 alloc.nativeAllocation = doNativeAllocation(nativeSize); 108 registry.registerNativeAllocation(alloc, alloc.nativeAllocation); 109 110 saved[i % numSavedAllocations] = alloc; 111 } 112 113 // Verify most of the allocations have been freed. Since we use fairly large Java 114 // objects, this doesn't test the GC triggering effect; we do that elsewhere. 115 // 116 // Since native and java objects have the same size, and we can only have max Java bytes 117 // in use, there should ideally be no more than max native bytes in use, once all enqueued 118 // deallocations have been processed. We call runFinalization() to make sure that the 119 // ReferenceQueueDaemon has processed all pending requests, and then check. 120 // (runFinalization() isn't documented to guarantee this, but it waits for a sentinel 121 // object to make it all the way through the pending reference queue, and hence has that 122 // effect.) 123 // 124 // However the garbage collector enqueues references asynchronously, by enqueuing 125 // another heap task. If the GC runs before we finish our allocation, but reference 126 // enqueueing is delayed, and runFinalization() runs between the time the GC reclaims 127 // memory and the references are enqueued, then runFinalization() may complete 128 // immediately, and further allocation may have occurred between the GC and the invocation 129 // of runFinalization(). Thus, under unlikely conditions, we may see up to twice as much 130 // native memory as the Java heap, and that's the actual condition we test. 131 System.runFinalization(); 132 nativeBytes = getNumNativeBytesAllocated(); 133 assertTrue("Excessive native bytes still allocated (" + nativeBytes + ")" 134 + " given max memory of (" + max + ")", nativeBytes <= 2 * max); 135 // Check that the array is fully populated, and sufficiently many native bytes 136 // are live. 137 long nativeReachableBytes = numSavedAllocations * nativeSize; 138 for (int i = 0; i < numSavedAllocations; i++) { 139 assertNotNull(saved[i]); 140 assertNotNull(saved[i].javaAllocation); 141 assertTrue(saved[i].nativeAllocation != 0); 142 } 143 assertTrue("Too few native bytes still allocated (" + nativeBytes + "); " 144 + nativeReachableBytes + " bytes are reachable", 145 nativeBytes >= nativeReachableBytes); 146 147 if (config.testMetrics) { 148 Collection<NativeAllocationRegistry.Metrics> metrics = 149 NativeAllocationRegistry.getMetrics(); 150 151 assertTrue("Expect at least 1 metrics, got "+ metrics.size() + "instead", 152 metrics.size() >= 1); 153 154 boolean hasTestClassMetrics = false; 155 for (NativeAllocationRegistry.Metrics m : metrics) { 156 if (m.getClassName().equals(TestClass.class.getName())) { 157 final long count = config.treatAsMalloced ? m.getMallocedCount() 158 : m.getNonmallocedCount(); 159 final long bytes = config.treatAsMalloced ? m.getMallocedBytes() 160 : m.getNonmallocedBytes(); 161 162 assertTrue("Expect native allocations count to be at least " + 163 numSavedAllocations + ", got " + count + " instead", 164 count >= numSavedAllocations); 165 assertTrue("Expect native allocations bytes to be at least " + 166 nativeReachableBytes + ", got " + bytes + " instead", 167 bytes >= nativeReachableBytes); 168 169 hasTestClassMetrics = true; 170 } 171 } 172 assertTrue("No metrics for " + TestClass.class.getName(), hasTestClassMetrics); 173 } 174 } 175 testNativeAllocationNonmallocNoSharedRegistry()176 public void testNativeAllocationNonmallocNoSharedRegistry() { 177 testNativeAllocation(new TestConfig(false, false, false)); 178 } 179 testNativeAllocationNonmallocSharedRegistry()180 public void testNativeAllocationNonmallocSharedRegistry() { 181 testNativeAllocation(new TestConfig(false, true, false)); 182 } 183 testNativeAllocationMallocNoSharedRegistry()184 public void testNativeAllocationMallocNoSharedRegistry() { 185 testNativeAllocation(new TestConfig(true, false, false)); 186 } 187 testNativeAllocationMallocSharedRegistry()188 public void testNativeAllocationMallocSharedRegistry() { 189 testNativeAllocation(new TestConfig(true, true, false)); 190 } 191 192 @RequiresFlagsEnabled(com.android.libcore.Flags.FLAG_NATIVE_METRICS) ignoreNativeAllocationNonmallocNoSharedRegistryWithMetrics()193 public void ignoreNativeAllocationNonmallocNoSharedRegistryWithMetrics() { 194 testNativeAllocation(new TestConfig(false, false, true)); 195 } 196 197 @RequiresFlagsEnabled(com.android.libcore.Flags.FLAG_NATIVE_METRICS) ignoreNativeAllocationNonmallocSharedRegistryWithMetrics()198 public void ignoreNativeAllocationNonmallocSharedRegistryWithMetrics() { 199 testNativeAllocation(new TestConfig(false, true, true)); 200 } 201 202 @RequiresFlagsEnabled(com.android.libcore.Flags.FLAG_NATIVE_METRICS) ignoreNativeAllocationMallocNoSharedRegistryWithMetrics()203 public void ignoreNativeAllocationMallocNoSharedRegistryWithMetrics() { 204 testNativeAllocation(new TestConfig(true, false, true)); 205 } 206 207 @RequiresFlagsEnabled(com.android.libcore.Flags.FLAG_NATIVE_METRICS) ignoreNativeAllocationMallocSharedRegistryWithMetrics()208 public void ignoreNativeAllocationMallocSharedRegistryWithMetrics() { 209 testNativeAllocation(new TestConfig(true, true, true)); 210 } 211 testBadSize()212 public void testBadSize() { 213 assertThrowsIllegalArgumentException(new Runnable() { 214 public void run() { 215 NativeAllocationRegistry registry = new NativeAllocationRegistry( 216 classLoader, getNativeFinalizer(), -8); 217 } 218 }); 219 } 220 testEarlyFree()221 public void testEarlyFree() { 222 if (isNativeBridgedABI()) { 223 // See the explanation in testNativeAllocation. 224 System.logI("Skipping test for native bridged ABI"); 225 return; 226 } 227 long size = 1234; 228 NativeAllocationRegistry registry 229 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), size); 230 long nativePtr = doNativeAllocation(size); 231 Object referent = new Object(); 232 Runnable cleaner = registry.registerNativeAllocation(referent, nativePtr); 233 long numBytesAllocatedBeforeClean = getNumNativeBytesAllocated(); 234 235 // Running the cleaner should cause the native finalizer to run. 236 cleaner.run(); 237 long numBytesAllocatedAfterClean = getNumNativeBytesAllocated(); 238 assertEquals(numBytesAllocatedBeforeClean - size, numBytesAllocatedAfterClean); 239 240 // Running the cleaner again should have no effect. 241 cleaner.run(); 242 assertEquals(numBytesAllocatedAfterClean, getNumNativeBytesAllocated()); 243 244 // There shouldn't be any problems when the referent object is GC'd. 245 referent = null; 246 Runtime.getRuntime().gc(); 247 } 248 testApplyFreeFunction()249 public void testApplyFreeFunction() { 250 if (isNativeBridgedABI()) { 251 // See the explanation in testNativeAllocation. 252 System.logI("Skipping test for native bridged ABI"); 253 return; 254 } 255 long size = 1234; 256 long nativePtr = doNativeAllocation(size); 257 long numBytesAllocatedBeforeFree = getNumNativeBytesAllocated(); 258 259 // Applying the free function should cause the native finalizer to run. 260 NativeAllocationRegistry.applyFreeFunction(getNativeFinalizer(), nativePtr); 261 long numBytesAllocatedAfterFree = getNumNativeBytesAllocated(); 262 assertEquals(numBytesAllocatedBeforeFree - size, numBytesAllocatedAfterFree); 263 } 264 testNullArguments()265 public void testNullArguments() { 266 final NativeAllocationRegistry registry 267 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), 1024); 268 final long fakeNativePtr = 0x1; 269 final Object referent = new Object(); 270 271 // referent should not be null 272 assertThrowsIllegalArgumentException(new Runnable() { 273 public void run() { 274 registry.registerNativeAllocation(null, fakeNativePtr); 275 } 276 }); 277 278 // nativePtr should not be null 279 assertThrowsIllegalArgumentException(new Runnable() { 280 public void run() { 281 registry.registerNativeAllocation(referent, 0); 282 } 283 }); 284 } 285 assertThrowsIllegalArgumentException(Runnable runnable)286 private static void assertThrowsIllegalArgumentException(Runnable runnable) { 287 try { 288 runnable.run(); 289 } catch (IllegalArgumentException ex) { 290 return; 291 } 292 fail("Expected IllegalArgumentException, but no exception was thrown."); 293 } 294 isNativeBridgedABI()295 private static native boolean isNativeBridgedABI(); getNativeFinalizer()296 private static native long getNativeFinalizer(); doNativeAllocation(long size)297 private static native long doNativeAllocation(long size); getNumNativeBytesAllocated()298 private static native long getNumNativeBytesAllocated(); 299 } 300