1 /* 2 * Copyright (C) 2011 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 com.android.media.tests; 18 19 import com.android.ddmlib.IDevice; 20 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; 21 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.log.LogUtil.CLog; 26 import com.android.tradefed.result.BugreportCollector; 27 import com.android.tradefed.result.BugreportCollector.Freq; 28 import com.android.tradefed.result.BugreportCollector.Noun; 29 import com.android.tradefed.result.BugreportCollector.Relation; 30 import com.android.tradefed.result.FileInputStreamSource; 31 import com.android.tradefed.result.ITestInvocationListener; 32 import com.android.tradefed.result.InputStreamSource; 33 import com.android.tradefed.result.LogDataType; 34 import com.android.tradefed.testtype.IDeviceTest; 35 import com.android.tradefed.testtype.IRemoteTest; 36 import com.android.tradefed.util.FileUtil; 37 import com.android.tradefed.util.StreamUtil; 38 import com.android.tradefed.util.proto.TfMetricProtoUtil; 39 40 import org.junit.Assert; 41 42 import java.io.File; 43 import java.io.FileInputStream; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.util.Arrays; 47 import java.util.Collection; 48 import java.util.HashMap; 49 import java.util.LinkedList; 50 import java.util.List; 51 import java.util.ListIterator; 52 import java.util.Map; 53 import java.util.concurrent.TimeUnit; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 57 /** 58 * Runs the Media memory test. This test will do various media actions ( ie. 59 * playback, recording and etc.) then capture the snapshot of mediaserver memory 60 * usage. The test summary is save to /sdcard/mediaMemOutput.txt 61 * <p/> 62 * Note that this test will not run properly unless /sdcard is mounted and 63 * writable. 64 */ 65 public class MediaMemoryTest implements IDeviceTest, IRemoteTest { 66 67 ITestDevice mTestDevice = null; 68 69 private static final String METRICS_RUN_NAME = "MediaMemoryLeak"; 70 71 // Constants for running the tests 72 private static final String TEST_CLASS_NAME = 73 "com.android.mediaframeworktest.performance.MediaPlayerPerformance"; 74 private static final String TEST_PACKAGE_NAME = "com.android.mediaframeworktest"; 75 private static final String TEST_RUNNER_NAME = ".MediaFrameworkPerfTestRunner"; 76 77 private final String mOutputPaths[] = {"mediaMemOutput.txt","mediaProcmemOutput.txt"}; 78 79 //Max test timeout - 4 hrs 80 private static final int MAX_TEST_TIMEOUT = 4 * 60 * 60 * 1000; 81 82 public Map<String, String> mPatternMap = new HashMap<>(); 83 private static final Pattern TOTAL_MEM_DIFF_PATTERN = 84 Pattern.compile("^The total diff = (\\d+)"); 85 86 @Option(name = "getHeapDump", description = "Collect the heap ") 87 private boolean mGetHeapDump = false; 88 89 @Option(name = "getProcMem", description = "Collect the procmem info ") 90 private boolean mGetProcMem = false; 91 92 @Option(name = "testName", description = "Test name to run. May be repeated.") 93 private Collection<String> mTests = new LinkedList<>(); 94 MediaMemoryTest()95 public MediaMemoryTest() { 96 mPatternMap.put("testCameraPreviewMemoryUsage", "CameraPreview"); 97 mPatternMap.put("testRecordAudioOnlyMemoryUsage", "AudioRecord"); 98 mPatternMap.put("testH263VideoPlaybackMemoryUsage", "H263Playback"); 99 mPatternMap.put("testRecordVideoAudioMemoryUsage", "H263RecordVideoAudio"); 100 mPatternMap.put("testH263RecordVideoOnlyMemoryUsage", "H263RecordVideoOnly"); 101 mPatternMap.put("testH264VideoPlaybackMemoryUsage", "H264Playback"); 102 mPatternMap.put("testMpeg4RecordVideoOnlyMemoryUsage", "MPEG4RecordVideoOnly"); 103 } 104 105 106 @Override run(ITestInvocationListener listener)107 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 108 Assert.assertNotNull(mTestDevice); 109 110 IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME, 111 TEST_RUNNER_NAME, mTestDevice.getIDevice()); 112 runner.setClassName(TEST_CLASS_NAME); 113 runner.setMaxTimeToOutputResponse(MAX_TEST_TIMEOUT, TimeUnit.MILLISECONDS); 114 if (mGetHeapDump) { 115 runner.addInstrumentationArg("get_heap_dump", "true"); 116 } 117 if (mGetProcMem) { 118 runner.addInstrumentationArg("get_procmem", "true"); 119 } 120 121 BugreportCollector bugListener = new BugreportCollector(listener, 122 mTestDevice); 123 bugListener.addPredicate(new BugreportCollector.Predicate( 124 Relation.AFTER, Freq.EACH, Noun.TESTRUN)); 125 126 if (mTests.size() > 0) { 127 for (String testName : mTests) { 128 runner.setMethodName(TEST_CLASS_NAME, testName); 129 mTestDevice.runInstrumentationTests(runner, bugListener); 130 } 131 } else { 132 mTestDevice.runInstrumentationTests(runner, bugListener); 133 } 134 logOutputFiles(listener); 135 cleanResultFile(); 136 } 137 138 /** 139 * Clean up the test result file from test run 140 */ cleanResultFile()141 private void cleanResultFile() throws DeviceNotAvailableException { 142 String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 143 for(String outputPath : mOutputPaths){ 144 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, outputPath)); 145 } 146 if (mGetHeapDump) { 147 mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, "*.dump")); 148 } 149 } 150 uploadHeapDumpFiles(ITestInvocationListener listener)151 private void uploadHeapDumpFiles(ITestInvocationListener listener) 152 throws DeviceNotAvailableException { 153 // Pull and upload the heap dump output files. 154 InputStreamSource outputSource = null; 155 File outputFile = null; 156 157 String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 158 159 String out = mTestDevice.executeShellCommand(String.format("ls %s/%s", 160 extStore, "*.dump")); 161 String heapOutputFiles[] = out.split("\n"); 162 163 for (String heapFile : heapOutputFiles) { 164 try { 165 outputFile = mTestDevice.pullFile(heapFile.trim()); 166 if (outputFile == null) { 167 continue; 168 } 169 outputSource = new FileInputStreamSource(outputFile); 170 listener.testLog(heapFile, LogDataType.TEXT, outputSource); 171 } finally { 172 FileUtil.deleteFile(outputFile); 173 StreamUtil.cancel(outputSource); 174 } 175 } 176 } 177 178 /** 179 * Pull the output files from the device, add it to the logs, and also parse 180 * out the relevant test metrics and report them. 181 */ logOutputFiles(ITestInvocationListener listener)182 private void logOutputFiles(ITestInvocationListener listener) 183 throws DeviceNotAvailableException { 184 File outputFile = null; 185 InputStreamSource outputSource = null; 186 187 if (mGetHeapDump) { 188 // Upload all the heap dump files. 189 uploadHeapDumpFiles(listener); 190 } 191 for(String outputPath : mOutputPaths){ 192 try { 193 outputFile = mTestDevice.pullFileFromExternal(outputPath); 194 195 if (outputFile == null) { 196 return; 197 } 198 199 // Upload a verbatim copy of the output file 200 CLog.d("Sending %d byte file %s into the logosphere!", 201 outputFile.length(), outputFile); 202 outputSource = new FileInputStreamSource(outputFile); 203 listener.testLog(outputPath, LogDataType.TEXT, outputSource); 204 205 // Parse the output file to upload aggregated metrics 206 parseOutputFile(new FileInputStream(outputFile), listener); 207 } catch (IOException e) { 208 CLog.e("IOException while reading or parsing output file: %s", 209 e.getMessage()); 210 } finally { 211 FileUtil.deleteFile(outputFile); 212 StreamUtil.cancel(outputSource); 213 } 214 } 215 } 216 217 /** 218 * Parse the relevant metrics from the Instrumentation test output file 219 */ parseOutputFile(InputStream dataStream, ITestInvocationListener listener)220 private void parseOutputFile(InputStream dataStream, 221 ITestInvocationListener listener) { 222 223 Map<String, String> runMetrics = new HashMap<>(); 224 225 // try to parse it 226 String contents; 227 try { 228 contents = StreamUtil.getStringFromStream(dataStream); 229 } catch (IOException e) { 230 CLog.e("Got IOException during test processing: %s", 231 e.getMessage()); 232 return; 233 } 234 235 List<String> lines = Arrays.asList(contents.split("\n")); 236 ListIterator<String> lineIter = lines.listIterator(); 237 String line; 238 while (lineIter.hasNext()) { 239 line = lineIter.next(); 240 if (mPatternMap.containsKey(line)) { 241 242 String key = mPatternMap.get(line); 243 // Look for the total diff 244 while (lineIter.hasNext()) { 245 line = lineIter.next(); 246 Matcher m = TOTAL_MEM_DIFF_PATTERN.matcher(line); 247 if (m.matches()) { 248 int result = Integer.parseInt(m.group(1)); 249 runMetrics.put(key, Integer.toString(result)); 250 break; 251 } 252 } 253 } else { 254 CLog.e("Got unmatched line: %s", line); 255 continue; 256 } 257 } 258 reportMetrics(listener, runMetrics); 259 } 260 261 /** 262 * Report run metrics by creating an empty test run to stick them in 263 * <p /> 264 * Exposed for unit testing 265 */ reportMetrics(ITestInvocationListener listener, Map<String, String> metrics)266 void reportMetrics(ITestInvocationListener listener, Map<String, String> metrics) { 267 CLog.d("About to report metrics: %s", metrics); 268 listener.testRunStarted(METRICS_RUN_NAME, 0); 269 listener.testRunEnded(0, TfMetricProtoUtil.upgradeConvert(metrics)); 270 } 271 272 @Override setDevice(ITestDevice device)273 public void setDevice(ITestDevice device) { 274 mTestDevice = device; 275 } 276 277 @Override getDevice()278 public ITestDevice getDevice() { 279 return mTestDevice; 280 } 281 } 282