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