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();