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