1 /* 2 * Copyright (C) 2016 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 android.sustainedPerformance.cts; 18 19 import com.android.tradefed.util.RunUtil; 20 import com.android.compatibility.common.util.CddTest; 21 import com.android.ddmlib.IShellOutputReceiver; 22 import com.android.ddmlib.Log; 23 import com.android.ddmlib.MultiLineReceiver; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.testtype.DeviceTestCase; 26 import java.util.*; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Scanner; 30 import java.util.concurrent.TimeUnit; 31 /** 32 * Test to check if device implements Sustained Performance Mode 33 */ 34 @CddTest(requirement="8.5/C-0-1,C-1-1,C-1-2") 35 public class SustainedPerformanceHostTest extends DeviceTestCase { 36 37 ITestDevice device; 38 private static final String PACKAGE = "com.android.gputest"; 39 private static final String CLASS = "GPUStressTestActivity"; 40 private static final String START_COMMAND = String.format( 41 "am start -W -a android.intent.action.MAIN -n %s/%s.%s", 42 PACKAGE, PACKAGE, CLASS); 43 private static final String START_COMMAND_MODE = String.format( 44 "am start -W -a android.intent.action.MAIN -n %s/%s.%s --ez SustainedPerformanceMode true", 45 PACKAGE, PACKAGE, CLASS); 46 private static final String STOP_COMMAND = String.format( 47 "am force-stop %s", PACKAGE); 48 private static final String TEST_PACKAGE = "android.test.app"; 49 private static final String TEST_CLASS = "DeviceTestActivity"; 50 private static final String START_TEST_COMMAND = String.format( 51 "am start -W -a android.intent.action.MAIN -n %s/%s.%s", 52 TEST_PACKAGE, TEST_PACKAGE, TEST_CLASS); 53 private static final String DHRYSTONE = "/data/local/tmp/"; 54 private static final String LOG_TAG = "sustainedPerfTest"; 55 56 private static ArrayList<Double> appResultsWithMode = new ArrayList<Double>(); 57 private static ArrayList<Double> appResultsWithoutMode = new ArrayList<Double>(); 58 private static ArrayList<Double> dhrystoneResultsWithMode = new ArrayList<Double>(); 59 private static ArrayList<Double> dhrystoneResultsWithoutMode = new ArrayList<Double>(); 60 private double dhryMin = Double.MAX_VALUE, dhryMax = Double.MIN_VALUE; 61 private static long testDuration = 1800000; //30 minutes 62 63 public class Dhrystone implements Runnable { 64 private boolean modeEnabled; 65 private long startTime; 66 private long loopCount = 300000000; 67 private long cpumask = 1; 68 Dhrystone(boolean enabled, long cm)69 public Dhrystone(boolean enabled, long cm) { 70 cpumask = cm; 71 modeEnabled = enabled; 72 startTime = System.currentTimeMillis(); 73 } 74 run()75 public void run() { 76 double[] testSet = new double[3]; 77 int index = 0; 78 try { 79 device.executeShellCommand("cd " + DHRYSTONE + " ; chmod 777 dhry"); 80 while (true) { 81 String result = device.executeShellCommand("echo " + loopCount 82 + " | taskset -a " + cpumask + " " + DHRYSTONE + "dhry"); 83 if (Math.abs(System.currentTimeMillis() - startTime) >= testDuration) { 84 break; 85 } else if (result.contains("Measured time too small")) { 86 loopCount = loopCount*10; 87 } else if (!result.isEmpty()){ 88 double dmips = Double.parseDouble(result); 89 testSet[index++] = dmips; 90 if (index == 3) { 91 synchronized(this) { 92 if (modeEnabled) { 93 dhrystoneResultsWithMode.add(testSet[1]); 94 } else { 95 dhrystoneResultsWithoutMode.add(testSet[1]); 96 } 97 if (testSet[1] > dhryMax) { 98 dhryMax = testSet[1]; 99 } 100 if (testSet[1] < dhryMin) { 101 dhryMin = testSet[1]; 102 } 103 index = 0; 104 } 105 } 106 } 107 } 108 } catch (Exception e) { 109 Log.e(LOG_TAG, e.toString()); 110 111 } 112 } 113 } 114 analyzeResults(String logs, boolean mode)115 public void analyzeResults(String logs, boolean mode) { 116 Double[] testSet = new Double[10]; 117 int index = 0; 118 double min = Double.MAX_VALUE, max = Double.MIN_VALUE; 119 boolean first = true; 120 121 Scanner in = new Scanner(logs); 122 while (in.hasNextLine()) { 123 String line = in.nextLine(); 124 if(line.startsWith("I/"+CLASS)) { 125 Double time = Double.parseDouble(line.split(":")[1]); 126 testSet[index++] = time; 127 if (index == 10) { 128 if (first) { 129 first = false; 130 index = 0; 131 continue; 132 } 133 Arrays.sort(testSet); 134 if (mode) { 135 appResultsWithMode.add(testSet[5]); 136 } else { 137 appResultsWithoutMode.add(testSet[5]); 138 } 139 if (testSet[5] > max) { 140 max = testSet[5]; 141 } 142 if (testSet[5] < min) { 143 min = testSet[5]; 144 } 145 index = 0; 146 } 147 } 148 } 149 in.close(); 150 double diff = (max - min)*100/max; 151 if (mode) { 152 appResultsWithMode.add(0, min); 153 appResultsWithMode.add(1, max); 154 appResultsWithMode.add(2, diff); 155 } else { 156 appResultsWithoutMode.add(0, min); 157 appResultsWithoutMode.add(1, max); 158 appResultsWithoutMode.add(2, diff); 159 } 160 } 161 setUpEnvironment()162 private void setUpEnvironment() throws Exception { 163 dhryMin = Double.MAX_VALUE; 164 dhryMax = Double.MIN_VALUE; 165 RunUtil.getDefault().sleep(600000); 166 device.executeAdbCommand("logcat", "-c"); 167 device.executeShellCommand("settings put global airplane_mode_on 1"); 168 device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true"); 169 } 170 testShader()171 public void testShader() throws Exception { 172 device = getDevice(); 173 174 /** 175 * Check if the device supports Sustained Performance Mode. 176 * If not then assert true and return. 177 **/ 178 device.executeAdbCommand("logcat", "-c"); 179 device.executeShellCommand(START_TEST_COMMAND); 180 String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", TEST_CLASS + ":I", "*:S"); 181 String testString = ""; 182 Scanner in = new Scanner(logs); 183 while (in.hasNextLine()) { 184 String line = in.nextLine(); 185 if(line.startsWith("I/"+TEST_CLASS)) { 186 testString = line.split(":")[1].trim(); 187 } 188 } 189 in.close(); 190 if (testString.isEmpty()) { 191 assertTrue(true); 192 return; 193 } 194 195 appResultsWithoutMode.clear(); 196 appResultsWithMode.clear(); 197 dhrystoneResultsWithoutMode.clear(); 198 dhrystoneResultsWithMode.clear(); 199 200 /* 201 * Run the test with the mode. 202 * Start the application and collect stats. 203 * Run two threads of dhrystone and collect stats. 204 */ 205 setUpEnvironment(); 206 device.executeShellCommand(START_COMMAND_MODE); 207 Thread dhrystone = new Thread(new Dhrystone(true, 1)); 208 Thread dhrystone1 = new Thread(new Dhrystone(true, 2)); 209 dhrystone.start(); 210 dhrystone1.start(); 211 RunUtil.getDefault().sleep(testDuration); 212 device.executeShellCommand(STOP_COMMAND); 213 dhrystone.join(); 214 dhrystone1.join(); 215 logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S"); 216 analyzeResults(logs, true); 217 double diff = (dhryMax - dhryMin)*100/dhryMax; 218 dhrystoneResultsWithMode.add(0, dhryMin); 219 dhrystoneResultsWithMode.add(1, dhryMax); 220 dhrystoneResultsWithMode.add(2, diff); 221 222 device.executeShellCommand("settings put global airplane_mode_on 0"); 223 device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false"); 224 225 double resDhry = dhrystoneResultsWithMode.get(2); 226 double resApp = appResultsWithMode.get(2); 227 228 /* Report if performance is below 5% margin for both dhrystone and shader */ 229 if ((resDhry > 5) || (resApp > 5)) { 230 Log.w("SustainedPerformanceHostTests", 231 "Sustainable mode results, Dhrystone: " + resDhry + " App: " + resApp); 232 } 233 234 /* 235 * Error if the performance in the mode is not consistent with 236 * 5% error margin for shader and 10% error margin for dhrystone. 237 */ 238 assertFalse("Results in the mode are not sustainable", 239 (resDhry > 15) || 240 (resApp > 5)); 241 } 242 } 243