Report amount of used memory in a VM
Add a benchmark which boots Microdroid in non-debuggable mode and
reports the amount of used memory from /proc/meminfo. This is different
from an existing host-side test which also reports stats from
/proc/meminfo but runs in full-debug mode and with root privileges. This
new test is therefore more representative of a production environment.
Test: atest MicrodroidBenchmarks
Change-Id: I03a528bc09a4d4a08767653601f14bf93018c907
diff --git a/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl b/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
index afcf989..9fdf190 100644
--- a/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
@@ -22,4 +22,7 @@
/** Reads a file and returns the elapsed seconds for the reading. */
double readFile(String filename, long fileSizeBytes, boolean isRand);
+
+ /** Returns an entry from /proc/meminfo. */
+ long getMemInfoEntry(String name);
}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 908da61..f236e47 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -272,4 +272,65 @@
forceStop(vm);
}
}
+
+ @Test
+ public void testMemoryUsage() throws Exception {
+ final String vmName = "test_vm_mem_usage";
+ VirtualMachineConfig.Builder builder = mInner.newVmConfigBuilder(
+ "assets/vm_config_io.json");
+ VirtualMachineConfig config = builder.debugLevel(DebugLevel.NONE).memoryMib(256).build();
+ mInner.forceCreateNewVirtualMachine(vmName, config);
+ VirtualMachine vm = mInner.getVirtualMachineManager().get(vmName);
+ MemoryUsageListener listener = new MemoryUsageListener();
+ listener.runToFinish(TAG, vm);
+
+ double mem_overall = 256.0;
+ double mem_total = (double) listener.mMemTotal / 1024.0;
+ double mem_free = (double) listener.mMemFree / 1024.0;
+ double mem_avail = (double) listener.mMemAvailable / 1024.0;
+ double mem_buffers = (double) listener.mBuffers / 1024.0;
+ double mem_cached = (double) listener.mCached / 1024.0;
+ double mem_slab = (double) listener.mSlab / 1024.0;
+
+ double mem_kernel = mem_overall - mem_total;
+ double mem_used = mem_total - mem_free - mem_buffers - mem_cached - mem_slab;
+ double mem_unreclaimable = mem_total - mem_avail;
+
+ Bundle bundle = new Bundle();
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_kernel_MB", mem_kernel);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_used_MB", mem_used);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_buffers_MB", mem_buffers);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_cached_MB", mem_cached);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_slab_MB", mem_slab);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_unreclaimable_MB", mem_unreclaimable);
+ mInstrumentation.sendStatus(0, bundle);
+ }
+
+ private static class MemoryUsageListener extends VmEventListener {
+ public long mMemTotal;
+ public long mMemFree;
+ public long mMemAvailable;
+ public long mBuffers;
+ public long mCached;
+ public long mSlab;
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ try {
+ IBenchmarkService service =
+ IBenchmarkService.Stub.asInterface(
+ vm.connectToVsockServer(IBenchmarkService.SERVICE_PORT).get());
+
+ mMemTotal = service.getMemInfoEntry("MemTotal");
+ mMemFree = service.getMemInfoEntry("MemFree");
+ mMemAvailable = service.getMemInfoEntry("MemAvailable");
+ mBuffers = service.getMemInfoEntry("Buffers");
+ mCached = service.getMemInfoEntry("Cached");
+ mSlab = service.getMemInfoEntry("Slab");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ forceStop(vm);
+ }
+ }
}
diff --git a/tests/benchmark/src/native/benchmarkbinary.cpp b/tests/benchmark/src/native/benchmarkbinary.cpp
index 5523579..2558a7d 100644
--- a/tests/benchmark/src/native/benchmarkbinary.cpp
+++ b/tests/benchmark/src/native/benchmarkbinary.cpp
@@ -25,10 +25,13 @@
#include <binder_rpc_unstable.hpp>
#include <chrono>
+#include <fstream>
#include <random>
#include <string>
#include "android-base/logging.h"
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
using aidl::android::system::virtualmachineservice::IVirtualMachineService;
using android::base::ErrnoError;
@@ -39,18 +42,35 @@
namespace {
constexpr uint64_t kBlockSizeBytes = 4096;
+template <typename T>
+static ndk::ScopedAStatus resultStatus(const T& result) {
+ if (!result.ok()) {
+ std::stringstream error;
+ error << result.error();
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ error.str().c_str());
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
class IOBenchmarkService : public aidl::com::android::microdroid::testservice::BnBenchmarkService {
public:
ndk::ScopedAStatus readFile(const std::string& filename, int64_t fileSizeBytes, bool isRand,
double* out) override {
- if (auto res = read_file(filename, fileSizeBytes, isRand); res.ok()) {
+ auto res = read_file(filename, fileSizeBytes, isRand);
+ if (res.ok()) {
*out = res.value();
- } else {
- std::stringstream error;
- error << "Failed reading file: " << res.error();
- return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
- error.str().c_str());
}
+ return resultStatus(res);
+ }
+
+ ndk::ScopedAStatus getMemInfoEntry(const std::string& name, int64_t* out) override {
+ auto value = read_meminfo_entry(name);
+ if (!value.ok()) {
+ return resultStatus(value);
+ }
+
+ *out = (int64_t)value.value();
return ndk::ScopedAStatus::ok();
}
@@ -87,6 +107,32 @@
}
return {((double)clock() - start) / CLOCKS_PER_SEC};
}
+
+ Result<size_t> read_meminfo_entry(const std::string& stat) {
+ std::ifstream fs("/proc/meminfo");
+ if (!fs.is_open()) {
+ return Error() << "could not open /proc/meminfo";
+ }
+
+ std::string line;
+ while (std::getline(fs, line)) {
+ auto elems = android::base::Split(line, ":");
+ if (elems[0] != stat) continue;
+
+ std::string str = android::base::Trim(elems[1]);
+ if (android::base::EndsWith(str, " kB")) {
+ str = str.substr(0, str.length() - 3);
+ }
+
+ size_t value;
+ if (!android::base::ParseUint(str, &value)) {
+ return ErrnoError() << "failed to parse \"" << str << "\" as size_t";
+ }
+ return {value};
+ }
+
+ return Error() << "entry \"" << stat << "\" not found";
+ }
};
Result<void> run_io_benchmark_tests() {