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 <sspatil@google.com>
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
index 4ec1c99..8fd18d0 100644
--- a/libmeminfo/sysmeminfo.cpp
+++ b/libmeminfo/sysmeminfo.cpp
@@ -20,6 +20,7 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -27,6 +28,7 @@
 #include <cstdio>
 #include <fstream>
 #include <iterator>
+#include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -77,6 +79,38 @@
     });
 }
 
+uint64_t SysMemInfo::ReadVmallocInfo(const std::string& path) {
+    uint64_t vmalloc_total = 0;
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return vmalloc_total;
+    }
+
+    char line[1024];
+    while (fgets(line, 1024, fp.get()) != nullptr) {
+        // We are looking for lines like
+        // 0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2
+        // vmalloc 0x0000000000000000-0x0000000000000000    8192
+        // wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc Notice that if the caller is
+        // coming from a module, the kernel prints and extra "[module_name]" after the address and
+        // the symbol of the call site. This means we can't use the old sscanf() method of getting
+        // the # of pages.
+        char* p_start = strstr(line, "pages=");
+        if (p_start == nullptr) {
+            // we didn't find anything
+            continue;
+        }
+
+        p_start = strtok(p_start, " ");
+        long nr_pages;
+        if (sscanf(p_start, "pages=%ld", &nr_pages) == 1) {
+            vmalloc_total += (nr_pages * getpagesize());
+        }
+    }
+
+    return vmalloc_total;
+}
+
 // TODO: Delete this function if it can't match up with the c-like implementation below.
 // Currently, this added about 50 % extra overhead on hikey.
 #if 0