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 
Dhrystone(boolean enabled, long cm)68         public Dhrystone(boolean enabled, long cm) {
69             cpumask = cm;
70             modeEnabled = enabled;
71             startTime = System.currentTimeMillis();
72         }
73 
run()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 
analyzeResults(String logs, boolean mode)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 
setUpEnvironment()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 
testShader()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