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 junit.framework.TestCase; 20 21 import libcore.util.NativeAllocationRegistry; 22 23 public class NativeAllocationRegistryTest extends TestCase { 24 25 static { 26 System.loadLibrary("javacoretests"); 27 } 28 29 private ClassLoader classLoader = NativeAllocationRegistryTest.class.getClassLoader(); 30 31 private static class TestConfig { 32 public boolean treatAsMalloced; 33 public boolean shareRegistry; 34 TestConfig(boolean treatAsMalloced, boolean shareRegistry)35 public TestConfig(boolean treatAsMalloced, boolean shareRegistry) { 36 this.shareRegistry = shareRegistry; 37 } 38 } 39 40 private static class Allocation { 41 public byte[] javaAllocation; 42 public long nativeAllocation; 43 } 44 45 // Verify that NativeAllocations and their referents are freed before we run 46 // out of space for new allocations. testNativeAllocation(TestConfig config)47 private void testNativeAllocation(TestConfig config) { 48 if (isNativeBridgedABI()) { 49 // 1. This test is intended to test platform internals, not public API. 50 // 2. The test would fail under native bridge as a side effect of how the tests work: 51 // - The tests run using the app architecture instead of the platform architecture 52 // - That scenario will never happen in practice due to (1) 53 // 3. This leaves a hole in testing for the case of native bridge, due to limitations 54 // in the testing infrastructure from (2). 55 System.logI("Skipping test for native bridged ABI"); 56 return; 57 } 58 Runtime.getRuntime().gc(); 59 System.runFinalization(); 60 long nativeBytes = getNumNativeBytesAllocated(); 61 assertEquals("Native bytes already allocated", 0, nativeBytes); 62 long max = Runtime.getRuntime().maxMemory(); 63 long total = Runtime.getRuntime().totalMemory(); 64 int size = 1024*1024; 65 final int nativeSize = size/2; 66 int javaSize = size/2; 67 int expectedMaxNumAllocations = (int)(max-total)/javaSize; 68 int numSavedAllocations = expectedMaxNumAllocations/2; 69 Allocation[] saved = new Allocation[numSavedAllocations]; 70 71 NativeAllocationRegistry registry = null; 72 int numAllocationsToSimulate = 10 * expectedMaxNumAllocations; 73 74 // Allocate more native allocations than will fit in memory. This should 75 // not throw OutOfMemoryError because the few allocations we save 76 // references to should easily fit. 77 for (int i = 0; i < numAllocationsToSimulate; i++) { 78 if (!config.shareRegistry || registry == null) { 79 if (config.treatAsMalloced) { 80 registry = NativeAllocationRegistry.createMalloced( 81 classLoader, getNativeFinalizer(), nativeSize); 82 } else { 83 registry = NativeAllocationRegistry.createNonmalloced( 84 classLoader, getNativeFinalizer(), nativeSize); 85 } 86 } 87 88 final Allocation alloc = new Allocation(); 89 alloc.javaAllocation = new byte[javaSize]; 90 alloc.nativeAllocation = doNativeAllocation(nativeSize); 91 registry.registerNativeAllocation(alloc, alloc.nativeAllocation); 92 93 saved[i%numSavedAllocations] = alloc; 94 } 95 96 // Verify most of the allocations have been freed. 97 // Since we use fairly large Java objects, this doesn't test the GC triggering 98 // effect; we do that elsewhere. 99 // Since native and java objects have the same size, and we can only have max 100 // Java bytes in use, there should be no more than max native bytes in use, 101 // once all enqueued deallocations have been processed. First make sure 102 // that the ReferenceQueueDaemon has processed all pending requests, and then 103 // check. 104 System.runFinalization(); 105 nativeBytes = getNumNativeBytesAllocated(); 106 assertTrue("Excessive native bytes still allocated (" + nativeBytes + ")" 107 + " given max memory of (" + max + ")", nativeBytes <= max); 108 // Check that the array is fully populated, and sufficiently many native bytes 109 // are live. 110 long nativeReachableBytes = numSavedAllocations * nativeSize; 111 for (int i = 0; i < numSavedAllocations; i++) { 112 assertNotNull(saved[i]); 113 assertNotNull(saved[i].javaAllocation); 114 assertTrue(saved[i].nativeAllocation != 0); 115 } 116 assertTrue("Too few native bytes still allocated (" + nativeBytes + "); " 117 + nativeReachableBytes + " bytes are reachable", 118 nativeBytes >= nativeReachableBytes); 119 } 120 testNativeAllocationNonmallocNoSharedRegistry()121 public void testNativeAllocationNonmallocNoSharedRegistry() { 122 testNativeAllocation(new TestConfig(false, false)); 123 } 124 testNativeAllocationNonmallocSharedRegistry()125 public void testNativeAllocationNonmallocSharedRegistry() { 126 testNativeAllocation(new TestConfig(false, true)); 127 } 128 testNativeAllocationMallocNoSharedRegistry()129 public void testNativeAllocationMallocNoSharedRegistry() { 130 testNativeAllocation(new TestConfig(true, false)); 131 } 132 testNativeAllocationMallocSharedRegistry()133 public void testNativeAllocationMallocSharedRegistry() { 134 testNativeAllocation(new TestConfig(true, true)); 135 } 136 testBadSize()137 public void testBadSize() { 138 assertThrowsIllegalArgumentException(new Runnable() { 139 public void run() { 140 NativeAllocationRegistry registry = new NativeAllocationRegistry( 141 classLoader, getNativeFinalizer(), -8); 142 } 143 }); 144 } 145 testEarlyFree()146 public void testEarlyFree() { 147 if (isNativeBridgedABI()) { 148 // See the explanation in testNativeAllocation. 149 System.logI("Skipping test for native bridged ABI"); 150 return; 151 } 152 long size = 1234; 153 NativeAllocationRegistry registry 154 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), size); 155 long nativePtr = doNativeAllocation(size); 156 Object referent = new Object(); 157 Runnable cleaner = registry.registerNativeAllocation(referent, nativePtr); 158 long numBytesAllocatedBeforeClean = getNumNativeBytesAllocated(); 159 160 // Running the cleaner should cause the native finalizer to run. 161 cleaner.run(); 162 long numBytesAllocatedAfterClean = getNumNativeBytesAllocated(); 163 assertEquals(numBytesAllocatedBeforeClean - size, numBytesAllocatedAfterClean); 164 165 // Running the cleaner again should have no effect. 166 cleaner.run(); 167 assertEquals(numBytesAllocatedAfterClean, getNumNativeBytesAllocated()); 168 169 // There shouldn't be any problems when the referent object is GC'd. 170 referent = null; 171 Runtime.getRuntime().gc(); 172 } 173 testNullArguments()174 public void testNullArguments() { 175 final NativeAllocationRegistry registry 176 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), 1024); 177 final long fakeNativePtr = 0x1; 178 final Object referent = new Object(); 179 180 // referent should not be null 181 assertThrowsIllegalArgumentException(new Runnable() { 182 public void run() { 183 registry.registerNativeAllocation(null, fakeNativePtr); 184 } 185 }); 186 187 // nativePtr should not be null 188 assertThrowsIllegalArgumentException(new Runnable() { 189 public void run() { 190 registry.registerNativeAllocation(referent, 0); 191 } 192 }); 193 } 194 assertThrowsIllegalArgumentException(Runnable runnable)195 private static void assertThrowsIllegalArgumentException(Runnable runnable) { 196 try { 197 runnable.run(); 198 } catch (IllegalArgumentException ex) { 199 return; 200 } 201 fail("Expected IllegalArgumentException, but no exception was thrown."); 202 } 203 isNativeBridgedABI()204 private static native boolean isNativeBridgedABI(); getNativeFinalizer()205 private static native long getNativeFinalizer(); doNativeAllocation(long size)206 private static native long doNativeAllocation(long size); getNumNativeBytesAllocated()207 private static native long getNumNativeBytesAllocated(); 208 } 209