Commit 1201c0fe authored by Ruben Laso Rodríguez's avatar Ruben Laso Rodríguez
Browse files

feat: use pfs library to extract info about memory pages from the '/proc/'

parent d6a3759d
......@@ -353,8 +353,6 @@ namespace {
migration::add_pids(children);
samples::update_PIDs_to_filter(children);
memory_info::update_memory_regions(samples::PIDs_to_filter);
}
if (update_mem && utils::time::time_until(last_mem_update, current_time) > secs_update_mem) {
......
......@@ -84,8 +84,6 @@ namespace migration {
}
}
memory_info::update_memory_regions(system_info::get_children());
if (verbose::print_with_lvl(verbose::DEFAULT_LVL)) {
const auto pages_proc = memory_info::n_pages_all_regions();
const auto pages_enough_info = perf_table.pages_with_enough_info();
......
......@@ -22,30 +22,17 @@
class mem_region : public mem_region_maps, public mem_region_numa_maps {
public:
mem_region(const pid_t pid, const size_t maps_index, const size_t numa_maps_index) :
mem_region_maps(pid, maps_index), mem_region_numa_maps(pid, numa_maps_index) {
}
mem_region(const pid_t pid, const std::string & line_maps, const size_t index_maps,
const std::string & line_numa_maps, const size_t index_numa_maps) :
mem_region_maps(line_maps, index_maps, pid), mem_region_numa_maps(line_numa_maps, index_numa_maps, pid) {
}
mem_region(mem_region_maps & maps, mem_region_numa_maps & numa_maps) :
mem_region_maps(maps), mem_region_numa_maps(numa_maps) {
}
[[nodiscard]] inline auto index_maps() const {
return mem_region_maps::index();
}
[[nodiscard]] inline auto index_numa_maps() const {
return mem_region_numa_maps::index();
mem_region(mem_region_maps maps, mem_region_numa_maps numa_maps) :
mem_region_maps(std::move(maps)), mem_region_numa_maps(std::move(numa_maps)) {
}
inline friend auto operator<<(std::ostream & os, const mem_region & m) -> std::ostream & {
const auto raw_perm = m.flags();
const auto perm = std::string(raw_perm.can_read ? "r" : "-") + std::string(raw_perm.can_write ? "w" : "-") +
std::string(raw_perm.can_execute ? "x" : "-");
os << utils::string::to_string_hex(m.begin()) << '-' << utils::string::to_string_hex(m.end()) << std::dec << ' '
<< m.bytes() << "B " << m.flags() << ' ' << "policy=" << m.policy() << ' ';
<< m.bytes() << "B " << perm << ' ' << "policy=" << m.policy() << ' ';
os << "Pages per node: ";
for (const auto n : system_info::nodes()) {
os << m.pages_per_node()[n] << (std::cmp_not_equal(n, m.pages_per_node().size() - 1) ? ',' : ' ');
......
......@@ -19,101 +19,48 @@
#include <stdexcept> // for runtime_error
#include <string> // for string, getline, operator<<
#include <sys/types.h> // for pid_t
#include <utility>
#include "utils/types.hpp" // for addr_t
#include "third_party_libs/pfs/include/pfs/procfs.hpp"
class mem_region_maps {
private:
static constexpr const char SHARED = 's';
static constexpr const char PRIVATE = 'p'; // copy on write
static constexpr const char READ = 'r';
static constexpr const char WRITE = 'w';
static constexpr const char EXECUTE = 'x';
static constexpr const char NO_PERMISSION = '-';
static constexpr const char * const STACK = "[stack]"; // Main process stack
static constexpr const char * const STACK_TID = "[heap:<tid>]"; // A thread's stack
static constexpr const char * const VDSO = "[vdso]"; // The virtual dynamically linked shared object
static constexpr const char * const HEAP = "[heap]"; // The process's heap
static constexpr const std::string_view STACK = "[stack]"; // Main process stack
static constexpr const std::string_view STACK_TID = "[heap:<tid>]"; // A thread's stack
static constexpr const std::string_view VDSO = "[vdso]"; // The virtual dynamically linked shared object
static constexpr const std::string_view HEAP = "[heap]"; // The process's heap
size_t index_;
pid_t tid_;
pid_t pid_;
addr_t begin_{};
addr_t end_{};
pfs::mem_region mem_region_;
size_t bytes_{};
std::array<char, 5> flags_{ '\0' };
uint32_t offset_{};
uint32_t device_maj_{};
uint32_t device_min_{};
uint32_t inode_{};
std::array<char, PATH_MAX> path_{ '\0' };
inline void parse_line(const std::string & line) {
std::sscanf(line.c_str(), "%lx-%lx %4c %x %x:%x %x %s", &begin_, &end_, flags_.data(), &offset_, &device_maj_,
&device_min_, &inode_, path_.data());
flags_.back() = '\0';
bytes_ = end_ - begin_;
}
public:
mem_region_maps(const pid_t pid, const size_t index) : index_(index), tid_(pid) {
const auto filename = "/proc/" + std::to_string(pid) + "/maps";
std::ifstream file(filename);
if (!file.good()) {
const auto error = "Cannot open file " + filename + ": " + strerror(errno);
throw std::runtime_error(error);
}
bool index_found = false;
size_t i = 0;
while (file.good()) {
std::string line;
std::getline(file, line);
++i;
if (std::cmp_equal(i, index)) {
index_found = true;
parse_line(line);
break;
}
}
if (!index_found) {
const auto error = "Cannot found line number " + std::to_string(index);
throw std::runtime_error(error);
}
}
mem_region_maps(const std::string & line_info, const size_t & index, const pid_t & tid) : index_(index), tid_(tid) {
parse_line(line_info);
mem_region_maps(const pid_t pid, pfs::mem_region region) :
pid_(pid), mem_region_(std::move(region)), bytes_(mem_region_.end_address - mem_region_.start_address) {
}
[[nodiscard]] inline auto index() const {
return index_;
mem_region_maps(const pid_t pid, const size_t index) :
pid_(pid),
mem_region_(pfs::procfs().get_task(pid).get_maps().at(index)),
bytes_(mem_region_.end_address - mem_region_.start_address) {
}
[[nodiscard]] inline auto begin() const {
return begin_;
return mem_region_.start_address;
}
[[nodiscard]] inline auto end() const {
return end_;
return mem_region_.end_address;
}
[[nodiscard]] inline auto bytes() const {
......@@ -121,59 +68,63 @@ public:
}
[[nodiscard]] inline auto flags() const {
return flags_.data();
return mem_region_.perm;
}
[[nodiscard]] inline auto path() const {
return path_;
return mem_region_.pathname;
}
[[nodiscard]] inline auto read() const -> bool {
return flags_[0] == READ;
return mem_region_.perm.can_read;
}
[[nodiscard]] inline auto write() const -> bool {
return flags_[1] == WRITE;
return mem_region_.perm.can_write;
}
[[nodiscard]] inline auto execute() const -> bool {
return flags_[2] == EXECUTE;
return mem_region_.perm.can_execute;
}
[[nodiscard]] inline auto is_private() const -> bool {
return flags_[4] == PRIVATE;
return mem_region_.perm.is_private;
}
[[nodiscard]] inline auto is_shared() const -> bool {
return flags_[4] == SHARED;
return mem_region_.perm.is_shared;
}
[[nodiscard]] inline auto heap() -> bool {
bool is_heap = false;
// First check if it is main process's stack
is_heap = strcmp(path_.data(), HEAP) == 0;
is_heap = mem_region_.pathname == HEAP;
if (!is_heap) {
std::array<char, PATH_MAX> heap_tid{ '\0' };
sprintf(heap_tid.data(), "[heap:%ul]", tid_);
is_heap = strcmp(path_.data(), heap_tid.data()) == 0;
std::string heap_tid(PATH_MAX, '\0');
sprintf(heap_tid.data(), "[heap:%ul]", pid_);
is_heap = mem_region_.pathname == heap_tid;
}
return is_heap;
}
[[nodiscard]] inline auto stack() const -> bool {
return strcmp(path_.data(), STACK) == 0;
return mem_region_.pathname == STACK;
}
[[nodiscard]] inline auto vdso() const -> bool {
return strcmp(path_.data(), VDSO) == 0;
return mem_region_.pathname == VDSO;
}
inline friend auto operator<<(std::ostream & os, const mem_region_maps & m) -> std::ostream & {
os << std::hex << m.begin_ << '-' << m.end_ << ' ' << m.flags_.data() << ' ' << m.offset_ << ' '
<< m.device_maj_ << ':' << m.device_min_ << ' ' << std::dec << m.inode_ << "\t\t\t" << m.path_.data();
inline friend auto operator<<(std::ostream & os, const mem_region_maps & mem_region) -> std::ostream & {
const auto & m = mem_region.mem_region_;
const auto perm = std::string(1, m.perm.can_read ? READ : NO_PERMISSION) +
std::string(1, m.perm.can_write ? WRITE : NO_PERMISSION) +
std::string(1, m.perm.can_execute ? EXECUTE : NO_PERMISSION);
os << std::hex << m.start_address << '-' << m.end_address << ' ' << perm << ' ' << m.offset << ' ' << m.device
<< ' ' << std::dec << m.inode << "\t\t\t" << m.pathname;
return os;
}
......
......@@ -26,9 +26,6 @@
class mem_region_numa_maps {
protected:
static constexpr size_t ADDRESS_POS = 0;
static constexpr size_t POLICY_POS = 1;
static constexpr const char * STACK_STR = "stack";
static constexpr const char * HEAP_STR = "heap";
static constexpr const char * HUGE_STR = "huge";
......@@ -38,14 +35,14 @@ private:
pid_t pid_; // PID of the process owning the memory page
// The first field of each line shows the starting address of the memory
// range. This field allows a correlation with the contents of the
// range. This field allows a correlation with the contents of the
// /proc/<pid>/maps file, which contains the end address of the range
// and other information, such as the access permissions and sharing.
addr_t address_{};
// The second field shows the memory policy currently in effect for the
// memory range. Note that the effective policy is not necessarily the
// policy installed by the process for that memory range. Specifically,
// memory range. Note that the effective policy is not necessarily the
// policy installed by the process for that memory range. Specifically,
// if the process installed a "default" policy for that range, the
// effective policy for that range will be the process policy, which may
// or may not be "default".
......@@ -55,16 +52,16 @@ private:
// The number of pages allocated on <node>. <nr_pages> includes
// only pages currently mapped by the process. Page migration
// and memory reclaim may have temporarily unmapped pages
// associated with this memory range. These pages may show up
// associated with this memory range. These pages may show up
// again only after the process has attempted to reference them.
// If the memory range represents a shared memory area or file
// mapping, other processes may currently have additional pages
// mapped in a corresponding memory range.
std::vector<size_t> pages_per_node_;
// The file backing the memory range. If the file is mapped as
// The file backing the memory range. If the file is mapped as
// private, write accesses may have generated COW (Copy-On-Write)
// pages in this memory range. These pages are displayed as
// pages in this memory range. These pages are displayed as
// anonymous pages.
std::string file_ = {};
......
......@@ -17,7 +17,9 @@
#include "utils/types.hpp" // for hres_clock, time_point
template<class T = uint64_t>
#include "third_party_libs/pfs/include/pfs/procfs.hpp"
template<class T = size_t>
class vmstat_t {
public:
static constexpr const char * VMSTAT_FILE = "/proc/vmstat";
......@@ -187,23 +189,11 @@ public:
static constexpr const char * vmacache_find_hits = "vmacache_find_hits";
static constexpr const char * vmacache_full_flushes = "vmacache_full_flushes";
inline auto update() -> bool {
std::ifstream file(VMSTAT_FILE);
if (!file.good()) { return false; }
auto update() -> bool {
param_val_map_ = pfs::procfs().get_meminfo();
last_update = hres_clock::now();
while (file.good()) {
std::string param;
T value;
file >> param;
file >> value;
param_val_map_[param] = value;
}
return true;
}
......
......@@ -231,62 +231,48 @@ namespace memory_info {
[[nodiscard]] auto is_huge_page(const addr_t addr, const pid_t pid) -> bool;
static void update_memory_regions(pid_t pid) {
umap<addr_t, mem_region_maps> regions_maps;
umap<addr_t, mem_region_numa_maps> regions_numa_maps;
static auto memory_regions_maps(const pid_t pid) {
const auto maps = pfs::procfs().get_task(pid).get_maps();
const auto maps_filename = "/proc/" + std::to_string(pid) + "/maps";
map<addr_t, mem_region_maps> regions_maps;
std::ifstream maps(maps_filename);
if (!maps.good()) {
const auto error = "Cannot open file " + maps_filename + ": " + strerror(errno);
throw std::runtime_error(error);
for (const auto & region : maps) {
regions_maps.emplace(region.start_address, mem_region_maps(pid, region));
}
size_t i = 0;
while (maps.good()) {
std::string line;
std::getline(maps, line);
mem_region_maps region(line, i, pid);
return regions_maps;
}
regions_maps.insert(std::make_pair(region.begin(), region));
static auto memory_regions_numa_maps(const pid_t pid) {
map<addr_t, mem_region_numa_maps> regions_numa_maps;
++i;
}
const auto file = "/proc/" + std::to_string(pid) + "/numa_maps";
const auto numa_maps_filename = "/proc/" + std::to_string(pid) + "/numa_maps";
std::ifstream in(file);
std::ifstream numa_maps(numa_maps_filename);
if (!in) { throw std::runtime_error("Cannot open file " + file + ": " + strerror(errno)); }
if (!numa_maps.good()) {
const auto error = "Cannot open file " + numa_maps_filename + ": " + strerror(errno);
throw std::runtime_error(error);
std::string line;
for (size_t i = 0; std::getline(in, line); ++i) {
const mem_region_numa_maps region(line, i, pid);
regions_numa_maps.emplace(region.address(), region);
}
i = 0;
while (numa_maps.good()) {
std::string line;
std::getline(numa_maps, line);
mem_region_numa_maps region(line, i, pid);
return regions_numa_maps;
}
regions_numa_maps.insert(std::make_pair(region.address(), region));
static void update_memory_regions(const pid_t pid) {
const auto regions_maps = memory_regions_maps(pid);
const auto regions_numa_maps = memory_regions_numa_maps(pid);
++i;
}
for (const auto & [addr, region_maps] : regions_maps) {
const auto & it_region_numa_maps = regions_numa_maps.find(addr);
for (auto & [address, region_numa_maps] : regions_numa_maps) {
const auto & region_maps = regions_maps.find(address);
if (it_region_numa_maps == regions_numa_maps.end()) { continue; }
if (region_maps == regions_maps.end()) { continue; }
const auto & region_numa_maps = it_region_numa_maps->second;
details::memory_regions.insert(std::make_pair(address, mem_region(region_maps->second, region_numa_maps)));
details::memory_regions.emplace(addr, mem_region{ region_maps, region_numa_maps });
}
}
......@@ -348,6 +334,7 @@ namespace memory_info {
template<template<typename...> typename Iterable>
static void print_memory(const Iterable<pid_t> & pids, std::ostream & os = std::cout) {
update_vmstat();
update_memory_regions(pids);
os << "NUMA hit: " << details::vmstat.get_value(vmstat_t<>::numa_hit) << '\n';
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment