1 /* 2 * Copyright (C) 2019 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 package com.android.bootimageprofile; 18 19 import static org.junit.Assert.assertTrue; 20 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 23 import com.android.tradefed.testtype.IDeviceTest; 24 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 28 @RunWith(DeviceJUnit4ClassRunner.class) 29 public class BootImageProfileTest implements IDeviceTest { 30 private ITestDevice mTestDevice; 31 private static final String SYSTEM_SERVER_PROFILE = 32 "/data/misc/profiles/cur/0/android/primary.prof"; 33 private static final boolean USE_PHENOTYPE = false; 34 private static final String DALVIK_VM_EXTRA_OPTS = 35 "-Xusejit:false -Xint -Xjitsaveprofilinginfo"; 36 37 @Override setDevice(ITestDevice testDevice)38 public void setDevice(ITestDevice testDevice) { 39 mTestDevice = testDevice; 40 } 41 42 @Override getDevice()43 public ITestDevice getDevice() { 44 return mTestDevice; 45 } 46 getProperty(String property)47 private String getProperty(String property) throws Exception { 48 if (USE_PHENOTYPE) { 49 return mTestDevice.getProperty("persist.device_config.runtime_native_boot." 50 + property); 51 } else { 52 return mTestDevice.executeShellCommand("getprop dalvik.vm." + property).trim(); 53 } 54 } 55 setProperty(String property, String value)56 private String setProperty(String property, String value) throws Exception { 57 if (USE_PHENOTYPE) { 58 return mTestDevice.executeShellCommand( 59 String.format("device_config put runtime_native_boot %s '%s'", property, value)); 60 } else { 61 return mTestDevice.executeShellCommand( 62 String.format("setprop dalvik.vm.%s '%s'", property, value)); 63 } 64 } 65 66 /** 67 * Validate that the boot image profile properties are set. 68 */ validateProperties()69 public void validateProperties() throws Exception { 70 String res = getProperty("profilebootclasspath"); 71 assertTrue("profile boot class path not enabled: " + res, "true".equals(res)); 72 res = getProperty("profilesystemserver"); 73 assertTrue("profile system server not enabled: " + res, "true".equals(res)); 74 res = getProperty("extra-opts"); 75 assertTrue("extra options not set: " + res, DALVIK_VM_EXTRA_OPTS.equals(res)); 76 } 77 forceSaveProfile(String pkg)78 private boolean forceSaveProfile(String pkg) throws Exception { 79 String pid = mTestDevice.executeShellCommand("pidof " + pkg).trim(); 80 if (pid.length() == 0) { 81 // Not yet running. 82 return false; 83 } 84 String res = mTestDevice.executeShellCommand("kill -s SIGUSR1 " + pid).trim(); 85 return res.length() == 0; 86 } 87 88 @Test testSystemServerProfile()89 public void testSystemServerProfile() throws Exception { 90 final int numIterations = 30; 91 String res; 92 // Set properties and wait for them to be readable. 93 for (int i = 1; i <= numIterations; ++i) { 94 String pbcp = getProperty("profilebootclasspath"); 95 boolean profileBootClassPath = "true".equals(pbcp); 96 String pss = getProperty("profilesystemserver"); 97 boolean profileSystemServer = "true".equals(pss); 98 String extraOpts = getProperty("extra-opts"); 99 boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts); 100 if (profileBootClassPath && profileSystemServer && extraOptsOk) { 101 break; 102 } 103 if (i == numIterations) { 104 assertTrue("profile system server not enabled: " + pss, profileSystemServer); 105 assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath); 106 assertTrue("extra options not set: " + extraOpts, extraOptsOk); 107 } 108 109 setProperty("profilebootclasspath", "true"); 110 setProperty("profilesystemserver", "true"); 111 setProperty("extra-opts", DALVIK_VM_EXTRA_OPTS); 112 Thread.sleep(1000); 113 } 114 115 // Restart shell and wait for system boot. 116 res = mTestDevice.executeShellCommand("stop"); 117 assertTrue("stop shell: " + res, res.length() == 0); 118 res = mTestDevice.executeShellCommand("start"); 119 assertTrue("start shell: " + res, res.length() == 0); 120 for (int i = 1; i <= numIterations; ++i) { 121 String pbcp = getProperty("profilebootclasspath"); 122 boolean profileBootClassPath = "true".equals(pbcp); 123 String pss = getProperty("profilesystemserver"); 124 boolean profileSystemServer = "true".equals(pss); 125 String extraOpts = getProperty("extra-opts"); 126 boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts); 127 if (profileBootClassPath && profileSystemServer) { 128 break; 129 } 130 if (i == numIterations) { 131 assertTrue("profile system server not enabled: " + pss, profileSystemServer); 132 assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath); 133 assertTrue("extra options not set: " + extraOpts, extraOptsOk); 134 } 135 Thread.sleep(1000); 136 } 137 138 // Trunacte the profile before force it to be saved to prevent previous profiles 139 // causing the test to pass. 140 res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim(); 141 assertTrue(res, res.length() == 0); 142 // Wait up to 20 seconds for the profile to be saved. 143 for (int i = 1; i <= numIterations; ++i) { 144 // Force save the profile since we truncated it. 145 if (forceSaveProfile("system_server")) { 146 // Might fail if system server is not yet running. 147 String s = mTestDevice.executeShellCommand( 148 "wc -c <" + SYSTEM_SERVER_PROFILE).trim(); 149 if ("0".equals(s)) { 150 Thread.sleep(1000); 151 continue; 152 } 153 } 154 155 // In case the profile is partially saved, wait an extra second. 156 Thread.sleep(1000); 157 158 // Validate that properties are still set. 159 validateProperties(); 160 161 // Validate that the profile is non empty. 162 res = mTestDevice.executeShellCommand("profman --dump-only --profile-file=" 163 + SYSTEM_SERVER_PROFILE); 164 boolean sawFramework = false; 165 boolean sawServices = false; 166 for (String line : res.split("\n")) { 167 if (line.contains("framework.jar")) { 168 sawFramework = true; 169 } else if (line.contains("framework-minus-apex.jar")) { 170 sawFramework = true; 171 } else if (line.contains("services.jar")) { 172 sawServices = true; 173 } 174 } 175 if (i == numIterations) { 176 // Only assert for last iteration since there are race conditions where the package 177 // manager might not be started whewn the profile saves. 178 assertTrue("Did not see framework.jar in " + res, sawFramework); 179 assertTrue("Did not see services.jar in " + res, sawServices); 180 } 181 182 // Test the profile contents contain common methods for core-oj that would normally be 183 // AOT compiled. Also test that services.jar has PackageManagerService.<init> since the 184 // package manager service should always be created during boot. 185 res = mTestDevice.executeShellCommand( 186 "profman --dump-classes-and-methods --profile-file=" 187 + SYSTEM_SERVER_PROFILE + " --apk=/apex/com.android.art/javalib/core-oj.jar" 188 + " --apk=/system/framework/services.jar"); 189 boolean sawObjectInit = false; 190 boolean sawPmInit = false; 191 for (String line : res.split("\n")) { 192 if (line.contains("Ljava/lang/Object;-><init>()V")) { 193 sawObjectInit = true; 194 } else if (line.contains("Lcom/android/server/pm/PackageManagerService;-><init>")) { 195 sawPmInit = true; 196 } 197 } 198 if (i == numIterations) { 199 assertTrue("Did not see Object.<init> in " + res, sawObjectInit); 200 assertTrue("Did not see PackageManagerService.<init> in " + res, sawPmInit); 201 } 202 203 if (sawFramework && sawServices && sawObjectInit && sawPmInit) { 204 break; // Asserts passed, exit. 205 } 206 } 207 } 208 } 209