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