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/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() {