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:
parent
3009be5cb6
commit
c24f1e3c63
5 changed files with 1935 additions and 2 deletions
|
@ -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]; }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
1774
libmeminfo/testdata1/vmallocinfo
Normal file
1774
libmeminfo/testdata1/vmallocinfo
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue