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