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>
This commit is contained in:
Sandeep Patil 2018-12-29 14:34:20 -08:00
parent 3009be5cb6
commit c24f1e3c63
5 changed files with 1935 additions and 2 deletions

View file

@ -50,12 +50,16 @@ class SysMemInfo final {
// Parse /proc/meminfo and read values that are needed
bool ReadMemInfo(const std::string& path = "/proc/meminfo");
bool ReadMemInfo(const std::vector<std::string>& tags,
const std::string& path = "/proc/meminfo");
bool ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
const std::string& path = "/proc/meminfo");
bool ReadMemInfo(std::vector<uint64_t>* out, const std::string& path = "/proc/meminfo");
// Parse /proc/vmallocinfo and return total physical memory mapped
// in vmalloc area by the kernel.
// Note that this deliberately ignores binder buffers. They are _always_
// mapped in a process and are counted for in each process.
uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");
// getters
uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }

View file

@ -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 @@ Hugepagesize: 2048 kB)meminfo";
}
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();

View file

@ -30,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
using namespace std;
using namespace android::meminfo;
@ -516,6 +517,66 @@ Hugepagesize: 2048 kB)meminfo";
EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
}
TEST(SysMemInfoParser, TestVmallocInfoNoMemory) {
std::string vmallocinfo =
R"vmallocinfo(0x0000000000000000-0x0000000000000000 69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap)vmallocinfo";
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
std::string file = std::string(tf.path);
SysMemInfo smi;
EXPECT_EQ(smi.ReadVmallocInfo(file), 0);
}
TEST(SysMemInfoParser, TestVmallocInfoKernel) {
std::string vmallocinfo =
R"vmallocinfo(0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc)vmallocinfo";
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
std::string file = std::string(tf.path);
SysMemInfo smi;
EXPECT_EQ(smi.ReadVmallocInfo(file), getpagesize());
}
TEST(SysMemInfoParser, TestVmallocInfoModule) {
std::string vmallocinfo =
R"vmallocinfo(0x0000000000000000-0x0000000000000000 28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
std::string file = std::string(tf.path);
SysMemInfo smi;
EXPECT_EQ(smi.ReadVmallocInfo(file), 6 * getpagesize());
}
TEST(SysMemInfoParser, TestVmallocInfoAll) {
std::string vmallocinfo =
R"vmallocinfo(0x0000000000000000-0x0000000000000000 69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap
0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
0x0000000000000000-0x0000000000000000 28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
std::string file = std::string(tf.path);
SysMemInfo smi;
EXPECT_EQ(smi.ReadVmallocInfo(file), 7 * getpagesize());
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc <= 1) {

View file

@ -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 @@ bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, std::vector<u
});
}
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

File diff suppressed because it is too large Load diff