/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.sustainedPerformance.cts; import com.android.compatibility.common.util.CddTest; import com.android.ddmlib.IShellOutputReceiver; import com.android.ddmlib.Log; import com.android.ddmlib.MultiLineReceiver; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceTestCase; import java.util.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; import java.util.concurrent.TimeUnit; /** * Test to check if device implements Sustained Performance Mode */ @CddTest(requirement="8.5/C-0-1,C-1-1,C-1-2") public class SustainedPerformanceHostTest extends DeviceTestCase { ITestDevice device; private static final String PACKAGE = "com.android.gputest"; private static final String CLASS = "GPUStressTestActivity"; private static final String START_COMMAND = String.format( "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS); private static final String START_COMMAND_MODE = String.format( "am start -W -a android.intent.action.MAIN -n %s/%s.%s --ez SustainedPerformanceMode true", PACKAGE, PACKAGE, CLASS); private static final String STOP_COMMAND = String.format( "am force-stop %s", PACKAGE); private static final String TEST_PACKAGE = "android.test.app"; private static final String TEST_CLASS = "DeviceTestActivity"; private static final String START_TEST_COMMAND = String.format( "am start -W -a android.intent.action.MAIN -n %s/%s.%s", TEST_PACKAGE, TEST_PACKAGE, TEST_CLASS); private static final String DHRYSTONE = "/data/local/tmp/"; private static final String LOG_TAG = "sustainedPerfTest"; private static ArrayList appResultsWithMode = new ArrayList(); private static ArrayList appResultsWithoutMode = new ArrayList(); private static ArrayList dhrystoneResultsWithMode = new ArrayList(); private static ArrayList dhrystoneResultsWithoutMode = new ArrayList(); private double dhryMin = Double.MAX_VALUE, dhryMax = Double.MIN_VALUE; private static long testDuration = 1800000; //30 minutes public class Dhrystone implements Runnable { private boolean modeEnabled; private long startTime; private long loopCount = 300000000; private long cpumask = 1; public Dhrystone(boolean enabled, long cm) { cpumask = cm; modeEnabled = enabled; startTime = System.currentTimeMillis(); } public void run() { double[] testSet = new double[3]; int index = 0; try { device.executeShellCommand("cd " + DHRYSTONE + " ; chmod 777 dhry"); while (true) { String result = device.executeShellCommand("echo " + loopCount + " | taskset -a " + cpumask + " " + DHRYSTONE + "dhry"); if (Math.abs(System.currentTimeMillis() - startTime) >= testDuration) { break; } else if (result.contains("Measured time too small")) { loopCount = loopCount*10; } else if (!result.isEmpty()){ double dmips = Double.parseDouble(result); testSet[index++] = dmips; if (index == 3) { synchronized(this) { if (modeEnabled) { dhrystoneResultsWithMode.add(testSet[1]); } else { dhrystoneResultsWithoutMode.add(testSet[1]); } if (testSet[1] > dhryMax) { dhryMax = testSet[1]; } if (testSet[1] < dhryMin) { dhryMin = testSet[1]; } index = 0; } } } } } catch (Exception e) { Log.e(LOG_TAG, e.toString()); } } } public void analyzeResults(String logs, boolean mode) { Double[] testSet = new Double[10]; int index = 0; double min = Double.MAX_VALUE, max = Double.MIN_VALUE; boolean first = true; Scanner in = new Scanner(logs); while (in.hasNextLine()) { String line = in.nextLine(); if(line.startsWith("I/"+CLASS)) { Double time = Double.parseDouble(line.split(":")[1]); testSet[index++] = time; if (index == 10) { if (first) { first = false; index = 0; continue; } Arrays.sort(testSet); if (mode) { appResultsWithMode.add(testSet[5]); } else { appResultsWithoutMode.add(testSet[5]); } if (testSet[5] > max) { max = testSet[5]; } if (testSet[5] < min) { min = testSet[5]; } index = 0; } } } in.close(); double diff = (max - min)*100/max; if (mode) { appResultsWithMode.add(0, min); appResultsWithMode.add(1, max); appResultsWithMode.add(2, diff); } else { appResultsWithoutMode.add(0, min); appResultsWithoutMode.add(1, max); appResultsWithoutMode.add(2, diff); } } private void setUpEnvironment() throws Exception { dhryMin = Double.MAX_VALUE; dhryMax = Double.MIN_VALUE; Thread.sleep(600000); device.executeAdbCommand("logcat", "-c"); device.executeShellCommand("settings put global airplane_mode_on 1"); device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true"); } public void testShader() throws Exception { device = getDevice(); /** * Check if the device supports Sustained Performance Mode. * If not then assert true and return. **/ device.executeAdbCommand("logcat", "-c"); device.executeShellCommand(START_TEST_COMMAND); String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", TEST_CLASS + ":I", "*:S"); String testString = ""; Scanner in = new Scanner(logs); while (in.hasNextLine()) { String line = in.nextLine(); if(line.startsWith("I/"+TEST_CLASS)) { testString = line.split(":")[1].trim(); } } in.close(); if (testString.isEmpty()) { assertTrue(true); return; } appResultsWithoutMode.clear(); appResultsWithMode.clear(); dhrystoneResultsWithoutMode.clear(); dhrystoneResultsWithMode.clear(); /* * Run the test with the mode. * Start the application and collect stats. * Run two threads of dhrystone and collect stats. */ setUpEnvironment(); device.executeShellCommand(START_COMMAND_MODE); Thread dhrystone = new Thread(new Dhrystone(true, 1)); Thread dhrystone1 = new Thread(new Dhrystone(true, 2)); dhrystone.start(); dhrystone1.start(); Thread.sleep(testDuration); device.executeShellCommand(STOP_COMMAND); dhrystone.join(); dhrystone1.join(); logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S"); analyzeResults(logs, true); double diff = (dhryMax - dhryMin)*100/dhryMax; dhrystoneResultsWithMode.add(0, dhryMin); dhrystoneResultsWithMode.add(1, dhryMax); dhrystoneResultsWithMode.add(2, diff); device.executeShellCommand("settings put global airplane_mode_on 0"); device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false"); double resDhry = dhrystoneResultsWithMode.get(2); double resApp = appResultsWithMode.get(2); /* Report if performance is below 5% margin for both dhrystone and shader */ if ((resDhry > 5) || (resApp > 5)) { Log.w("SustainedPerformanceHostTests", "Sustainable mode results, Dhrystone: " + resDhry + " App: " + resApp); } /* * Error if the performance in the mode is not consistent with * 5% error margin for shader and 10% error margin for dhrystone. */ assertFalse("Results in the mode are not sustainable", (resDhry > 15) || (resApp > 5)); } }