1/* 2 * Copyright (C) 2008 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 17package com.android.dumprendertree; 18 19import com.android.dumprendertree.forwarder.AdbUtils; 20import com.android.dumprendertree.forwarder.ForwardServer; 21 22import android.app.Instrumentation; 23import android.content.Context; 24import android.content.Intent; 25import android.os.Bundle; 26import android.os.Debug; 27import android.os.Environment; 28import android.os.Process; 29import android.test.ActivityInstrumentationTestCase2; 30import android.util.Log; 31 32import java.io.File; 33import java.io.FileOutputStream; 34import java.io.IOException; 35import java.io.InputStream; 36import java.io.OutputStream; 37import java.io.PrintStream; 38import java.util.concurrent.CountDownLatch; 39import java.util.concurrent.TimeUnit; 40import java.util.regex.Matcher; 41import java.util.regex.Pattern; 42 43public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> { 44 45 private final static String LOGTAG = "LoadTest"; 46 private final static String LOAD_TEST_RESULT = 47 Environment.getExternalStorageDirectory() + "/load_test_result.txt"; 48 private final static int MAX_GC_WAIT_SEC = 10; 49 private final static int LOCAL_PORT = 17171; 50 private boolean mFinished; 51 static final String LOAD_TEST_RUNNER_FILES[] = { 52 "run_page_cycler.py" 53 }; 54 private ForwardServer mForwardServer; 55 56 public LoadTestsAutoTest() { 57 super(TestShellActivity.class); 58 } 59 60 // This function writes the result of the layout test to 61 // Am status so that it can be picked up from a script. 62 public void passOrFailCallback(String file, boolean result) { 63 Instrumentation inst = getInstrumentation(); 64 Bundle bundle = new Bundle(); 65 bundle.putBoolean(file, result); 66 inst.sendStatus(0, bundle); 67 } 68 69 private String setUpForwarding(String forwardInfo, String suite, String iteration) throws IOException { 70 // read forwarding information first 71 Pattern forwardPattern = Pattern.compile("(.*):(\\d+)/(.*)/"); 72 Matcher matcher = forwardPattern.matcher(forwardInfo); 73 if (!matcher.matches()) { 74 throw new RuntimeException("Invalid forward information"); 75 } 76 String host = matcher.group(1); 77 int port = Integer.parseInt(matcher.group(2)); 78 mForwardServer = new ForwardServer(LOCAL_PORT, AdbUtils.resolve(host), port); 79 mForwardServer.start(); 80 return String.format("http://127.0.0.1:%d/%s/%s/start.html?auto=1&iterations=%s", 81 LOCAL_PORT, matcher.group(3), suite, iteration); 82 } 83 84 // Invokes running of layout tests 85 // and waits till it has finished running. 86 public void runPageCyclerTest() throws IOException { 87 LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); 88 89 if (runner.mPageCyclerSuite != null) { 90 // start forwarder to use page cycler suites hosted on external web server 91 if (runner.mPageCyclerForwardHost == null) { 92 throw new RuntimeException("no forwarder information provided"); 93 } 94 runner.mTestPath = setUpForwarding(runner.mPageCyclerForwardHost, 95 runner.mPageCyclerSuite, runner.mPageCyclerIteration); 96 Log.d(LOGTAG, "using path: " + runner.mTestPath); 97 } 98 99 if (runner.mTestPath == null) { 100 throw new RuntimeException("No test specified"); 101 } 102 103 final TestShellActivity activity = (TestShellActivity) getActivity(); 104 105 Log.v(LOGTAG, "About to run tests, calling gc first..."); 106 freeMem(); 107 108 // Run tests 109 runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis, 110 runner.mGetDrawTime, runner.mSaveImagePath); 111 112 getInstrumentation().runOnMainSync(new Runnable() { 113 114 @Override 115 public void run() { 116 activity.clearCache(); 117 } 118 }); 119 if (mForwardServer != null) { 120 mForwardServer.stop(); 121 mForwardServer = null; 122 } 123 try { 124 Thread.sleep(5000); 125 } catch (InterruptedException e) { 126 } 127 dumpMemoryInfo(); 128 129 // Kill activity 130 activity.finish(); 131 } 132 133 private void freeMem() { 134 Log.v(LOGTAG, "freeMem: calling gc..."); 135 final CountDownLatch latch = new CountDownLatch(1); 136 @SuppressWarnings("unused") 137 Object dummy = new Object() { 138 // this object instance is used to track gc 139 @Override 140 protected void finalize() throws Throwable { 141 latch.countDown(); 142 super.finalize(); 143 } 144 }; 145 dummy = null; 146 System.gc(); 147 try { 148 if (!latch.await(MAX_GC_WAIT_SEC, TimeUnit.SECONDS)) { 149 Log.w(LOGTAG, "gc did not happen in 10s"); 150 } 151 } catch (InterruptedException e) { 152 //ignore 153 } 154 } 155 156 private void printRow(PrintStream ps, String format, Object...objs) { 157 ps.println(String.format(format, objs)); 158 } 159 160 private void dumpMemoryInfo() { 161 try { 162 freeMem(); 163 Log.v(LOGTAG, "Dumping memory information."); 164 165 FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true); 166 PrintStream ps = new PrintStream(out); 167 168 ps.print("\n\n\n"); 169 ps.println("** MEMINFO in pid " + Process.myPid() 170 + " [com.android.dumprendertree] **"); 171 String formatString = "%17s %8s %8s %8s %8s"; 172 173 long nativeMax = Debug.getNativeHeapSize() / 1024; 174 long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; 175 long nativeFree = Debug.getNativeHeapFreeSize() / 1024; 176 Runtime runtime = Runtime.getRuntime(); 177 long dalvikMax = runtime.totalMemory() / 1024; 178 long dalvikFree = runtime.freeMemory() / 1024; 179 long dalvikAllocated = dalvikMax - dalvikFree; 180 181 182 Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); 183 Debug.getMemoryInfo(memInfo); 184 185 final int nativeShared = memInfo.nativeSharedDirty; 186 final int dalvikShared = memInfo.dalvikSharedDirty; 187 final int otherShared = memInfo.otherSharedDirty; 188 189 final int nativePrivate = memInfo.nativePrivateDirty; 190 final int dalvikPrivate = memInfo.dalvikPrivateDirty; 191 final int otherPrivate = memInfo.otherPrivateDirty; 192 193 printRow(ps, formatString, "", "native", "dalvik", "other", "total"); 194 printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); 195 printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A", 196 nativeAllocated + dalvikAllocated); 197 printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A", 198 nativeFree + dalvikFree); 199 200 printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss, 201 memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); 202 203 printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared, 204 nativeShared + dalvikShared + otherShared); 205 printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate, 206 nativePrivate + dalvikPrivate + otherPrivate); 207 ps.print("\n\n\n"); 208 ps.flush(); 209 ps.close(); 210 out.flush(); 211 out.close(); 212 } catch (IOException e) { 213 Log.e(LOGTAG, e.getMessage()); 214 } 215 } 216 217 // A convenient method to be called by another activity. 218 private void runTestAndWaitUntilDone(TestShellActivity activity, String url, int timeout, 219 boolean getDrawTime, String saveImagePath) { 220 activity.setCallback(new TestShellCallback() { 221 public void finished() { 222 synchronized (LoadTestsAutoTest.this) { 223 mFinished = true; 224 LoadTestsAutoTest.this.notifyAll(); 225 } 226 } 227 228 public void timedOut(String url) { 229 } 230 }); 231 232 mFinished = false; 233 Intent intent = new Intent(Intent.ACTION_VIEW); 234 intent.setClass(activity, TestShellActivity.class); 235 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 236 intent.putExtra(TestShellActivity.TEST_URL, url); 237 intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout); 238 intent.putExtra(TestShellActivity.RESULT_FILE, LOAD_TEST_RESULT); 239 intent.putExtra(TestShellActivity.GET_DRAW_TIME, getDrawTime); 240 if (saveImagePath != null) 241 intent.putExtra(TestShellActivity.SAVE_IMAGE, saveImagePath); 242 activity.startActivity(intent); 243 244 // Wait until done. 245 synchronized (this) { 246 while(!mFinished) { 247 try { 248 this.wait(); 249 } catch (InterruptedException e) { } 250 } 251 } 252 } 253 254 public void copyRunnerAssetsToCache() { 255 try { 256 Context targetContext = getInstrumentation().getTargetContext(); 257 File cacheDir = targetContext.getCacheDir(); 258 259 for( int i=0; i< LOAD_TEST_RUNNER_FILES.length; i++) { 260 InputStream in = targetContext.getAssets().open( 261 LOAD_TEST_RUNNER_FILES[i]); 262 OutputStream out = new FileOutputStream( 263 new File(cacheDir, LOAD_TEST_RUNNER_FILES[i])); 264 265 byte[] buf = new byte[2048]; 266 int len; 267 268 while ((len = in.read(buf)) >= 0 ) { 269 out.write(buf, 0, len); 270 } 271 out.close(); 272 in.close(); 273 } 274 }catch (IOException e) { 275 e.printStackTrace(); 276 } 277 278 } 279 280} 281