meminfo: Add ReadVmallocInfo()

This is to replace occurrences of get_allocated_vmalloc_memory().
Splitting into libmeminfo already found a bug with current code which
failed to account for memory allocated by modules due to addition of
the extra [%module_name%] in __builtin_return_address().

  See: https://elixir.bootlin.com/linux/latest/source/kernel/kallsyms.c#L373

Also improves the performance a bit in the process.

Bug: 119639955
Bug: 111694435
Test: libmeminfo_test 1 --gtest_filter=SysMemInfoParser.TestVmallocInfo
Test: libmeminfo_benchmark --benchmark_filter=BM_VmallocInfo_*
Result:
----------------------------------------------------------------
Benchmark                         Time           CPU Iterations
----------------------------------------------------------------
BM_VmallocInfo_old_fixed     459239 ns     457268 ns       1532
BM_VmallocInfo_new           386032 ns     384353 ns       1821
----------------------------------------------------------------

Change-Id: I1b6606ac73b5cc2dac31d24487b462ec9abfb2ef
Signed-off-by: Sandeep Patil <[email protected]>
diff --git a/libmeminfo_benchmark.cpp b/libmeminfo_benchmark.cpp
index 2660a4d..e9cb763 100644
--- a/libmeminfo_benchmark.cpp
+++ b/libmeminfo_benchmark.cpp
@@ -26,6 +26,8 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 
 #include <benchmark/benchmark.h>
 
@@ -397,4 +399,62 @@
 }
 BENCHMARK(BM_MemInfoWithZram_new);
 
+// Current implementation is in frameworks/base/core/jni/android_os_Debug.cpp.
+// That implementation is still buggy and it skips over vmalloc allocated memory by kernel modules.
+// This is the *fixed* version of the same implementation intended for benchmarking against the new
+// one.
+static uint64_t get_allocated_vmalloc_memory(const std::string& vm_file) {
+    char line[1024];
+
+    uint64_t vmalloc_allocated_size = 0;
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(vm_file.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return 0;
+    }
+
+    while (true) {
+        if (fgets(line, 1024, fp.get()) == NULL) {
+            break;
+        }
+
+        // check to see if there are pages mapped in vmalloc area
+        if (!strstr(line, "pages=")) {
+            continue;
+        }
+
+        long nr_pages;
+        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
+            vmalloc_allocated_size += (nr_pages * getpagesize());
+        } else if (sscanf(line, "%*x-%*x %*ld %*s %*s pages=%ld", &nr_pages) == 1) {
+            // The second case is for kernel modules. If allocation comes from the module,
+            // kernel puts an extra string containing the module name before "pages=" in
+            // the line.
+            //    See: https://elixir.bootlin.com/linux/latest/source/kernel/kallsyms.c#L373
+            vmalloc_allocated_size += (nr_pages * getpagesize());
+        }
+    }
+    return vmalloc_allocated_size;
+}
+
+static void BM_VmallocInfo_old_fixed(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string vmallocinfo =
+            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+    for (auto _ : state) {
+        CHECK_EQ(get_allocated_vmalloc_memory(vmallocinfo), 29884416);
+    }
+}
+BENCHMARK(BM_VmallocInfo_old_fixed);
+
+static void BM_VmallocInfo_new(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string vmallocinfo =
+            ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+    for (auto _ : state) {
+        SysMemInfo smi;
+        CHECK_EQ(smi.ReadVmallocInfo(vmallocinfo), 29884416);
+    }
+}
+BENCHMARK(BM_VmallocInfo_new);
+
 BENCHMARK_MAIN();