Merge "Deprecate FILE_NAME_TAG"
diff --git a/libraries/annotations/src/android/platform/test/annotations/PlatinumTest.java b/libraries/annotations/src/android/platform/test/annotations/PlatinumTest.java
new file mode 100644
index 0000000..6ec6dad
--- /dev/null
+++ b/libraries/annotations/src/android/platform/test/annotations/PlatinumTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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 android.platform.test.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test that should run as part of the Platinum.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface PlatinumTest {
+ /** Defines the Platinum test focus area which the test associates to. */
+ String focusArea() default "";
+}
\ No newline at end of file
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java
index 9a9a350..764ede7 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/HeapDumpHelper.java
@@ -18,11 +18,15 @@
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@@ -36,11 +40,21 @@
private static final String HEAPDUMP_NATIVE_OUTPUT_FILE_METRIC_NAME = "native_heapdump_file_";
private static final String HEAPDUMP_CMD = "am dumpheap %s %s";
private static final String NATIVE_HEAPDUMP_CMD = "am dumpheap -n %s %s";
+ private static final String PIDOF_CMD = "pidof %s";
+ private static final String MV_CMD = "mv %s %s";
+
+ @VisibleForTesting
+ static final String MANAGED_HEAPDUMP_EMPTY_FILES_COUNT_METRIC =
+ "managed_heapdump_empty_files_count";
+
+ @VisibleForTesting
+ static final String NATIVE_HEAPDUMP_EMPTY_FILES_COUNT_METRIC =
+ "native_heapdump_empty_files_count";
String mId = null;
File mResultsFile = null;
private String[] mProcessNames = null;
- private String mTestOutputDir = null;
+ private Path mTestOutputDir;
private boolean mNativeHeapDumpEnabled = false;
private UiDevice mUiDevice;
HashMap<String, String> mHeapDumpFinalMap;
@@ -52,7 +66,7 @@
public void setUp(String testOutputDir, String... processNames) {
mProcessNames = processNames;
- mTestOutputDir = testOutputDir;
+ mTestOutputDir = Paths.get(testOutputDir);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
@@ -68,38 +82,53 @@
@Override
public Map<String, String> getMetrics() {
-
Log.i(TAG, "Metric collector enabled. Dumping the hprof.");
int processCount = 0;
+ int managedEmptyFilesCount = 0;
+ int nativeEmptyFilesCount = 0;
for (String processName : mProcessNames) {
+ String pid = getPid(processName);
+ // If the PID doesn't exist, we don't need to run the dumpheap command
+ if (pid.isEmpty()) {
+ continue;
+ }
if (mId != null && !mId.isEmpty()) {
try {
processCount++;
- String finalHeapDumpPath =
+ String fileName =
String.format(
- "%s%s%s_%s.hprof",
- mTestOutputDir,
+ "%s%s_%s.hprof",
HEAPDUMP_MANAGED_OUTPUT_FILE_METRIC_NAME,
processName.replace("/", "#"),
mId);
- execHeapDump(processName, finalHeapDumpPath, false);
+ String finalHeapDumpPath = mTestOutputDir.resolve(fileName).toString();
+ execHeapDump(pid, processName, finalHeapDumpPath, false);
+ if (isEmptyFile(finalHeapDumpPath)) {
+ managedEmptyFilesCount++;
+ finalHeapDumpPath = renameEmptyFile(mTestOutputDir, fileName);
+ }
mHeapDumpFinalMap.put(
HEAPDUMP_MANAGED_OUTPUT_FILE_METRIC_NAME + processCount,
finalHeapDumpPath);
if (mNativeHeapDumpEnabled) {
- String finalNativeHeapDumpPath =
+ String nativeFileName =
String.format(
- "%s%s%s_%s.txt",
- mTestOutputDir,
+ "%s%s_%s.txt",
HEAPDUMP_NATIVE_OUTPUT_FILE_METRIC_NAME,
processName.replace("/", "#"),
mId);
- execHeapDump(processName, finalNativeHeapDumpPath, true);
+ String finalNativeHeapDumpPath =
+ mTestOutputDir.resolve(nativeFileName).toString();
+ execHeapDump(pid, processName, finalNativeHeapDumpPath, true);
+ if (isEmptyFile(finalNativeHeapDumpPath)) {
+ nativeEmptyFilesCount++;
+ finalNativeHeapDumpPath =
+ renameEmptyFile(mTestOutputDir, nativeFileName);
+ }
mHeapDumpFinalMap.put(
HEAPDUMP_NATIVE_OUTPUT_FILE_METRIC_NAME + processCount,
finalNativeHeapDumpPath);
}
-
} catch (Throwable e) {
Log.e(TAG, "dumpheap command failed", e);
}
@@ -107,19 +136,35 @@
Log.e(TAG, "Metric collector is enabled but the heap dump file id is not valid.");
}
}
+ mHeapDumpFinalMap.put(
+ MANAGED_HEAPDUMP_EMPTY_FILES_COUNT_METRIC, String.valueOf(managedEmptyFilesCount));
+ mHeapDumpFinalMap.put(
+ NATIVE_HEAPDUMP_EMPTY_FILES_COUNT_METRIC, String.valueOf(nativeEmptyFilesCount));
return mHeapDumpFinalMap;
}
- private String execHeapDump(String processName, String filePath, boolean isNativeHeapDump)
+ /** Get the pid of a process name */
+ private String getPid(String processName) {
+ String output = "";
+ try {
+ output = mUiDevice.executeShellCommand(String.format(PIDOF_CMD, processName));
+ Log.i(TAG, String.format("The PID of %s is %s.", processName, output));
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Failed to get the pid of %s", processName), e);
+ }
+ return output;
+ }
+
+ private String execHeapDump(
+ String pid, String processName, String filePath, boolean isNativeHeapDump)
throws IOException {
try {
- String heapdumpCommand = isNativeHeapDump ? NATIVE_HEAPDUMP_CMD : HEAPDUMP_CMD;
- Log.i(
- TAG,
- "Running heapdump command :"
- + String.format(heapdumpCommand, processName, filePath));
- return mUiDevice.executeShellCommand(
- String.format(heapdumpCommand, processName, filePath));
+ String heapdumpCommand =
+ isNativeHeapDump
+ ? String.format(NATIVE_HEAPDUMP_CMD, pid, filePath)
+ : String.format(HEAPDUMP_CMD, pid, filePath);
+ Log.i(TAG, "Running heapdump command :" + heapdumpCommand);
+ return mUiDevice.executeShellCommand(heapdumpCommand);
} catch (IOException e) {
throw new RuntimeException(
String.format("Unable to execute heapdump command for %s ", processName), e);
@@ -136,6 +181,26 @@
return true;
}
+ /** Returns true if heapdump file is empty */
+ private boolean isEmptyFile(String path) throws IOException {
+ long bytes = Files.size(Paths.get(path));
+ Log.i(TAG, String.format("File size of %s is %s bytes", path, bytes));
+ return bytes < 10;
+ }
+
+ /** Rename an empty file */
+ private String renameEmptyFile(Path dir, String fileName) {
+ String oldFile = dir.resolve(fileName).toString();
+ String newFile = dir.resolve("EMPTY-" + fileName).toString();
+ try {
+ mUiDevice.executeShellCommand(String.format(MV_CMD, oldFile, newFile));
+ return newFile;
+ } catch (IOException e) {
+ Log.i(TAG, String.format("Rename %s failed.", oldFile), e);
+ }
+ return oldFile;
+ }
+
@Override
public boolean stopCollecting() {
mId = null;
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
index 455c1e5..f1edd4d 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/ShowmapSnapshotHelper.java
@@ -29,6 +29,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.InputMismatchException;
@@ -47,12 +48,15 @@
*/
public class ShowmapSnapshotHelper implements ICollectorHelper<String> {
private static final String TAG = ShowmapSnapshotHelper.class.getSimpleName();
-
private static final String DROP_CACHES_CMD = "echo %d > /proc/sys/vm/drop_caches";
private static final String PIDOF_CMD = "pidof %s";
public static final String ALL_PROCESSES_CMD = "ps -A";
private static final String SHOWMAP_CMD = "showmap -v %d";
private static final String CHILD_PROCESSES_CMD = "ps -A --ppid %d";
+ @VisibleForTesting public static final String OOM_SCORE_ADJ_CMD = "cat /proc/%d/oom_score_adj";
+ private static final int PROCESS_OOM_SCORE_IMPERCEPTIBLE = 200;
+ private static final int PROCESS_OOM_SCORE_CACHED = 899;
+ private static final String ACTIVITY_LRU_CMD = "dumpsys activity lru";
private static final String THREADS_FILE_PATH = "/sdcard/countThreads.sh";
@VisibleForTesting public static final String THREADS_CMD = "sh /sdcard/countThreads.sh";
private static final String THREADS_EXEC_SCRIPT =
@@ -60,16 +64,19 @@
+ " /proc/$i/cmdline) : $(ls /proc/$i/task | wc -l)\"; done;";
public static final String THREADS_PATTERN = "(?<key>^threads_count_.+) : (?<value>[0-9]+)";
public static final String OUTPUT_METRIC_PATTERN = "showmap_%s_bytes";
+ public static final String OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN =
+ "showmap_%s_bytes_imperceptible";
public static final String OUTPUT_FILE_PATH_KEY = "showmap_output_file";
public static final String PROCESS_COUNT = "process_count";
public static final String CHILD_PROCESS_COUNT_PREFIX = "child_processes_count";
public static final String OUTPUT_CHILD_PROCESS_COUNT_KEY = CHILD_PROCESS_COUNT_PREFIX + "_%s";
public static final String PROCESS_WITH_CHILD_PROCESS_COUNT =
"process_with_child_process_count";
- private static final String CHILD_PROCESS_NAME_REGEX = "(\\S+)$";
private static final String METRIC_VALUE_SEPARATOR = "_";
public static final String PARENT_PROCESS_STRING = "parent_process";
public static final String CHILD_PROCESS_STRING = "child_process";
+ // The reason to skip the process: b/272181398#comment24
+ private static final Set<String> SKIP_PROCESS = new HashSet<>(Arrays.asList("logcat", "sh"));
private String[] mProcessNames = null;
private String mTestOutputDir = null;
@@ -162,37 +169,61 @@
}
HashSet<Integer> zygoteChildrenPids = getZygoteChildrenPids();
FileWriter writer = new FileWriter(new File(mTestOutputFile), true);
+
+ try {
+ // dump the activity lru to better understand the process state
+ String activityLRU = executeShellCommand(ACTIVITY_LRU_CMD);
+ Log.d(TAG, String.format("Dumpsys activity lru output: %s", activityLRU));
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Failed to execute %s", ACTIVITY_LRU_CMD));
+ }
+
for (String processName : mProcessNames) {
List<Integer> pids = new ArrayList<>();
// Collect required data
try {
pids = getPids(processName);
for (Integer pid : pids) {
- // Force Garbage collect to trim transient objects before taking memory
- // measurements as memory tests aim to track persistent memory regression
- // instead of transient memory which also allows for de-noising and reducing
- // likelihood of false alerts.
- if (mRunGcPrecollection && zygoteChildrenPids.contains(pid)) {
- // Skip native processes from sending GC signal.
- android.os.Trace.beginSection("IssueGCForPid: " + pid);
- // Perform a synchronous GC which happens when we request meminfo
- // This save us the need of setting up timeouts that may or may not
- // match with the end time of GC.
- mUiDevice.executeShellCommand("dumpsys meminfo -a " + pid);
- android.os.Trace.endSection();
- }
+ // Force Garbage collect to trim transient objects before taking memory
+ // measurements as memory tests aim to track persistent memory regression
+ // instead of transient memory which also allows for de-noising and reducing
+ // likelihood of false alerts.
+ if (mRunGcPrecollection && zygoteChildrenPids.contains(pid)) {
+ // Skip native processes from sending GC signal.
+ android.os.Trace.beginSection("IssueGCForPid: " + pid);
+ // Perform a synchronous GC which happens when we request meminfo
+ // This save us the need of setting up timeouts that may or may not
+ // match with the end time of GC.
+ mUiDevice.executeShellCommand("dumpsys meminfo -a " + pid);
+ android.os.Trace.endSection();
+ }
- android.os.Trace.beginSection("ExecuteShowmap");
- String showmapOutput = execShowMap(processName, pid);
- android.os.Trace.endSection();
- parseAndUpdateMemoryInfo(processName, showmapOutput);
- // Store showmap output into file. If there are more than one process
- // with same name write the individual showmap associated with pid.
- storeToFile(mTestOutputFile, processName, pid, showmapOutput, writer);
- // Parse number of child processes for the given pid and update the
- // total number of child process count for the process name that pid
- // is associated with.
- updateChildProcessesDetails(processName, pid);
+ android.os.Trace.beginSection("ExecuteShowmap");
+ String showmapOutput = execShowMap(processName, pid);
+ android.os.Trace.endSection();
+ // Mark the imperceptible process for showmap and child process count
+ if (isProcessOomScoreAbove(
+ processName, pid, PROCESS_OOM_SCORE_IMPERCEPTIBLE)) {
+ Log.i(
+ TAG,
+ String.format(
+ "This process is imperceptible: %s", processName));
+ parseAndUpdateMemoryInfo(
+ processName,
+ showmapOutput,
+ OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN);
+ } else {
+ parseAndUpdateMemoryInfo(
+ processName, showmapOutput, OUTPUT_METRIC_PATTERN);
+ }
+
+ // Store showmap output into file. If there are more than one process
+ // with same name write the individual showmap associated with pid.
+ storeToFile(mTestOutputFile, processName, pid, showmapOutput, writer);
+ // Parse number of child processes for the given pid and update the
+ // total number of child process count for the process name that pid
+ // is associated with.
+ updateChildProcessesDetails(processName, pid);
}
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage(), e.getCause());
@@ -388,7 +419,8 @@
* @param processName name of the process to extract memory info for
* @param showmapOutput showmap command output
*/
- private void parseAndUpdateMemoryInfo(String processName, String showmapOutput)
+ private void parseAndUpdateMemoryInfo(
+ String processName, String showmapOutput, String metricPattern)
throws RuntimeException {
try {
@@ -405,9 +437,8 @@
for (Map.Entry<String, List<Integer>> entry : mMetricNameIndexMap.entrySet()) {
Long metricValue = 0L;
- String metricKey = constructKey(
- String.format(OUTPUT_METRIC_PATTERN, entry.getKey()),
- processName);
+ String metricKey =
+ constructKey(String.format(metricPattern, entry.getKey()), processName);
for (int index = 0; index < entry.getValue().size(); index++) {
metricValue += Long.parseLong(summarySplit[entry.getValue().get(index) + 1]);
}
@@ -473,6 +504,27 @@
}
/**
+ * Return true if the giving process is imperceptible. If the OOM adjustment score is in [900,
+ * 1000), the process is cached. If the OOM adjustment score is in (-1000, 200], the process is
+ * perceptible. If the OOM adjustment score is in (200, 1000), the process is imperceptible
+ */
+ public boolean isProcessOomScoreAbove(String processName, long pid, int threshold) {
+ try {
+ String score = executeShellCommand(String.format(OOM_SCORE_ADJ_CMD, pid));
+ boolean result = Integer.parseInt(score.trim()) > threshold;
+ Log.i(
+ TAG,
+ String.format(
+ "The OOM adjustment score for process %s is %s", processName, score));
+ return result;
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Unable to get process oom_score_adj for %s", processName), e);
+ // We don't know the process is cached or not, still collect it
+ return false;
+ }
+ }
+
+ /**
* Retrieves the number of child processes for the given process id and updates the total
* process count and adds a child process metric for the process name that pid is associated
* with.
@@ -482,8 +534,8 @@
*/
private void updateChildProcessesDetails(String processName, long pid) {
String childProcessName;
+ String childPID;
String completeChildProcessMetric;
- Pattern childProcessPattern = Pattern.compile(CHILD_PROCESS_NAME_REGEX);
try {
Log.i(TAG,
String.format("Retrieving child processes count for process name: %s with"
@@ -491,38 +543,50 @@
String childProcessesStr = mUiDevice
.executeShellCommand(String.format(CHILD_PROCESSES_CMD, pid));
Log.i(TAG, String.format("Child processes cmd output: %s", childProcessesStr));
- String[] childProcessStrSplit = childProcessesStr.split("\\n");
- // To discard the header line in the command output.
- int childProcessCount = childProcessStrSplit.length - 1;
- String childCountMetricKey = String.format(OUTPUT_CHILD_PROCESS_COUNT_KEY, processName);
+ int childProcessCount = 0;
+ String[] childProcessStrSplit = childProcessesStr.split("\\n");
+ for (String line : childProcessStrSplit) {
+ // To discard the header line in the command output.
+ if (Objects.equals(line, childProcessStrSplit[0])) continue;
+ String[] childProcessSplit = line.trim().split("\\s+");
+ /**
+ * final metric will be of following format
+ * parent_process_<process>_child_process_<process>
+ * parent_process_zygote64_child_process_system_server
+ */
+ childPID = childProcessSplit[1];
+ childProcessName = childProcessSplit[8];
+ // Skip the logcat and sh processes in child process count
+ if (SKIP_PROCESS.contains(childProcessName)
+ || isProcessOomScoreAbove(
+ childProcessName,
+ Long.parseLong(childPID),
+ PROCESS_OOM_SCORE_CACHED)) {
+ Log.i(
+ TAG,
+ String.format(
+ "Skip the child process %s in the parent process %s.",
+ childProcessName, processName));
+ continue;
+ }
+ childProcessCount++;
+ completeChildProcessMetric =
+ String.join(
+ METRIC_VALUE_SEPARATOR,
+ PARENT_PROCESS_STRING,
+ processName,
+ CHILD_PROCESS_STRING,
+ childProcessName);
+ mMemoryMap.put(completeChildProcessMetric, "1");
+ }
+ String childCountMetricKey = String.format(OUTPUT_CHILD_PROCESS_COUNT_KEY, processName);
if (childProcessCount > 0) {
mMemoryMap.put(childCountMetricKey,
Long.toString(
Long.parseLong(mMemoryMap.getOrDefault(childCountMetricKey, "0"))
+ childProcessCount));
}
- for (String line : childProcessStrSplit) {
- // To discard the header line in the command output.
- if (Objects.equals(line, childProcessStrSplit[0])) continue;
- Matcher childProcessMatcher = childProcessPattern.matcher(line);
- if (childProcessMatcher.find()) {
- /**
- * final metric will be of following format
- * parent_process_<process>_child_process_<process>
- * parent_process_zygote64_child_process_system_server
- */
- childProcessName = childProcessMatcher.group(1);
- completeChildProcessMetric =
- String.join(
- METRIC_VALUE_SEPARATOR,
- PARENT_PROCESS_STRING,
- processName,
- CHILD_PROCESS_STRING,
- childProcessName);
- mMemoryMap.put(completeChildProcessMetric, "1");
- }
- }
} catch (IOException e) {
throw new RuntimeException("Unable to run child process command.", e);
}
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java
index c322e26..386ce38 100644
--- a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/HeapDumpHelperTest.java
@@ -58,11 +58,29 @@
}
@Test
+ public void testHeapDumpCollectionNoProcess() {
+ mHeapDumpHelper.setUp("/data/local/tmp/", "");
+ assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-1"));
+ Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+ assertTrue(metrics.size() == 2);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ }
+
+ @Test
public void testSuccessfulHeapDumpCollection() {
mHeapDumpHelper.setUp("/data/local/tmp/", "com.android.systemui");
assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-1"));
Map<String, String> metrics = mHeapDumpHelper.getMetrics();
- assertTrue(metrics.size() == 1);
+ assertTrue(metrics.size() == 3);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "managed_heapdump_file_"
+ + "com.android.systemui_sample-heapdump-1.hprof"));
}
@Test
@@ -70,11 +88,15 @@
mHeapDumpHelper.setUp("/data/local/tmp/", "/system/bin/surfaceflinger");
assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-1"));
Map<String, String> metrics = mHeapDumpHelper.getMetrics();
- assertTrue(metrics.size() == 1);
+ assertTrue(metrics.size() == 3);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("1"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
assertTrue(
metrics.get("managed_heapdump_file_1")
.equalsIgnoreCase(
- "/data/local/tmp/managed_heapdump_file_#system#bin#surfaceflinger_sample-heapdump-1.hprof"));
+ "/data/local/tmp/"
+ + "EMPTY-managed_heapdump_file_"
+ + "#system#bin#surfaceflinger_sample-heapdump-1.hprof"));
}
@Test
@@ -83,7 +105,21 @@
mHeapDumpHelper.setUp("/data/local/tmp/", processNames);
assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-2"));
Map<String, String> metrics = mHeapDumpHelper.getMetrics();
- assertTrue(metrics.size() == 2);
+ assertTrue(metrics.size() == 4);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "managed_heapdump_file_"
+ + "com.android.systemui_sample-heapdump-2.hprof"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_2")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "managed_heapdump_file_"
+ + "system_server_sample-heapdump-2.hprof"));
}
@Test
@@ -93,11 +129,32 @@
mHeapDumpHelper.enableNativeHeapDump();
assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-2"));
Map<String, String> metrics = mHeapDumpHelper.getMetrics();
- assertTrue(metrics.size() == 4);
+ assertTrue(metrics.size() == 6);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "managed_heapdump_file_"
+ + "com.android.systemui_sample-heapdump-2.hprof"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_2")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "managed_heapdump_file_"
+ + "system_server_sample-heapdump-2.hprof"));
+ assertTrue(
+ metrics.get("native_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "native_heapdump_file_"
+ + "com.android.systemui_sample-heapdump-2.txt"));
assertTrue(
metrics.get("native_heapdump_file_2")
.equalsIgnoreCase(
- "/data/local/tmp/native_heapdump_file_system_server_sample-heapdump-2.txt"));
+ "/data/local/tmp/"
+ + "native_heapdump_file_system_server_sample-heapdump-2.txt"));
}
@Test
@@ -105,6 +162,48 @@
mHeapDumpHelper.setUp("/data/local/tmp/", "com.android.systemui");
assertFalse(mHeapDumpHelper.startCollecting(""));
Map<String, String> metrics = mHeapDumpHelper.getMetrics();
- assertTrue(metrics.size() == 0);
+ assertTrue(metrics.size() == 2);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ }
+
+ @Test
+ public void testHeapDumpCollectionForInvalidProcesses() {
+ String[] processNames = new String[] {"systemui", "twoshay"};
+ mHeapDumpHelper.setUp("/data/local/tmp/", processNames);
+ assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-2"));
+ Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+ assertTrue(metrics.size() == 3);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("1"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("0"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "EMPTY-managed_heapdump_file_"
+ + "twoshay_sample-heapdump-2.hprof"));
+ }
+
+ @Test
+ public void testNativeHeapDumpCollectionForInvalidProcesses() {
+ String[] processNames = new String[] {"systemui", "twoshay"};
+ mHeapDumpHelper.setUp("/data/local/tmp/", processNames);
+ mHeapDumpHelper.enableNativeHeapDump();
+ assertTrue(mHeapDumpHelper.startCollecting("sample-heapdump-2"));
+ Map<String, String> metrics = mHeapDumpHelper.getMetrics();
+ assertTrue(metrics.size() == 4);
+ assertTrue(metrics.get("managed_heapdump_empty_files_count").equalsIgnoreCase("1"));
+ assertTrue(metrics.get("native_heapdump_empty_files_count").equalsIgnoreCase("1"));
+ assertTrue(
+ metrics.get("managed_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "EMPTY-managed_heapdump_file_"
+ + "twoshay_sample-heapdump-2.hprof"));
+ assertTrue(
+ metrics.get("native_heapdump_file_1")
+ .equalsIgnoreCase(
+ "/data/local/tmp/"
+ + "EMPTY-native_heapdump_file_twoshay_sample-heapdump-2.txt"));
}
}
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ShowmapSnapshotHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ShowmapSnapshotHelperTest.java
index 76625a7..5d13bcd 100644
--- a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ShowmapSnapshotHelperTest.java
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ShowmapSnapshotHelperTest.java
@@ -21,8 +21,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -71,6 +76,15 @@
private static final String[] TWO_PROCESS_LIST = {
"com.android.systemui", "system_server"
};
+ private static final String[] MIXED_PROCESS_LIST = {
+ "com.android.systemui",
+ "system_server",
+ "com.google.android.googlequicksearchbox:search",
+ "com.google.android.connectivitymonitor"
+ };
+ private static final String[] CACHED_PROCESS_LIST = {
+ "com.google.android.googlequicksearchbox:search", "com.google.android.connectivitymonitor"
+ };
private static final String[] NO_PROCESS_LIST = {
null
};
@@ -155,6 +169,81 @@
testProcessList(METRIC_INDEX_STR, TWO_PROCESS_LIST);
}
+ /** Test that cached processes are skipped for showmap metrics. */
+ @Test
+ public void testGetMetrics_MixedProcess() {
+ doReturn(true)
+ .when(mShowmapSnapshotHelper)
+ .isProcessOomScoreAbove(
+ eq("com.google.android.googlequicksearchbox:search"), anyLong(), anyInt());
+ doReturn(true)
+ .when(mShowmapSnapshotHelper)
+ .isProcessOomScoreAbove(
+ eq("com.google.android.connectivitymonitor"), anyLong(), anyInt());
+ doReturn(false)
+ .when(mShowmapSnapshotHelper)
+ .isProcessOomScoreAbove(eq("com.android.systemui"), anyLong(), anyInt());
+ doReturn(false)
+ .when(mShowmapSnapshotHelper)
+ .isProcessOomScoreAbove(eq("system_server"), anyLong(), anyInt());
+ mShowmapSnapshotHelper.setUp(VALID_OUTPUT_DIR, MIXED_PROCESS_LIST);
+ mShowmapSnapshotHelper.setMetricNameIndex(METRIC_INDEX_STR);
+ assertTrue(mShowmapSnapshotHelper.startCollecting());
+ Map<String, String> metrics = mShowmapSnapshotHelper.getMetrics();
+ assertFalse(metrics.isEmpty());
+ for (String processName : CACHED_PROCESS_LIST) {
+ assertTrue(
+ metrics.containsKey(
+ constructKey(
+ String.format(
+ ShowmapSnapshotHelper
+ .OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN,
+ "rss"),
+ processName)));
+ assertTrue(
+ metrics.containsKey(
+ constructKey(
+ String.format(
+ ShowmapSnapshotHelper
+ .OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN,
+ "pss"),
+ processName)));
+ }
+ assertTrue(metrics.containsKey(ShowmapSnapshotHelper.OUTPUT_FILE_PATH_KEY));
+ }
+
+ /** Test isProcessOomScoreAbove() from cached process only. */
+ @Test
+ public void testGetMetrics_CachedProcess() throws IOException {
+ doReturn("1000")
+ .when(mShowmapSnapshotHelper)
+ .executeShellCommand(contains("oom_score_adj"));
+ mShowmapSnapshotHelper.setUp(VALID_OUTPUT_DIR, CACHED_PROCESS_LIST);
+ mShowmapSnapshotHelper.setMetricNameIndex(METRIC_INDEX_STR);
+ assertTrue(mShowmapSnapshotHelper.startCollecting());
+ Map<String, String> metrics = mShowmapSnapshotHelper.getMetrics();
+ assertFalse(metrics.isEmpty());
+ for (String processName : CACHED_PROCESS_LIST) {
+ assertTrue(
+ metrics.containsKey(
+ constructKey(
+ String.format(
+ ShowmapSnapshotHelper
+ .OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN,
+ "rss"),
+ processName)));
+ assertTrue(
+ metrics.containsKey(
+ constructKey(
+ String.format(
+ ShowmapSnapshotHelper
+ .OUTPUT_IMPERCEPTIBLE_METRIC_PATTERN,
+ "pss"),
+ processName)));
+ }
+ assertTrue(metrics.containsKey(ShowmapSnapshotHelper.OUTPUT_FILE_PATH_KEY));
+ }
+
/**
* Test all process flag return more than 2 processes metrics at least.
*/
@@ -167,7 +256,6 @@
Map<String, String> metrics = mShowmapSnapshotHelper.getMetrics();
assertTrue(metrics.size() > 2);
assertTrue(metrics.containsKey(ShowmapSnapshotHelper.OUTPUT_FILE_PATH_KEY));
-
}
@Test
@@ -220,7 +308,6 @@
mShowmapSnapshotHelper.setAllProcesses();
assertTrue(mShowmapSnapshotHelper.startCollecting());
Map<String, String> metrics = mShowmapSnapshotHelper.getMetrics();
-
assertTrue(metrics.size() != 0);
// process count, process with child process count and path to snapshot file in the output
@@ -235,6 +322,19 @@
// At least one process (i.e init) will have child process
assertTrue(parentWithChildProcessSet.size() > 0);
assertTrue(metrics.containsKey(ShowmapSnapshotHelper.CHILD_PROCESS_COUNT_PREFIX + "_init"));
+ // These assertions are for checking SKIP_PROCESS
+ assertFalse(
+ metrics.containsKey(
+ ShowmapSnapshotHelper.PARENT_PROCESS_STRING
+ + "_init_"
+ + ShowmapSnapshotHelper.CHILD_PROCESS_STRING
+ + "_logcat"));
+ assertFalse(
+ metrics.containsKey(
+ ShowmapSnapshotHelper.PARENT_PROCESS_STRING
+ + "_init_"
+ + ShowmapSnapshotHelper.CHILD_PROCESS_STRING
+ + "_sh"));
}
@Test
@@ -274,6 +374,9 @@
}
private void testProcessList(String metricIndexStr, String... processNames) {
+ doReturn(false)
+ .when(mShowmapSnapshotHelper)
+ .isProcessOomScoreAbove(anyString(), anyLong(), anyInt());
mShowmapSnapshotHelper.setUp(VALID_OUTPUT_DIR, processNames);
mShowmapSnapshotHelper.setMetricNameIndex(metricIndexStr);
assertTrue(mShowmapSnapshotHelper.startCollecting());
diff --git a/libraries/health/rules/src/android/platform/test/rule/QuickstepPressureRule.java b/libraries/health/rules/src/android/platform/test/rule/QuickstepPressureRule.java
index 804d0a7..2ff3f48 100644
--- a/libraries/health/rules/src/android/platform/test/rule/QuickstepPressureRule.java
+++ b/libraries/health/rules/src/android/platform/test/rule/QuickstepPressureRule.java
@@ -29,18 +29,22 @@
// TODO: b/243991517 Replace static sleep with process not crashing verification.
private static final long MIN_CRASH_WAIT_TIMEOUT = 500;
private static final long UI_RESPONSE_TIMEOUT_MSECS = 10000;
+ @VisibleForTesting static final String PACKAGES_OPTION = "quickstep-packages";
- private final String[] mPackages;
+ private String[] mPackages;
public QuickstepPressureRule(String... packages) {
- if (packages.length == 0) {
- throw new IllegalArgumentException("Must supply an application to open.");
- }
mPackages = packages;
}
@Override
protected void starting(Description description) {
+ String packageOption = getArguments().getString(PACKAGES_OPTION);
+ mPackages = packageOption == null ? mPackages : packageOption.split(",");
+ if (mPackages.length == 0) {
+ throw new IllegalArgumentException("Must supply an application to open.");
+ }
+
// Start each app in sequence.
for (String pkg : mPackages) {
startActivity(pkg);
diff --git a/libraries/health/rules/tests/src/android/platform/test/rule/QuickstepPressureRuleTest.java b/libraries/health/rules/tests/src/android/platform/test/rule/QuickstepPressureRuleTest.java
index 82836dd..771f6f8 100644
--- a/libraries/health/rules/tests/src/android/platform/test/rule/QuickstepPressureRuleTest.java
+++ b/libraries/health/rules/tests/src/android/platform/test/rule/QuickstepPressureRuleTest.java
@@ -16,8 +16,11 @@
package android.platform.test.rule;
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.fail;
+import android.os.Bundle;
+
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
@@ -32,9 +35,11 @@
public class QuickstepPressureRuleTest {
/** Tests that this rule will fail to register if no apps are supplied. */
@Test
- public void testNoAppToOpenFails() {
+ public void testNoAppToOpenFails() throws Throwable {
try {
- QuickstepPressureRule rule = new QuickstepPressureRule();
+ TestableQuickstepPressureRule rule = new TestableQuickstepPressureRule();
+ rule.apply(rule.getTestStatement(), Description.createTestDescription("clzz", "mthd"))
+ .evaluate();
fail("An illegal argument error should have been thrown, but wasn't.");
} catch (IllegalArgumentException e) {
return;
@@ -67,8 +72,22 @@
.inOrder();
}
+ /** Tests that this rule will open one app from option before the test, if supplied. */
+ @Test
+ public void testOneAppToOpenFromOption() throws Throwable {
+ Bundle PackageOption = new Bundle();
+ PackageOption.putString(QuickstepPressureRule.PACKAGES_OPTION, "option.package.name");
+ TestableQuickstepPressureRule rule = new TestableQuickstepPressureRule(PackageOption);
+ rule.apply(rule.getTestStatement(), Description.createTestDescription("clzz", "mthd"))
+ .evaluate();
+ assertThat(rule.getOperations())
+ .containsExactly("start option.package.name", "test")
+ .inOrder();
+ }
+
private static class TestableQuickstepPressureRule extends QuickstepPressureRule {
private List<String> mOperations = new ArrayList<>();
+ private Bundle mBundle = new Bundle();
public TestableQuickstepPressureRule(String app) {
super(app);
@@ -78,11 +97,20 @@
super(apps);
}
+ public TestableQuickstepPressureRule(Bundle bundle) {
+ mBundle = bundle;
+ }
+
@Override
void startActivity(String pkg) {
mOperations.add(String.format("start %s", pkg));
}
+ @Override
+ protected Bundle getArguments() {
+ return mBundle;
+ }
+
public List<String> getOperations() {
return mOperations;
}
diff --git a/libraries/power-helper/Android.bp b/libraries/power-helper/Android.bp
index 230791b..80bbfbe 100644
--- a/libraries/power-helper/Android.bp
+++ b/libraries/power-helper/Android.bp
@@ -27,7 +27,7 @@
libs: [
"android.test.runner.stubs",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"android.test.base.stubs",
],
static_libs: ["junit"],
diff --git a/libraries/power-helper/sample/Android.bp b/libraries/power-helper/sample/Android.bp
index f55a852..56dfcc9 100644
--- a/libraries/power-helper/sample/Android.bp
+++ b/libraries/power-helper/sample/Android.bp
@@ -9,7 +9,7 @@
static_libs: [
"PowerTestHelper-src",
"android.test.runner.stubs",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"android.test.base.stubs",
"junit",
],
diff --git a/libraries/power-helper/src/com/android/helper/PowerTestHelper.java b/libraries/power-helper/src/com/android/helper/PowerTestHelper.java
index f290205..266f8f0 100644
--- a/libraries/power-helper/src/com/android/helper/PowerTestHelper.java
+++ b/libraries/power-helper/src/com/android/helper/PowerTestHelper.java
@@ -16,20 +16,19 @@
package com.android.helper;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.test.InstrumentationTestRunner;
+
+import androidx.test.uiautomator.UiAutomatorTestCase;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
-import java.util.List;
import java.util.Properties;
-import android.os.Bundle;
-import android.os.Environment;
-
-import android.os.SystemClock;
-import android.support.test.uiautomator.UiAutomatorTestCase;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
public class PowerTestHelper extends UiAutomatorTestCase {
private final static String PARAM_CONFIG = "conf";
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
index 22a45bd..27ecd2e 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
@@ -59,10 +59,17 @@
open class GoldenImagePathManager @JvmOverloads constructor(
open val appContext: Context,
open val assetsPathRelativeToBuildRoot: String = "assets",
- open val deviceLocalPath: String = getDeviceOutputDirectory(appContext),
+ open var deviceLocalPath: String = getDeviceOutputDirectory(appContext),
open val pathConfig: PathConfig = getSimplePathConfig()
) {
+ init {
+ val robolectricOverride = System.getProperty("robolectric.artifacts.dir")
+ if (Build.FINGERPRINT.contains("robolectric") && !robolectricOverride.isNullOrEmpty()) {
+ deviceLocalPath = robolectricOverride
+ }
+ }
+
public val imageExtension = "png"
/*
diff --git a/robolab/roboStandaloneProj/tests/Android.bp b/robolab/roboStandaloneProj/tests/Android.bp
index 3e5bf10..cc57001 100644
--- a/robolab/roboStandaloneProj/tests/Android.bp
+++ b/robolab/roboStandaloneProj/tests/Android.bp
@@ -36,6 +36,4 @@
instrumentation_for: "MyRoboApplication",
upstream: true,
-
- test_suites: ["general-tests"],
}
diff --git a/tests/perf/PowerPerfTest/Android.bp b/tests/perf/PowerPerfTest/Android.bp
index bdf537b..3a159b9 100644
--- a/tests/perf/PowerPerfTest/Android.bp
+++ b/tests/perf/PowerPerfTest/Android.bp
@@ -21,7 +21,7 @@
sdk_version: "current",
static_libs: [
"PowerTestHelper-src",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"junit",
],
libs: ["android.test.base.stubs"],