• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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