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