Merge changes Ifec1533f,I66267892,I45ab976c into main
* changes:
Test PSCI MEM_PROTECT
Add Event class for KvmHypTracer
Generalize KvmHypTracer
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index 0280652..4a61016 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -47,6 +47,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@@ -121,9 +122,14 @@
@Test
public void testNoLongHypSections() throws Exception {
- assumeTrue("Skip without hypervisor tracing", KvmHypTracer.isSupported(getDevice()));
+ String[] hypEvents = {
+ "hyp_enter", "hyp_exit"
+ };
- KvmHypTracer tracer = new KvmHypTracer(getDevice());
+ assumeTrue("Skip without hypervisor tracing",
+ KvmHypTracer.isSupported(getDevice(), hypEvents));
+
+ KvmHypTracer tracer = new KvmHypTracer(getDevice(), hypEvents);
String result = tracer.run(COMPOSD_CMD_BIN + " test-compile");
assertWithMessage("Failed to test compilation VM.")
.that(result).ignoringCase().contains("all ok");
@@ -134,6 +140,36 @@
}
@Test
+ public void testPsciMemProtect() throws Exception {
+ String[] hypEvents = {
+ "psci_mem_protect"
+ };
+
+ assumeTrue("Skip without hypervisor tracing",
+ KvmHypTracer.isSupported(getDevice(), hypEvents));
+ KvmHypTracer tracer = new KvmHypTracer(getDevice(), hypEvents);
+
+ /* We need to wait for crosvm to die so all the VM pages are reclaimed */
+ String result = tracer.run(COMPOSD_CMD_BIN + " test-compile && killall -w crosvm || true");
+ assertWithMessage("Failed to test compilation VM.")
+ .that(result).ignoringCase().contains("all ok");
+
+ List<Integer> values = tracer.getPsciMemProtect();
+
+ assertWithMessage("PSCI MEM_PROTECT events not recorded")
+ .that(values.size()).isGreaterThan(2);
+
+ assertWithMessage("PSCI MEM_PROTECT counter not starting from 0")
+ .that(values.get(0)).isEqualTo(0);
+
+ assertWithMessage("PSCI MEM_PROTECT counter not ending with 0")
+ .that(values.get(values.size() - 1)).isEqualTo(0);
+
+ assertWithMessage("PSCI MEM_PROTECT counter didn't increment")
+ .that(Collections.max(values)).isGreaterThan(0);
+ }
+
+ @Test
public void testCameraAppStartupTime() throws Exception {
String[] launchIntentPackages = {
"com.android.camera2",
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
index 0d8ee96..5c72358 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
@@ -30,24 +30,63 @@
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
+class KvmHypEvent {
+ public final int cpu;
+ public final double timestamp;
+ public final String name;
+ public final String args;
+ public final boolean valid;
+
+ private static final Pattern LOST_EVENT_PATTERN = Pattern.compile(
+ "^CPU:[0-9]* \\[LOST ([0-9]*) EVENTS\\]");
+
+ public KvmHypEvent(String str) {
+ Matcher matcher = LOST_EVENT_PATTERN.matcher(str);
+ if (matcher.find())
+ throw new OutOfMemoryError("Lost " + matcher.group(1) + " events");
+
+ Pattern pattern = Pattern.compile(
+ "^\\[([0-9]*)\\][ \t]*([0-9]*\\.[0-9]*): (\\S+) (.*)");
+
+ matcher = pattern.matcher(str);
+ if (!matcher.find()) {
+ valid = false;
+ cpu = 0;
+ timestamp = 0;
+ name = "";
+ args = "";
+ CLog.w("Failed to parse hyp event: " + str);
+ return;
+ }
+
+ cpu = Integer.parseInt(matcher.group(1));
+ timestamp = Double.parseDouble(matcher.group(2));
+ name = matcher.group(3);
+ args = matcher.group(4);
+ valid = true;
+ }
+
+ public String toString() {
+ return String.format(
+ "[%03d]\t%f: %s %s", cpu, timestamp, name, args);
+ }
+}
+
/** This class provides utilities to interact with the hyp tracing subsystem */
public final class KvmHypTracer {
private static final String HYP_TRACING_ROOT = "/sys/kernel/tracing/hyp/";
- private static final String HYP_EVENTS[] = { "hyp_enter", "hyp_exit" };
private static final int DEFAULT_BUF_SIZE_KB = 4 * 1024;
- private static final Pattern LOST_EVENT_PATTERN = Pattern.compile(
- "^CPU:[0-9]* \\[LOST ([0-9]*) EVENTS\\]");
- private static final Pattern EVENT_PATTERN = Pattern.compile(
- "^\\[([0-9]*)\\][ \t]*([0-9]*\\.[0-9]*): (" + String.join("|", HYP_EVENTS) + ") (.*)");
private final CommandRunner mRunner;
private final ITestDevice mDevice;
private final int mNrCpus;
+ private final String mHypEvents[];
private final ArrayList<File> mTraces;
@@ -59,22 +98,23 @@
return "events/hyp/" + event + "/";
}
- public static boolean isSupported(ITestDevice device) throws Exception {
- for (String event: HYP_EVENTS) {
+ public static boolean isSupported(ITestDevice device, String[] events) throws Exception {
+ for (String event: events) {
if (!device.doesFileExist(HYP_TRACING_ROOT + eventDir(event) + "/enable"))
return false;
}
return true;
}
- public KvmHypTracer(@Nonnull ITestDevice device) throws Exception {
- assertWithMessage("Hypervisor tracing not supported")
- .that(isSupported(device)).isTrue();
+ public KvmHypTracer(@Nonnull ITestDevice device, String[] events) throws Exception {
+ assertWithMessage("Hypervisor events " + String.join(",", events) + " not supported")
+ .that(isSupported(device, events)).isTrue();
mDevice = device;
mRunner = new CommandRunner(mDevice);
mTraces = new ArrayList<File>();
mNrCpus = Integer.parseInt(mRunner.run("nproc"));
+ mHypEvents = events;
}
public String run(String payload_cmd) throws Exception {
@@ -83,7 +123,7 @@
setNode("tracing_on", 0);
mRunner.run("echo 0 | tee " + HYP_TRACING_ROOT + "events/*/*/enable");
setNode("buffer_size_kb", DEFAULT_BUF_SIZE_KB);
- for (String event: HYP_EVENTS)
+ for (String event: mHypEvents)
setNode(eventDir(event) + "/enable", 1);
setNode("trace", 0);
@@ -96,15 +136,21 @@
cmd += "CPU" + i + "_TRACE_PIPE_PID=$!;";
}
+ String cmd_script = mRunner.run("mktemp -t cmd_script.XXXXXXXXXX");
+ mRunner.run("echo '" + payload_cmd + "' > " + cmd_script);
+
/* Run the payload with tracing enabled */
cmd += "echo 1 > tracing_on;";
String cmd_stdout = mRunner.run("mktemp -t cmd_stdout.XXXXXXXXXX");
- cmd += payload_cmd + " > " + cmd_stdout + ";";
+ cmd += "sh " + cmd_script + " > " + cmd_stdout + ";";
cmd += "echo 0 > tracing_on;";
- /* Actively kill the cat subprocesses as trace_pipe is blocking */
- for (int i = 0; i < mNrCpus; i++)
+ /* Wait for cat to finish reading the pipe interface before killing it */
+ for (int i = 0; i < mNrCpus; i++) {
+ cmd += "while $(test '$(ps -o S -p $CPU" + i
+ + "_TRACE_PIPE_PID | tail -n 1)' = 'R'); do sleep 1; done;";
cmd += "kill -9 $CPU" + i + "_TRACE_PIPE_PID;";
+ }
cmd += "wait";
/*
@@ -116,6 +162,8 @@
*/
mRunner.run(cmd);
+ mRunner.run("rm -f " + cmd_script);
+
for (String t: trace_pipes) {
File trace = mDevice.pullFile(t);
assertNotNull(trace);
@@ -128,37 +176,57 @@
return res;
}
+ private boolean hasEvents(String[] events) {
+ for (String event : events) {
+ if (!Arrays.asList(mHypEvents).contains(event)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private KvmHypEvent getNextEvent(BufferedReader br) throws Exception {
+ KvmHypEvent event;
+ String l;
+
+ if ((l = br.readLine()) == null)
+ return null;
+
+ event = new KvmHypEvent(l);
+ if (!event.valid)
+ return null;
+
+ return event;
+ }
+
public SimpleStats getDurationStats() throws Exception {
+ String[] reqEvents = {"hyp_enter", "hyp_exit"};
SimpleStats stats = new SimpleStats();
+ assertWithMessage("KvmHypTracer() is missing events " + String.join(",", reqEvents))
+ .that(hasEvents(reqEvents)).isTrue();
+
for (File trace: mTraces) {
BufferedReader br = new BufferedReader(new FileReader(trace));
double last = 0.0, hyp_enter = 0.0;
- String l, prev_event = "";
- while ((l = br.readLine()) != null) {
- Matcher matcher = LOST_EVENT_PATTERN.matcher(l);
- if (matcher.find())
- throw new OutOfMemoryError("Lost " + matcher.group(1) + " events");
+ String prev_event = "";
+ KvmHypEvent hypEvent;
- matcher = EVENT_PATTERN.matcher(l);
- if (!matcher.find()) {
- CLog.w("Failed to parse hyp event: " + l);
- continue;
- }
-
- int cpu = Integer.parseInt(matcher.group(1));
+ while ((hypEvent = getNextEvent(br)) != null) {
+ int cpu = hypEvent.cpu;
if (cpu < 0 || cpu >= mNrCpus)
throw new ParseException("Incorrect CPU number: " + cpu, 0);
- double cur = Double.parseDouble(matcher.group(2));
+ double cur = hypEvent.timestamp;
if (cur < last)
throw new ParseException("Time must not go backward: " + cur, 0);
last = cur;
- String event = matcher.group(3);
+ String event = hypEvent.name;
if (event.equals(prev_event)) {
- throw new ParseException("Hyp event found twice in a row: " + trace + " - " + l,
- 0);
+ throw new ParseException("Hyp event found twice in a row: " +
+ trace + " - " + hypEvent, 0);
}
switch (event) {
@@ -170,7 +238,7 @@
hyp_enter = cur;
break;
default:
- throw new ParseException("Unexpected line in trace" + l, 0);
+ throw new ParseException("Unexpected line in trace " + hypEvent, 0);
}
prev_event = event;
}
@@ -178,4 +246,55 @@
return stats;
}
+
+ public List<Integer> getPsciMemProtect() throws Exception {
+ String[] reqEvents = {"psci_mem_protect"};
+ List<Integer> psciMemProtect = new ArrayList<>();
+
+ assertWithMessage("KvmHypTracer() is missing events " + String.join(",", reqEvents))
+ .that(hasEvents(reqEvents)).isTrue();
+
+ BufferedReader[] brs = new BufferedReader[mTraces.size()];
+ KvmHypEvent[] next = new KvmHypEvent[mTraces.size()];
+
+ for (int i = 0; i < mTraces.size(); i++) {
+ brs[i] = new BufferedReader(new FileReader(mTraces.get(i)));
+ next[i] = getNextEvent(brs[i]);
+ }
+
+ while (true) {
+ double oldest = Double.MAX_VALUE;
+ int oldestIdx = -1;
+
+ for (int i = 0; i < mTraces.size(); i ++) {
+ if ((next[i] != null) && (next[i].timestamp < oldest)) {
+ oldest = next[i].timestamp;
+ oldestIdx = i;
+ }
+ }
+
+ if (oldestIdx < 0)
+ break;
+
+ Pattern pattern = Pattern.compile(
+ "count=([0-9]*) was=([0-9]*)");
+ Matcher matcher = pattern.matcher(next[oldestIdx].args);
+ if (!matcher.find()) {
+ throw new ParseException("Unexpected psci_mem_protect event: " +
+ next[oldestIdx], 0);
+ }
+
+ int count = Integer.parseInt(matcher.group(1));
+ int was = Integer.parseInt(matcher.group(2));
+
+ if (psciMemProtect.isEmpty()) {
+ psciMemProtect.add(was);
+ }
+
+ psciMemProtect.add(count);
+ next[oldestIdx] = getNextEvent(brs[oldestIdx]);
+ }
+
+ return psciMemProtect;
+ }
}