1 /* 2 * Copyright (C) 2017 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 public class Main { main(String[] args)18 public static void main(String[] args) throws Exception { 19 System.loadLibrary(args[0]); 20 if (isAotCompiled(Main.class, "hasJit")) { 21 throw new Error("This test must be run with --no-prebuild!"); 22 } 23 if (!hasJit()) { 24 return; 25 } 26 27 testCompilationUseAndCollection(); 28 testMixedFramesOnStack(); 29 } 30 testCompilationUseAndCollection()31 public static void testCompilationUseAndCollection() { 32 // Test that callThrough() can be JIT-compiled. 33 assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 34 assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 35 ensureCompiledCallThroughEntrypoint(/* call */ true); 36 assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 37 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 38 39 // Use callThrough() once again now that the method has a JIT-compiled stub. 40 callThrough(Main.class, "doNothing"); 41 42 // Test that GC with the JIT-compiled stub on the stack does not collect it. 43 // Also tests stack walk over the JIT-compiled stub. 44 callThrough(Main.class, "testGcWithCallThroughStubOnStack"); 45 46 // Test that, when marking used methods before a full JIT GC, a single execution 47 // of the GenericJNI trampoline can save the compiled stub from being collected. 48 testSingleInvocationTriggersRecompilation(); 49 50 // Test that the JNI compiled stub can actually be collected. 51 testStubCanBeCollected(); 52 } 53 testGcWithCallThroughStubOnStack()54 public static void testGcWithCallThroughStubOnStack() { 55 // Check that this method was called via JIT-compiled callThrough() stub. 56 assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 57 // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. 58 assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 59 60 doJitGcsUntilFullJitGcIsScheduled(); 61 // The callThrough() on the stack above this method is using the compiled stub, 62 // so the JIT GC should not remove the compiled code. 63 jitGc(); 64 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 65 } 66 testSingleInvocationTriggersRecompilation()67 public static void testSingleInvocationTriggersRecompilation() { 68 // After scheduling a full JIT GC, single call through the GenericJNI 69 // trampoline should ensure that the compiled stub is used again. 70 doJitGcsUntilFullJitGcIsScheduled(); 71 callThrough(Main.class, "doNothing"); 72 ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. 73 assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 74 jitGc(); // This JIT GC should not collect the callThrough() stub. 75 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 76 } 77 testMixedFramesOnStack()78 public static void testMixedFramesOnStack() { 79 // Starts without a compiled JNI stub for callThrough(). 80 assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 81 assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 82 callThrough(Main.class, "testMixedFramesOnStackStage2"); 83 // We have just returned through the JIT-compiled JNI stub, so it must still 84 // be compiled (though not necessarily with the entrypoint pointing to it). 85 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 86 // Though the callThrough() is on the stack, that frame is using the GenericJNI 87 // and does not prevent the collection of the JNI stub. 88 testStubCanBeCollected(); 89 } 90 testMixedFramesOnStackStage2()91 public static void testMixedFramesOnStackStage2() { 92 // We cannot assert that callThrough() has no JIT compiled stub as that check 93 // may race against the compilation task. Just check the caller. 94 assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 95 // Now ensure that the JNI stub is compiled and used. 96 ensureCompiledCallThroughEntrypoint(/* call */ true); 97 callThrough(Main.class, "testMixedFramesOnStackStage3"); 98 } 99 testMixedFramesOnStackStage3()100 public static void testMixedFramesOnStackStage3() { 101 // Check that this method was called via JIT-compiled callThrough() stub. 102 assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 103 // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. 104 assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 105 // For a good measure, try a JIT GC. 106 jitGc(); 107 } 108 testStubCanBeCollected()109 public static void testStubCanBeCollected() { 110 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 111 doJitGcsUntilFullJitGcIsScheduled(); 112 assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 113 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 114 jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. 115 assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 116 assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 117 } 118 doJitGcsUntilFullJitGcIsScheduled()119 public static void doJitGcsUntilFullJitGcIsScheduled() { 120 // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. 121 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 122 ensureCompiledCallThroughEntrypoint(/* call */ true); 123 // Perform JIT GC until the next GC is marked to do full collection. 124 do { 125 assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 126 callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. 127 } while (!isNextJitGcFull()); 128 // The JIT GC before the full collection resets entrypoints and waits to see 129 // if the methods are still in use. 130 assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 131 assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 132 } 133 ensureCompiledCallThroughEntrypoint(boolean call)134 public static void ensureCompiledCallThroughEntrypoint(boolean call) { 135 int count = 0; 136 while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { 137 // If `call` is true, also exercise the `callThrough()` method to increase hotness. 138 // Ramp-up the number of calls we do up to 1 << 12. 139 final int rampUpCutOff = 12; 140 int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; 141 for (int i = 0; i < limit; ++i) { 142 callThrough(Main.class, "doNothing"); 143 } 144 try { 145 // Sleep to give a chance for the JIT to compile `callThrough` stub. 146 // After the ramp-up phase, give the JIT even more time to compile. 147 Thread.sleep(count >= rampUpCutOff ? 200 : 100); 148 } catch (Exception e) { 149 // Ignore 150 } 151 if (++count == 50) { 152 throw new Error("TIMEOUT"); 153 } 154 }; 155 } 156 assertTrue(boolean value)157 public static void assertTrue(boolean value) { 158 if (!value) { 159 throw new AssertionError("Expected true!"); 160 } 161 } 162 assertFalse(boolean value)163 public static void assertFalse(boolean value) { 164 if (value) { 165 throw new AssertionError("Expected false!"); 166 } 167 } 168 doNothing()169 public static void doNothing() { } throwError()170 public static void throwError() { throw new Error(); } 171 172 // Note that the callThrough()'s shorty differs from shorties of the other 173 // native methods used in this test because of the return type `void.` callThrough(Class<?> cls, String methodName)174 public native static void callThrough(Class<?> cls, String methodName); 175 jitGc()176 public native static void jitGc(); isNextJitGcFull()177 public native static boolean isNextJitGcFull(); 178 isAotCompiled(Class<?> cls, String methodName)179 public native static boolean isAotCompiled(Class<?> cls, String methodName); hasJitCompiledEntrypoint(Class<?> cls, String methodName)180 public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName); hasJitCompiledCode(Class<?> cls, String methodName)181 public native static boolean hasJitCompiledCode(Class<?> cls, String methodName); hasJit()182 private native static boolean hasJit(); 183 } 184