xref: /aosp_15_r20/art/test/2271-profile-inline-cache/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2023 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 import java.io.File;
18*795d594fSAndroid Build Coastguard Worker import java.io.IOException;
19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Method;
20*795d594fSAndroid Build Coastguard Worker import java.util.Arrays;
21*795d594fSAndroid Build Coastguard Worker import java.util.stream.Collectors;
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker public class Main {
24*795d594fSAndroid Build Coastguard Worker     private static File sFile = null;
25*795d594fSAndroid Build Coastguard Worker     private static Method sMethod1 = null;
26*795d594fSAndroid Build Coastguard Worker     private static Method sMethod2 = null;
27*795d594fSAndroid Build Coastguard Worker     private static Method sMethod3 = null;
28*795d594fSAndroid Build Coastguard Worker     private static Method sMethod4 = null;
29*795d594fSAndroid Build Coastguard Worker     private static Method sMethod5 = null;
30*795d594fSAndroid Build Coastguard Worker 
main(String[] args)31*795d594fSAndroid Build Coastguard Worker     public static void main(String[] args) throws Exception {
32*795d594fSAndroid Build Coastguard Worker         System.loadLibrary(args[0]);
33*795d594fSAndroid Build Coastguard Worker 
34*795d594fSAndroid Build Coastguard Worker         if (!hasJit()) {
35*795d594fSAndroid Build Coastguard Worker             // Test requires JIT for creating profiling infos.
36*795d594fSAndroid Build Coastguard Worker             return;
37*795d594fSAndroid Build Coastguard Worker         }
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker         sMethod1 = Main.class.getDeclaredMethod("$noinline$method1", Base.class);
40*795d594fSAndroid Build Coastguard Worker         sMethod2 = Main.class.getDeclaredMethod("$noinline$method2", Base.class);
41*795d594fSAndroid Build Coastguard Worker         sMethod3 = Main.class.getDeclaredMethod("$noinline$method3", Base.class);
42*795d594fSAndroid Build Coastguard Worker         sMethod4 = Main.class.getDeclaredMethod("$noinline$method4", Base.class);
43*795d594fSAndroid Build Coastguard Worker         sMethod5 = Main.class.getDeclaredMethod("$noinline$method5", Base.class);
44*795d594fSAndroid Build Coastguard Worker 
45*795d594fSAndroid Build Coastguard Worker         sFile = createTempFile();
46*795d594fSAndroid Build Coastguard Worker         sFile.deleteOnExit();
47*795d594fSAndroid Build Coastguard Worker         String codePath = System.getenv("DEX_LOCATION") + "/2271-profile-inline-cache.jar";
48*795d594fSAndroid Build Coastguard Worker         VMRuntime.registerAppInfo("test.app", sFile.getPath(), sFile.getPath(),
49*795d594fSAndroid Build Coastguard Worker                 new String[] {codePath}, VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
50*795d594fSAndroid Build Coastguard Worker 
51*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < 10; i++) {
52*795d594fSAndroid Build Coastguard Worker             try {
53*795d594fSAndroid Build Coastguard Worker                 test();
54*795d594fSAndroid Build Coastguard Worker                 return;
55*795d594fSAndroid Build Coastguard Worker             } catch (ScopedAssertNoGc.NoGcAssertionFailure e) {
56*795d594fSAndroid Build Coastguard Worker                 // This should rarely happen. When it happens, reset the state, delete the profile,
57*795d594fSAndroid Build Coastguard Worker                 // and try again.
58*795d594fSAndroid Build Coastguard Worker                 reset();
59*795d594fSAndroid Build Coastguard Worker                 sFile.delete();
60*795d594fSAndroid Build Coastguard Worker             }
61*795d594fSAndroid Build Coastguard Worker         }
62*795d594fSAndroid Build Coastguard Worker 
63*795d594fSAndroid Build Coastguard Worker         // The possibility of hitting this line only exists in theory, unless the test is wrong.
64*795d594fSAndroid Build Coastguard Worker         throw new RuntimeException("NoGcAssertionFailure occurred 10 times");
65*795d594fSAndroid Build Coastguard Worker     }
66*795d594fSAndroid Build Coastguard Worker 
test()67*795d594fSAndroid Build Coastguard Worker     private static void test() throws Exception {
68*795d594fSAndroid Build Coastguard Worker         Derived1 derived1 = new Derived1();
69*795d594fSAndroid Build Coastguard Worker         Derived2 derived2 = new Derived2();
70*795d594fSAndroid Build Coastguard Worker 
71*795d594fSAndroid Build Coastguard Worker         // This method is below the inline cache threshold.
72*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(sMethod1);
73*795d594fSAndroid Build Coastguard Worker         try (ScopedAssertNoGc noGc = new ScopedAssertNoGc()) {
74*795d594fSAndroid Build Coastguard Worker             $noinline$method1(derived1);
75*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < 2998; i++) {
76*795d594fSAndroid Build Coastguard Worker                 $noinline$method1(derived2);
77*795d594fSAndroid Build Coastguard Worker             }
78*795d594fSAndroid Build Coastguard Worker         }
79*795d594fSAndroid Build Coastguard Worker         checkMethodHasNoInlineCache(sFile, sMethod1);
80*795d594fSAndroid Build Coastguard Worker 
81*795d594fSAndroid Build Coastguard Worker         // This method is right on the inline cache threshold.
82*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(sMethod2);
83*795d594fSAndroid Build Coastguard Worker         try (ScopedAssertNoGc noGc = new ScopedAssertNoGc()) {
84*795d594fSAndroid Build Coastguard Worker             $noinline$method2(derived1);
85*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < 2999; i++) {
86*795d594fSAndroid Build Coastguard Worker                 $noinline$method2(derived2);
87*795d594fSAndroid Build Coastguard Worker             }
88*795d594fSAndroid Build Coastguard Worker         }
89*795d594fSAndroid Build Coastguard Worker         checkMethodHasInlineCache(sFile, sMethod2, Derived1.class, Derived2.class);
90*795d594fSAndroid Build Coastguard Worker 
91*795d594fSAndroid Build Coastguard Worker         // This method is above the inline cache threshold.
92*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(sMethod3);
93*795d594fSAndroid Build Coastguard Worker         try (ScopedAssertNoGc noGc = new ScopedAssertNoGc()) {
94*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < 10000; i++) {
95*795d594fSAndroid Build Coastguard Worker                 $noinline$method3(derived1);
96*795d594fSAndroid Build Coastguard Worker             }
97*795d594fSAndroid Build Coastguard Worker             for (int i = 0; i < 10000; i++) {
98*795d594fSAndroid Build Coastguard Worker                 $noinline$method3(derived2);
99*795d594fSAndroid Build Coastguard Worker             }
100*795d594fSAndroid Build Coastguard Worker         }
101*795d594fSAndroid Build Coastguard Worker         checkMethodHasInlineCache(sFile, sMethod3, Derived1.class, Derived2.class);
102*795d594fSAndroid Build Coastguard Worker 
103*795d594fSAndroid Build Coastguard Worker         // This method is above the JIT threshold.
104*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(sMethod4);
105*795d594fSAndroid Build Coastguard Worker         try (ScopedAssertNoGc noGc = new ScopedAssertNoGc()) {
106*795d594fSAndroid Build Coastguard Worker             $noinline$method4(derived1);
107*795d594fSAndroid Build Coastguard Worker             $noinline$method4(derived2);
108*795d594fSAndroid Build Coastguard Worker         }
109*795d594fSAndroid Build Coastguard Worker         ensureMethodJitCompiled(sMethod4);
110*795d594fSAndroid Build Coastguard Worker         checkMethodHasInlineCache(sFile, sMethod4, Derived1.class, Derived2.class);
111*795d594fSAndroid Build Coastguard Worker 
112*795d594fSAndroid Build Coastguard Worker         // This method is above the JIT threshold.
113*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(sMethod5);
114*795d594fSAndroid Build Coastguard Worker         try (ScopedAssertNoGc noGc = new ScopedAssertNoGc()) {
115*795d594fSAndroid Build Coastguard Worker             $noinline$method5(derived1);
116*795d594fSAndroid Build Coastguard Worker             $noinline$method5(derived2);
117*795d594fSAndroid Build Coastguard Worker         }
118*795d594fSAndroid Build Coastguard Worker         ensureMethodJitCompiled(sMethod5);
119*795d594fSAndroid Build Coastguard Worker         // We currently do not encode inlined inline caches.
120*795d594fSAndroid Build Coastguard Worker         checkMethodHasNoInlineCache(sFile, sMethod5);
121*795d594fSAndroid Build Coastguard Worker     }
122*795d594fSAndroid Build Coastguard Worker 
reset()123*795d594fSAndroid Build Coastguard Worker     private static void reset() {
124*795d594fSAndroid Build Coastguard Worker         removeJitCompiledMethod(sMethod1, false /* releaseMemory */);
125*795d594fSAndroid Build Coastguard Worker         removeJitCompiledMethod(sMethod2, false /* releaseMemory */);
126*795d594fSAndroid Build Coastguard Worker         removeJitCompiledMethod(sMethod3, false /* releaseMemory */);
127*795d594fSAndroid Build Coastguard Worker         removeJitCompiledMethod(sMethod4, false /* releaseMemory */);
128*795d594fSAndroid Build Coastguard Worker     }
129*795d594fSAndroid Build Coastguard Worker 
$noinline$method1(Base obj)130*795d594fSAndroid Build Coastguard Worker     public static void $noinline$method1(Base obj) {
131*795d594fSAndroid Build Coastguard Worker         obj.f();
132*795d594fSAndroid Build Coastguard Worker     }
133*795d594fSAndroid Build Coastguard Worker 
$noinline$method2(Base obj)134*795d594fSAndroid Build Coastguard Worker     public static void $noinline$method2(Base obj) {
135*795d594fSAndroid Build Coastguard Worker         obj.f();
136*795d594fSAndroid Build Coastguard Worker     }
137*795d594fSAndroid Build Coastguard Worker 
$noinline$method3(Base obj)138*795d594fSAndroid Build Coastguard Worker     public static void $noinline$method3(Base obj) {
139*795d594fSAndroid Build Coastguard Worker         obj.f();
140*795d594fSAndroid Build Coastguard Worker     }
141*795d594fSAndroid Build Coastguard Worker 
$noinline$method4(Base obj)142*795d594fSAndroid Build Coastguard Worker     public static void $noinline$method4(Base obj) {
143*795d594fSAndroid Build Coastguard Worker         obj.f();
144*795d594fSAndroid Build Coastguard Worker     }
145*795d594fSAndroid Build Coastguard Worker 
$noinline$method5(Base obj)146*795d594fSAndroid Build Coastguard Worker     public static void $noinline$method5(Base obj) {
147*795d594fSAndroid Build Coastguard Worker         $inline$callF(obj);
148*795d594fSAndroid Build Coastguard Worker     }
149*795d594fSAndroid Build Coastguard Worker 
$inline$callF(Base obj)150*795d594fSAndroid Build Coastguard Worker     public static void $inline$callF(Base obj) {
151*795d594fSAndroid Build Coastguard Worker       obj.f();
152*795d594fSAndroid Build Coastguard Worker     }
153*795d594fSAndroid Build Coastguard Worker 
154*795d594fSAndroid Build Coastguard Worker     public static class Base {
f()155*795d594fSAndroid Build Coastguard Worker         public void f() {}
156*795d594fSAndroid Build Coastguard Worker     }
157*795d594fSAndroid Build Coastguard Worker 
158*795d594fSAndroid Build Coastguard Worker     public static class Derived1 extends Base {
159*795d594fSAndroid Build Coastguard Worker         @Override
f()160*795d594fSAndroid Build Coastguard Worker         public void f() {}
161*795d594fSAndroid Build Coastguard Worker     }
162*795d594fSAndroid Build Coastguard Worker 
163*795d594fSAndroid Build Coastguard Worker     public static class Derived2 extends Base {
164*795d594fSAndroid Build Coastguard Worker         @Override
f()165*795d594fSAndroid Build Coastguard Worker         public void f() {}
166*795d594fSAndroid Build Coastguard Worker     }
167*795d594fSAndroid Build Coastguard Worker 
checkMethodHasInlineCache(File file, Method m, Class<?>... targetTypes)168*795d594fSAndroid Build Coastguard Worker     private static void checkMethodHasInlineCache(File file, Method m, Class<?>... targetTypes) {
169*795d594fSAndroid Build Coastguard Worker         ensureProfileProcessing();
170*795d594fSAndroid Build Coastguard Worker         if (!hasInlineCacheInProfile(file.getPath(), m, targetTypes)) {
171*795d594fSAndroid Build Coastguard Worker             throw new RuntimeException("Expected method " + m
172*795d594fSAndroid Build Coastguard Worker                     + " to have inline cache in the profile with target types "
173*795d594fSAndroid Build Coastguard Worker                     + Arrays.stream(targetTypes)
174*795d594fSAndroid Build Coastguard Worker                               .map(Class::getName)
175*795d594fSAndroid Build Coastguard Worker                               .collect(Collectors.joining(", ")));
176*795d594fSAndroid Build Coastguard Worker         }
177*795d594fSAndroid Build Coastguard Worker     }
178*795d594fSAndroid Build Coastguard Worker 
checkMethodHasNoInlineCache(File file, Method m)179*795d594fSAndroid Build Coastguard Worker     private static void checkMethodHasNoInlineCache(File file, Method m) {
180*795d594fSAndroid Build Coastguard Worker         ensureProfileProcessing();
181*795d594fSAndroid Build Coastguard Worker         if (hasInlineCacheInProfile(file.getPath(), m)) {
182*795d594fSAndroid Build Coastguard Worker             throw new RuntimeException(
183*795d594fSAndroid Build Coastguard Worker                     "Expected method " + m + " not to have inline cache in the profile");
184*795d594fSAndroid Build Coastguard Worker         }
185*795d594fSAndroid Build Coastguard Worker     }
186*795d594fSAndroid Build Coastguard Worker 
ensureJitBaselineCompiled(Method method)187*795d594fSAndroid Build Coastguard Worker     public static void ensureJitBaselineCompiled(Method method) {
188*795d594fSAndroid Build Coastguard Worker         ensureJitBaselineCompiled(method.getDeclaringClass(), method.getName());
189*795d594fSAndroid Build Coastguard Worker     }
ensureMethodJitCompiled(Method method)190*795d594fSAndroid Build Coastguard Worker     public static native void ensureMethodJitCompiled(Method method);
ensureJitBaselineCompiled(Class<?> cls, String methodName)191*795d594fSAndroid Build Coastguard Worker     public static native void ensureJitBaselineCompiled(Class<?> cls, String methodName);
ensureProfileProcessing()192*795d594fSAndroid Build Coastguard Worker     public static native void ensureProfileProcessing();
hasInlineCacheInProfile( String profile, Method method, Class<?>... targetTypes)193*795d594fSAndroid Build Coastguard Worker     public static native boolean hasInlineCacheInProfile(
194*795d594fSAndroid Build Coastguard Worker             String profile, Method method, Class<?>... targetTypes);
hasJit()195*795d594fSAndroid Build Coastguard Worker     public static native boolean hasJit();
getCurrentGcNum()196*795d594fSAndroid Build Coastguard Worker     public static native int getCurrentGcNum();
removeJitCompiledMethod(Method method, boolean releaseMemory)197*795d594fSAndroid Build Coastguard Worker     public static native boolean removeJitCompiledMethod(Method method, boolean releaseMemory);
198*795d594fSAndroid Build Coastguard Worker 
199*795d594fSAndroid Build Coastguard Worker     private static final String TEMP_FILE_NAME_PREFIX = "temp";
200*795d594fSAndroid Build Coastguard Worker     private static final String TEMP_FILE_NAME_SUFFIX = "-file";
201*795d594fSAndroid Build Coastguard Worker 
createTempFile()202*795d594fSAndroid Build Coastguard Worker     private static File createTempFile() throws Exception {
203*795d594fSAndroid Build Coastguard Worker         try {
204*795d594fSAndroid Build Coastguard Worker             return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
205*795d594fSAndroid Build Coastguard Worker         } catch (IOException e) {
206*795d594fSAndroid Build Coastguard Worker             System.setProperty("java.io.tmpdir", "/data/local/tmp");
207*795d594fSAndroid Build Coastguard Worker             try {
208*795d594fSAndroid Build Coastguard Worker                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
209*795d594fSAndroid Build Coastguard Worker             } catch (IOException e2) {
210*795d594fSAndroid Build Coastguard Worker                 System.setProperty("java.io.tmpdir", "/sdcard");
211*795d594fSAndroid Build Coastguard Worker                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
212*795d594fSAndroid Build Coastguard Worker             }
213*795d594fSAndroid Build Coastguard Worker         }
214*795d594fSAndroid Build Coastguard Worker     }
215*795d594fSAndroid Build Coastguard Worker 
216*795d594fSAndroid Build Coastguard Worker     private static class VMRuntime {
217*795d594fSAndroid Build Coastguard Worker         public static final int CODE_PATH_TYPE_PRIMARY_APK = 1 << 0;
218*795d594fSAndroid Build Coastguard Worker         private static final Method registerAppInfoMethod;
219*795d594fSAndroid Build Coastguard Worker 
220*795d594fSAndroid Build Coastguard Worker         static {
221*795d594fSAndroid Build Coastguard Worker             try {
222*795d594fSAndroid Build Coastguard Worker                 Class<? extends Object> c = Class.forName("dalvik.system.VMRuntime");
223*795d594fSAndroid Build Coastguard Worker                 registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", String.class,
224*795d594fSAndroid Build Coastguard Worker                         String.class, String.class, String[].class, int.class);
225*795d594fSAndroid Build Coastguard Worker             } catch (Exception e) {
226*795d594fSAndroid Build Coastguard Worker                 throw new RuntimeException(e);
227*795d594fSAndroid Build Coastguard Worker             }
228*795d594fSAndroid Build Coastguard Worker         }
229*795d594fSAndroid Build Coastguard Worker 
registerAppInfo(String packageName, String curProfile, String refProfile, String[] codePaths, int codePathsType)230*795d594fSAndroid Build Coastguard Worker         public static void registerAppInfo(String packageName, String curProfile, String refProfile,
231*795d594fSAndroid Build Coastguard Worker                 String[] codePaths, int codePathsType) throws Exception {
232*795d594fSAndroid Build Coastguard Worker             registerAppInfoMethod.invoke(
233*795d594fSAndroid Build Coastguard Worker                     null, packageName, curProfile, refProfile, codePaths, codePathsType);
234*795d594fSAndroid Build Coastguard Worker         }
235*795d594fSAndroid Build Coastguard Worker     }
236*795d594fSAndroid Build Coastguard Worker 
237*795d594fSAndroid Build Coastguard Worker     // This scope is intended to guard code that doesn't expect GC to take place. Because we can't
238*795d594fSAndroid Build Coastguard Worker     // really prevent GC in Java code (calling a native method that enters a GCCriticalSection will
239*795d594fSAndroid Build Coastguard Worker     // cause the runtime to hang forever when transitioning from native back to Java), this is a
240*795d594fSAndroid Build Coastguard Worker     // workaround that forces a GC at the beginning so that GC will unlikely take place within the
241*795d594fSAndroid Build Coastguard Worker     // scope. If a GC still takes place within the scope, this will throw NoGcAssertionFailure.
242*795d594fSAndroid Build Coastguard Worker     //
243*795d594fSAndroid Build Coastguard Worker     // The baseline code doesn't update the inline cache if we are marking, so we use this scope to
244*795d594fSAndroid Build Coastguard Worker     // guard calls to virtual methods for which we want inline cache to be updated.
245*795d594fSAndroid Build Coastguard Worker     private static class ScopedAssertNoGc implements AutoCloseable {
246*795d594fSAndroid Build Coastguard Worker         private final int mLastGcNum;
247*795d594fSAndroid Build Coastguard Worker 
ScopedAssertNoGc()248*795d594fSAndroid Build Coastguard Worker         public ScopedAssertNoGc() {
249*795d594fSAndroid Build Coastguard Worker             System.gc();
250*795d594fSAndroid Build Coastguard Worker             mLastGcNum = getCurrentGcNum();
251*795d594fSAndroid Build Coastguard Worker         }
252*795d594fSAndroid Build Coastguard Worker 
253*795d594fSAndroid Build Coastguard Worker         @Override
close()254*795d594fSAndroid Build Coastguard Worker         public void close() throws NoGcAssertionFailure {
255*795d594fSAndroid Build Coastguard Worker             int currentGcNum = getCurrentGcNum();
256*795d594fSAndroid Build Coastguard Worker             if (currentGcNum != mLastGcNum) {
257*795d594fSAndroid Build Coastguard Worker                 throw new NoGcAssertionFailure(
258*795d594fSAndroid Build Coastguard Worker                         String.format("GC happened within the scope (before: %d, after: %d)",
259*795d594fSAndroid Build Coastguard Worker                                 mLastGcNum, currentGcNum));
260*795d594fSAndroid Build Coastguard Worker             }
261*795d594fSAndroid Build Coastguard Worker         }
262*795d594fSAndroid Build Coastguard Worker 
263*795d594fSAndroid Build Coastguard Worker         public static class NoGcAssertionFailure extends Exception {
NoGcAssertionFailure(String message)264*795d594fSAndroid Build Coastguard Worker             public NoGcAssertionFailure(String message) {
265*795d594fSAndroid Build Coastguard Worker                 super(message);
266*795d594fSAndroid Build Coastguard Worker             }
267*795d594fSAndroid Build Coastguard Worker         }
268*795d594fSAndroid Build Coastguard Worker     }
269*795d594fSAndroid Build Coastguard Worker }
270