1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker public class Main { main(String[] args)18*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) throws Exception { 19*795d594fSAndroid Build Coastguard Worker System.loadLibrary(args[0]); 20*795d594fSAndroid Build Coastguard Worker if (isAotCompiled(Main.class, "hasJit")) { 21*795d594fSAndroid Build Coastguard Worker throw new Error("This test must be run with --no-prebuild!"); 22*795d594fSAndroid Build Coastguard Worker } 23*795d594fSAndroid Build Coastguard Worker if (!hasJit()) { 24*795d594fSAndroid Build Coastguard Worker return; 25*795d594fSAndroid Build Coastguard Worker } 26*795d594fSAndroid Build Coastguard Worker 27*795d594fSAndroid Build Coastguard Worker // Deoptimize callThrough() to ensure it can be JITed afterwards. 28*795d594fSAndroid Build Coastguard Worker deoptimizeNativeMethod(Main.class, "callThrough"); 29*795d594fSAndroid Build Coastguard Worker testCompilationUseAndCollection(); 30*795d594fSAndroid Build Coastguard Worker testMixedFramesOnStack(); 31*795d594fSAndroid Build Coastguard Worker } 32*795d594fSAndroid Build Coastguard Worker testCompilationUseAndCollection()33*795d594fSAndroid Build Coastguard Worker public static void testCompilationUseAndCollection() { 34*795d594fSAndroid Build Coastguard Worker // Test that callThrough() can be JIT-compiled. 35*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 36*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 37*795d594fSAndroid Build Coastguard Worker ensureCompiledCallThroughEntrypoint(/* call */ true); 38*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 39*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 40*795d594fSAndroid Build Coastguard Worker 41*795d594fSAndroid Build Coastguard Worker // Use callThrough() once again now that the method has a JIT-compiled stub. 42*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "doNothing"); 43*795d594fSAndroid Build Coastguard Worker 44*795d594fSAndroid Build Coastguard Worker // Test that GC with the JIT-compiled stub on the stack does not collect it. 45*795d594fSAndroid Build Coastguard Worker // Also tests stack walk over the JIT-compiled stub. 46*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "testGcWithCallThroughStubOnStack"); 47*795d594fSAndroid Build Coastguard Worker 48*795d594fSAndroid Build Coastguard Worker // Test that the JNI compiled stub can actually be collected. 49*795d594fSAndroid Build Coastguard Worker testStubCanBeCollected(); 50*795d594fSAndroid Build Coastguard Worker } 51*795d594fSAndroid Build Coastguard Worker testGcWithCallThroughStubOnStack()52*795d594fSAndroid Build Coastguard Worker public static void testGcWithCallThroughStubOnStack() { 53*795d594fSAndroid Build Coastguard Worker // Check that this method was called via JIT-compiled callThrough() stub. 54*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 55*795d594fSAndroid Build Coastguard Worker // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. 56*795d594fSAndroid Build Coastguard Worker assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 57*795d594fSAndroid Build Coastguard Worker 58*795d594fSAndroid Build Coastguard Worker doJitGcsUntilFullJitGcIsScheduled(); 59*795d594fSAndroid Build Coastguard Worker // The callThrough() on the stack above this method is using the compiled stub, 60*795d594fSAndroid Build Coastguard Worker // so the JIT GC should not remove the compiled code. 61*795d594fSAndroid Build Coastguard Worker jitGc(); 62*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 63*795d594fSAndroid Build Coastguard Worker } 64*795d594fSAndroid Build Coastguard Worker testMixedFramesOnStack()65*795d594fSAndroid Build Coastguard Worker public static void testMixedFramesOnStack() { 66*795d594fSAndroid Build Coastguard Worker // Starts without a compiled JNI stub for callThrough(). 67*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 68*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 69*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "testMixedFramesOnStackStage2"); 70*795d594fSAndroid Build Coastguard Worker // Though the callThrough() is on the stack, that frame is using the GenericJNI 71*795d594fSAndroid Build Coastguard Worker // and does not prevent the collection of the JNI stub. 72*795d594fSAndroid Build Coastguard Worker testStubCanBeCollected(); 73*795d594fSAndroid Build Coastguard Worker } 74*795d594fSAndroid Build Coastguard Worker testMixedFramesOnStackStage2()75*795d594fSAndroid Build Coastguard Worker public static void testMixedFramesOnStackStage2() { 76*795d594fSAndroid Build Coastguard Worker // We cannot assert that callThrough() has no JIT compiled stub as that check 77*795d594fSAndroid Build Coastguard Worker // may race against the compilation task. Just check the caller. 78*795d594fSAndroid Build Coastguard Worker assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 79*795d594fSAndroid Build Coastguard Worker // Now ensure that the JNI stub is compiled and used. 80*795d594fSAndroid Build Coastguard Worker ensureCompiledCallThroughEntrypoint(/* call */ true); 81*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "testMixedFramesOnStackStage3"); 82*795d594fSAndroid Build Coastguard Worker } 83*795d594fSAndroid Build Coastguard Worker testMixedFramesOnStackStage3()84*795d594fSAndroid Build Coastguard Worker public static void testMixedFramesOnStackStage3() { 85*795d594fSAndroid Build Coastguard Worker // Check that this method was called via JIT-compiled callThrough() stub. 86*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 87*795d594fSAndroid Build Coastguard Worker // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. 88*795d594fSAndroid Build Coastguard Worker assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); 89*795d594fSAndroid Build Coastguard Worker // For a good measure, try a JIT GC. 90*795d594fSAndroid Build Coastguard Worker jitGc(); 91*795d594fSAndroid Build Coastguard Worker } 92*795d594fSAndroid Build Coastguard Worker testStubCanBeCollected()93*795d594fSAndroid Build Coastguard Worker public static void testStubCanBeCollected() { 94*795d594fSAndroid Build Coastguard Worker jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. 95*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 96*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledCode(Main.class, "callThrough")); 97*795d594fSAndroid Build Coastguard Worker } 98*795d594fSAndroid Build Coastguard Worker doJitGcsUntilFullJitGcIsScheduled()99*795d594fSAndroid Build Coastguard Worker public static void doJitGcsUntilFullJitGcIsScheduled() { 100*795d594fSAndroid Build Coastguard Worker // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. 101*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 102*795d594fSAndroid Build Coastguard Worker ensureCompiledCallThroughEntrypoint(/* call */ true); 103*795d594fSAndroid Build Coastguard Worker // Perform JIT GC until the next GC is marked to do full collection. 104*795d594fSAndroid Build Coastguard Worker do { 105*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); 106*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. 107*795d594fSAndroid Build Coastguard Worker } while (!isNextJitGcFull()); 108*795d594fSAndroid Build Coastguard Worker // The JIT GC before the full collection resets entrypoints and waits to see 109*795d594fSAndroid Build Coastguard Worker // if the methods are still in use. 110*795d594fSAndroid Build Coastguard Worker assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); 111*795d594fSAndroid Build Coastguard Worker assertTrue(hasJitCompiledCode(Main.class, "callThrough")); 112*795d594fSAndroid Build Coastguard Worker } 113*795d594fSAndroid Build Coastguard Worker ensureCompiledCallThroughEntrypoint(boolean call)114*795d594fSAndroid Build Coastguard Worker public static void ensureCompiledCallThroughEntrypoint(boolean call) { 115*795d594fSAndroid Build Coastguard Worker int count = 0; 116*795d594fSAndroid Build Coastguard Worker while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { 117*795d594fSAndroid Build Coastguard Worker // If `call` is true, also exercise the `callThrough()` method to increase hotness. 118*795d594fSAndroid Build Coastguard Worker // Ramp-up the number of calls we do up to 1 << 12. 119*795d594fSAndroid Build Coastguard Worker final int rampUpCutOff = 12; 120*795d594fSAndroid Build Coastguard Worker int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; 121*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < limit; ++i) { 122*795d594fSAndroid Build Coastguard Worker callThrough(Main.class, "doNothing"); 123*795d594fSAndroid Build Coastguard Worker } 124*795d594fSAndroid Build Coastguard Worker try { 125*795d594fSAndroid Build Coastguard Worker // Sleep to give a chance for the JIT to compile `callThrough` stub. 126*795d594fSAndroid Build Coastguard Worker // After the ramp-up phase, give the JIT even more time to compile. 127*795d594fSAndroid Build Coastguard Worker Thread.sleep(count >= rampUpCutOff ? 200 : 100); 128*795d594fSAndroid Build Coastguard Worker } catch (Exception e) { 129*795d594fSAndroid Build Coastguard Worker // Ignore 130*795d594fSAndroid Build Coastguard Worker } 131*795d594fSAndroid Build Coastguard Worker if (++count == 50) { 132*795d594fSAndroid Build Coastguard Worker throw new Error("TIMEOUT"); 133*795d594fSAndroid Build Coastguard Worker } 134*795d594fSAndroid Build Coastguard Worker } 135*795d594fSAndroid Build Coastguard Worker } 136*795d594fSAndroid Build Coastguard Worker assertTrue(boolean value)137*795d594fSAndroid Build Coastguard Worker public static void assertTrue(boolean value) { 138*795d594fSAndroid Build Coastguard Worker if (!value) { 139*795d594fSAndroid Build Coastguard Worker throw new AssertionError("Expected true!"); 140*795d594fSAndroid Build Coastguard Worker } 141*795d594fSAndroid Build Coastguard Worker } 142*795d594fSAndroid Build Coastguard Worker assertFalse(boolean value)143*795d594fSAndroid Build Coastguard Worker public static void assertFalse(boolean value) { 144*795d594fSAndroid Build Coastguard Worker if (value) { 145*795d594fSAndroid Build Coastguard Worker throw new AssertionError("Expected false!"); 146*795d594fSAndroid Build Coastguard Worker } 147*795d594fSAndroid Build Coastguard Worker } 148*795d594fSAndroid Build Coastguard Worker doNothing()149*795d594fSAndroid Build Coastguard Worker public static void doNothing() { } throwError()150*795d594fSAndroid Build Coastguard Worker public static void throwError() { throw new Error(); } 151*795d594fSAndroid Build Coastguard Worker 152*795d594fSAndroid Build Coastguard Worker // Note that the callThrough()'s shorty differs from shorties of the other native methods used 153*795d594fSAndroid Build Coastguard Worker // in this test (except deoptimizeNativeMethod) because of the return type `void.` callThrough(Class<?> cls, String methodName)154*795d594fSAndroid Build Coastguard Worker public native static void callThrough(Class<?> cls, String methodName); deoptimizeNativeMethod(Class<?> cls, String methodName)155*795d594fSAndroid Build Coastguard Worker public native static void deoptimizeNativeMethod(Class<?> cls, String methodName); 156*795d594fSAndroid Build Coastguard Worker jitGc()157*795d594fSAndroid Build Coastguard Worker public native static void jitGc(); isNextJitGcFull()158*795d594fSAndroid Build Coastguard Worker public native static boolean isNextJitGcFull(); 159*795d594fSAndroid Build Coastguard Worker isAotCompiled(Class<?> cls, String methodName)160*795d594fSAndroid Build Coastguard Worker public native static boolean isAotCompiled(Class<?> cls, String methodName); hasJitCompiledEntrypoint(Class<?> cls, String methodName)161*795d594fSAndroid Build Coastguard Worker public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName); hasJitCompiledCode(Class<?> cls, String methodName)162*795d594fSAndroid Build Coastguard Worker public native static boolean hasJitCompiledCode(Class<?> cls, String methodName); hasJit()163*795d594fSAndroid Build Coastguard Worker private native static boolean hasJit(); 164*795d594fSAndroid Build Coastguard Worker } 165