summaryrefslogtreecommitdiffstats
path: root/tests/MemoryUsage
diff options
context:
space:
mode:
authorMaxim Siniavine <siniavine@google.com>2012-08-17 14:17:06 -0700
committerMaxim Siniavine <siniavine@google.com>2012-08-21 11:31:44 -0700
commit9229700728ec4b7bca28da5325b48a4acb4bfc0d (patch)
treed09c3effdaf9109e7fff3da26fe474dcce80b710 /tests/MemoryUsage
parentaeca6898041437a7e2ba68c1421b9be90669154d (diff)
downloadframeworks_base-9229700728ec4b7bca28da5325b48a4acb4bfc0d.zip
frameworks_base-9229700728ec4b7bca28da5325b48a4acb4bfc0d.tar.gz
frameworks_base-9229700728ec4b7bca28da5325b48a4acb4bfc0d.tar.bz2
Added a test to measure memory usage of apps.
Each app uses a certain amount of memory when running in the foreground. This test takes a list of app on the command line starts them one at a time and reports the total PSS of the app's process. The test allows to monitor memory usage over time. Change-Id: I3411bd96cf7c7af10acbb8deeb9936469b810ea2
Diffstat (limited to 'tests/MemoryUsage')
-rw-r--r--tests/MemoryUsage/Android.mk16
-rw-r--r--tests/MemoryUsage/AndroidManifest.xml12
-rw-r--r--tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageInstrumentation.java38
-rw-r--r--tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java235
4 files changed, 301 insertions, 0 deletions
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
new file mode 100644
index 0000000..e7bfb4f
--- /dev/null
+++ b/tests/MemoryUsage/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := MemoryUsage
+
+LOCAL_SDK_VERSION := 7
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file
diff --git a/tests/MemoryUsage/AndroidManifest.xml b/tests/MemoryUsage/AndroidManifest.xml
new file mode 100644
index 0000000..3932e5b
--- /dev/null
+++ b/tests/MemoryUsage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.memoryusage">
+ <instrumentation android:label="Memory usage instrumentation"
+ android:name="com.android.tests.memoryusage.MemoryUsageInstrumentation"
+ android:targetPackage="com.android.tests.memoryusage" />
+
+ <application android:label="Memory Usage Test">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest> \ No newline at end of file
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageInstrumentation.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageInstrumentation.java
new file mode 100644
index 0000000..ed6d7e6
--- /dev/null
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageInstrumentation.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.memoryusage;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * InstrumentationTestRunner for use with the {@link MemoryUsageTest}.
+ */
+public class MemoryUsageInstrumentation extends InstrumentationTestRunner {
+
+ private Bundle arguments;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ this.arguments = arguments;
+ super.onCreate(arguments);
+ }
+
+ public Bundle getBundle() {
+ return arguments;
+ }
+
+}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
new file mode 100644
index 0000000..f26edc6
--- /dev/null
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.memoryusage;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Debug.MemoryInfo;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This test is intended to measure the amount of memory applications use when
+ * they start. Names of the applications are passed in command line, and the
+ * test starts each application, waits until its memory usage is stabilized and
+ * reports the total PSS in kilobytes of each processes.
+ * The instrumentation expects the following key to be passed on the command line:
+ * apps - A list of applications to start and their corresponding result keys
+ * in the following format:
+ * -e apps <app name>^<result key>|<app name>^<result key>
+ */
+public class MemoryUsageTest extends InstrumentationTestCase {
+
+ private static final int SLEEP_TIME = 1000;
+ private static final int THRESHOLD = 1024;
+ private static final int MAX_ITERATIONS = 10;
+ private static final int MIN_ITERATIONS = 4;
+
+ private static final String TAG = "MemoryUsageInstrumentation";
+ private static final String KEY_APPS = "apps";
+
+ private Map<String, Intent> nameToIntent;
+ private Map<String, String> nameToProcess;
+ private Map<String, String> nameToResultKey;
+
+ public void testMemory() {
+ MemoryUsageInstrumentation instrumentation =
+ (MemoryUsageInstrumentation) getInstrumentation();
+ Bundle args = instrumentation.getBundle();
+
+ createMappings();
+ parseArgs(args);
+
+ Bundle results = new Bundle();
+ for (String app : nameToResultKey.keySet()) {
+ String processName;
+ try {
+ processName = startApp(app);
+ measureMemory(app, processName, results);
+ } catch (NameNotFoundException e) {
+ Log.i(TAG, "Application " + app + " not found");
+ }
+
+ }
+ instrumentation.sendStatus(0, results);
+ }
+
+ private void parseArgs(Bundle args) {
+ nameToResultKey = new HashMap<String, String>();
+ String appList = args.getString(KEY_APPS);
+
+ if (appList == null)
+ return;
+
+ String appNames[] = appList.split("\\|");
+ for (String pair : appNames) {
+ String[] parts = pair.split("\\^");
+ if (parts.length != 2) {
+ Log.e(TAG, "The apps key is incorectly formatted");
+ fail();
+ }
+
+ nameToResultKey.put(parts[0], parts[1]);
+ }
+ }
+
+ private void createMappings() {
+ nameToIntent = new HashMap<String, Intent>();
+ nameToProcess = new HashMap<String, String>();
+
+ PackageManager pm = getInstrumentation().getContext()
+ .getPackageManager();
+ Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
+ if (ris == null || ris.isEmpty()) {
+ Log.i(TAG, "Could not find any apps");
+ } else {
+ for (ResolveInfo ri : ris) {
+ Log.i(TAG, "Name: " + ri.loadLabel(pm).toString()
+ + " package: " + ri.activityInfo.packageName
+ + " name: " + ri.activityInfo.name);
+ Intent startIntent = new Intent(intentToResolve);
+ startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ startIntent.setClassName(ri.activityInfo.packageName,
+ ri.activityInfo.name);
+ nameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
+ nameToProcess.put(ri.loadLabel(pm).toString(),
+ ri.activityInfo.processName);
+ }
+ }
+ }
+
+ private String startApp(String appName) throws NameNotFoundException {
+ Log.i(TAG, "Starting " + appName);
+
+ if (!nameToProcess.containsKey(appName))
+ throw new NameNotFoundException("Could not find: " + appName);
+
+ String process = nameToProcess.get(appName);
+ Intent startIntent = nameToIntent.get(appName);
+ getInstrumentation().getContext().startActivity(startIntent);
+ return process;
+ }
+
+ private void measureMemory(String appName, String processName,
+ Bundle results) {
+ List<Integer> pssData = new ArrayList<Integer>();
+ int pss = 0;
+ int iteration = 0;
+ while (iteration < MAX_ITERATIONS) {
+ sleep();
+ pss = getPss(processName);
+ Log.i(TAG, appName + "=" + pss);
+ if (pss < 0) {
+ reportError(appName, processName, results);
+ return;
+ }
+ pssData.add(pss);
+ if (iteration >= MIN_ITERATIONS && stabilized(pssData)) {
+ results.putInt(nameToResultKey.get(appName), pss);
+ return;
+ }
+ iteration++;
+ }
+
+ Log.w(TAG, appName + " memory usage did not stabilize");
+ results.putInt(appName, average(pssData));
+ }
+
+ private int average(List<Integer> pssData) {
+ int sum = 0;
+ for (int sample : pssData) {
+ sum += sample;
+ }
+
+ return sum / pssData.size();
+ }
+
+ private boolean stabilized(List<Integer> pssData) {
+ if (pssData.size() < 3)
+ return false;
+ int diff1 = Math.abs(pssData.get(pssData.size() - 1) - pssData.get(pssData.size() - 2));
+ int diff2 = Math.abs(pssData.get(pssData.size() - 2) - pssData.get(pssData.size() - 3));
+
+ Log.i(TAG, "diff1=" + diff1 + " diff2=" + diff2);
+
+ return (diff1 + diff2) < THRESHOLD;
+ }
+
+ private void sleep() {
+ try {
+ Thread.sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ private void reportError(String appName, String processName, Bundle results) {
+ ActivityManager am = (ActivityManager) getInstrumentation()
+ .getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
+ if (crashes != null) {
+ for (ProcessErrorStateInfo crash : crashes) {
+ if (!crash.processName.equals(processName))
+ continue;
+
+ Log.w(TAG, appName + " crashed: " + crash.shortMsg);
+ results.putString(nameToResultKey.get(appName), crash.shortMsg);
+ return;
+ }
+ }
+
+ results.putString(nameToResultKey.get(appName),
+ "Crashed for unknown reason");
+ Log.w(TAG, appName
+ + " not found in process list, most likely it is crashed");
+ }
+
+ private int getPss(String processName) {
+ ActivityManager am = (ActivityManager) getInstrumentation()
+ .getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ List<RunningAppProcessInfo> apps = am.getRunningAppProcesses();
+
+ for (RunningAppProcessInfo proc : apps) {
+ if (!proc.processName.equals(processName)) {
+ continue;
+ }
+
+ int[] pids = {
+ proc.pid };
+
+ MemoryInfo meminfo = am.getProcessMemoryInfo(pids)[0];
+ return meminfo.getTotalPss();
+
+ }
+ return -1;
+ }
+}