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