Skip to content

Commit 88f1230

Browse files
authored
Merge pull request ceph#58115 from ronen-fr/wip-rf-memmodel
common: MemoryModel: performance improvements and API changes Reviewed-by: Venky Shankar <[email protected]> Reviewed-by: Milind Changire <[email protected]>
2 parents 9c7e72c + d699e23 commit 88f1230

File tree

4 files changed

+198
-99
lines changed

4 files changed

+198
-99
lines changed

src/common/MemoryModel.cc

Lines changed: 106 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,140 @@
1-
#include "MemoryModel.h"
2-
#include "include/compat.h"
31
#include "debug.h"
2+
3+
#include "include/compat.h"
4+
5+
#include "MemoryModel.h"
46
#if defined(__linux__)
57
#include <malloc.h>
68
#endif
79

8-
#include <fstream>
10+
#include <charconv>
11+
12+
#include "common/fmt_common.h"
13+
914

1015
#define dout_subsys ceph_subsys_
1116

1217
using namespace std;
18+
using mem_snap_t = MemoryModel::mem_snap_t;
1319

14-
MemoryModel::MemoryModel(CephContext *cct_)
15-
: cct(cct_)
20+
inline bool MemoryModel::cmp_against(
21+
const std::string &ln,
22+
std::string_view param,
23+
long &v) const
1624
{
25+
if (ln.size() < (param.size() + 10)) {
26+
return false;
27+
}
28+
if (ln.starts_with(param)) {
29+
auto p = ln.c_str();
30+
auto s = p + param.size();
31+
// charconv does not like leading spaces
32+
while (*s && isblank(*s)) {
33+
s++;
34+
}
35+
from_chars(s, p + ln.size(), v);
36+
return true;
37+
}
38+
return false;
1739
}
1840

19-
void MemoryModel::_sample(snap *psnap)
20-
{
21-
ifstream f;
2241

23-
f.open(PROCPREFIX "/proc/self/status");
24-
if (!f.is_open()) {
25-
ldout(cct, 0) << "check_memory_usage unable to open " PROCPREFIX "/proc/self/status" << dendl;
26-
return;
27-
}
28-
while (!f.eof()) {
29-
string line;
30-
getline(f, line);
31-
32-
if (strncmp(line.c_str(), "VmSize:", 7) == 0)
33-
psnap->size = atol(line.c_str() + 7);
34-
else if (strncmp(line.c_str(), "VmRSS:", 6) == 0)
35-
psnap->rss = atol(line.c_str() + 7);
36-
else if (strncmp(line.c_str(), "VmHWM:", 6) == 0)
37-
psnap->hwm = atol(line.c_str() + 7);
38-
else if (strncmp(line.c_str(), "VmLib:", 6) == 0)
39-
psnap->lib = atol(line.c_str() + 7);
40-
else if (strncmp(line.c_str(), "VmPeak:", 7) == 0)
41-
psnap->peak = atol(line.c_str() + 7);
42-
else if (strncmp(line.c_str(), "VmData:", 7) == 0)
43-
psnap->data = atol(line.c_str() + 7);
42+
tl::expected<int64_t, std::string> MemoryModel::get_mapped_heap()
43+
{
44+
if (!proc_maps.is_open()) {
45+
return tl::unexpected("unable to open proc/maps");
4446
}
45-
f.close();
47+
// always rewind before reading
48+
proc_maps.clear();
49+
proc_maps.seekg(0);
4650

47-
f.open(PROCPREFIX "/proc/self/maps");
48-
if (!f.is_open()) {
49-
ldout(cct, 0) << "check_memory_usage unable to open " PROCPREFIX "/proc/self/maps" << dendl;
50-
return;
51-
}
51+
int64_t heap = 0;
5252

53-
long heap = 0;
54-
while (f.is_open() && !f.eof()) {
53+
while (proc_maps.is_open() && !proc_maps.eof()) {
5554
string line;
56-
getline(f, line);
57-
//ldout(cct, 0) << "line is " << line << dendl;
55+
getline(proc_maps, line);
5856

59-
const char *start = line.c_str();
60-
const char *dash = start;
61-
while (*dash && *dash != '-') dash++;
57+
if (line.length() < 48) {
58+
// a malformed line. We expect at least
59+
// '560c03f8d000-560c03fae000 rw-p 00000000 00:00 0'
60+
continue;
61+
}
62+
63+
const char* start = line.c_str();
64+
const char* dash = start;
65+
while (*dash && *dash != '-')
66+
dash++;
6267
if (!*dash)
6368
continue;
64-
const char *end = dash + 1;
65-
while (*end && *end != ' ') end++;
69+
const char* end = dash + 1;
70+
while (*end && *end != ' ')
71+
end++;
6672
if (!*end)
6773
continue;
68-
unsigned long long as = strtoll(start, 0, 16);
69-
unsigned long long ae = strtoll(dash+1, 0, 16);
70-
71-
//ldout(cct, 0) << std::hex << as << " to " << ae << std::dec << dendl;
7274

75+
auto addr_end = end;
7376
end++;
74-
const char *mode = end;
75-
76-
int skip = 4;
77-
while (skip--) {
78-
end++;
79-
while (*end && *end != ' ') end++;
80-
}
81-
if (*end)
82-
end++;
83-
84-
long size = ae - as;
85-
//ldout(cct, 0) << "size " << size << " mode is '" << mode << "' end is '" << end << "'" << dendl;
77+
const char* mode = end;
8678

8779
/*
8880
* anything 'rw' and anon is assumed to be heap.
81+
* But we should count lines with inode '0' and '[heap]' as well
8982
*/
90-
if (mode[0] == 'r' && mode[1] == 'w' && !*end)
83+
if (mode[0] != 'r' || mode[1] != 'w') {
84+
continue;
85+
}
86+
87+
auto the_rest = line.substr(5 + end - start);
88+
if (!the_rest.starts_with("00000000 00:00 0")) {
89+
continue;
90+
}
91+
92+
std::string_view final_token{the_rest.begin() + sizeof("00000000 00:00 0") - 1,
93+
the_rest.end()};
94+
if (final_token.size() < 3 ||
95+
final_token.ends_with("[heap]") || final_token.ends_with("[stack]")) {
96+
// calculate and sum the size of the heap segment
97+
uint64_t as{0ull};
98+
from_chars(start, dash, as, 16);
99+
uint64_t ae{0ull};
100+
from_chars(dash + 1, addr_end, ae, 16);
101+
// fmt::print("\t\tas:{:x} ae:{:x} -> {}\n", as, ae, ((ae - as) >> 10));
102+
long size = ae - as;
91103
heap += size;
104+
}
92105
}
93106

94-
psnap->heap = heap >> 10;
107+
return heap;
108+
}
109+
110+
111+
tl::expected<mem_snap_t, std::string> MemoryModel::full_sample()
112+
{
113+
if (!proc_status.is_open()) {
114+
return tl::unexpected("unable to open proc/status");
115+
}
116+
// always rewind before reading
117+
proc_status.clear();
118+
proc_status.seekg(0);
119+
120+
mem_snap_t s;
121+
// we will be looking for 6 entries
122+
int yet_to_find = 6;
123+
124+
while (!proc_status.eof() && yet_to_find > 0) {
125+
string ln;
126+
getline(proc_status, ln);
127+
128+
if (cmp_against(ln, "VmSize:", s.size) ||
129+
cmp_against(ln, "VmRSS:", s.rss) || cmp_against(ln, "VmHWM:", s.hwm) ||
130+
cmp_against(ln, "VmLib:", s.lib) ||
131+
cmp_against(ln, "VmPeak:", s.peak) ||
132+
cmp_against(ln, "VmData:", s.data)) {
133+
yet_to_find--;
134+
}
135+
}
95136

137+
// get heap size
138+
s.heap = static_cast<long>(get_mapped_heap().value_or(0));
139+
return s;
96140
}

src/common/MemoryModel.h

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,67 @@
1515
#ifndef CEPH_MEMORYMODEL_H
1616
#define CEPH_MEMORYMODEL_H
1717

18+
#include <fstream>
19+
#include <string>
20+
#include <string_view>
1821
#include "include/common_fwd.h"
22+
#include "include/compat.h"
23+
#include "include/expected.hpp"
24+
1925

2026
class MemoryModel {
2127
public:
22-
struct snap {
23-
long peak;
24-
long size;
25-
long hwm;
26-
long rss;
27-
long data;
28-
long lib;
29-
30-
long heap;
31-
32-
snap() : peak(0), size(0), hwm(0), rss(0), data(0), lib(0),
33-
heap(0)
34-
{}
35-
36-
long get_total() { return size; }
37-
long get_rss() { return rss; }
38-
long get_heap() { return heap; }
39-
} last;
28+
struct mem_snap_t {
29+
long peak{0};
30+
long size{0};
31+
long hwm{0};
32+
long rss{0};
33+
long data{0};
34+
long lib{0};
35+
long heap{0};
36+
37+
long get_total() const { return size; }
38+
long get_rss() const { return rss; }
39+
long get_heap() const { return heap; }
40+
};
4041

4142
private:
42-
CephContext *cct;
43-
void _sample(snap *p);
43+
static inline constexpr const char* proc_stat_fn = PROCPREFIX "/proc/self/status";
44+
static inline constexpr const char* proc_maps_fn = PROCPREFIX "/proc/self/maps";
45+
46+
std::ifstream proc_status{proc_stat_fn};
47+
std::ifstream proc_maps{proc_maps_fn};
48+
49+
/**
50+
* @brief Get the mapped heap size
51+
*
52+
* Read /proc/self/maps to get the heap size.
53+
* \retval the mapped heap size, or an error message if the file had not been opened
54+
* when the object was constructed.
55+
*/
56+
tl::expected<int64_t, std::string> get_mapped_heap();
57+
58+
/**
59+
* @brief Compare a line against an expected data label
60+
*
61+
* If the line starts with the expected label, extract the value and store it in v.
62+
* \retval true if the line starts with the expected label
63+
*/
64+
bool cmp_against(const std::string& ln, std::string_view param, long& v) const;
4465

4566
public:
46-
explicit MemoryModel(CephContext *cct);
47-
void sample(snap *p = 0) {
48-
_sample(&last);
49-
if (p)
50-
*p = last;
51-
}
67+
/**
68+
* @brief extract memory usage information from /proc/self/status &
69+
* /proc/self/maps
70+
*
71+
* Read /proc/self/status and /proc/self/maps to get memory usage information.
72+
* \retval a structure containing the memory usage information, or an error
73+
* message if /proc/status had not been opened when the object was
74+
* constructed.
75+
* Note that no error is returned if only /proc/maps is not open (the heap
76+
* size will be reported as 0).
77+
*/
78+
tl::expected<mem_snap_t, std::string> full_sample();
5279
};
5380

5481
#endif

src/mds/MDCache.cc

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
#include "msg/Message.h"
4747
#include "msg/Messenger.h"
4848

49-
#include "common/MemoryModel.h"
5049
#include "common/errno.h"
5150
#include "common/perf_counters.h"
5251
#include "common/safe_io.h"
@@ -7832,10 +7831,15 @@ void MDCache::trim_client_leases()
78327831

78337832
void MDCache::check_memory_usage()
78347833
{
7835-
static MemoryModel mm(g_ceph_context);
7836-
static MemoryModel::snap last;
7837-
mm.sample(&last);
7838-
static MemoryModel::snap baseline = last;
7834+
MemoryModel::mem_snap_t memory_snap;
7835+
7836+
if (upkeep_memory_stats) {
7837+
auto maybe_memory_snap = upkeep_memory_stats->full_sample();
7838+
if (maybe_memory_snap) {
7839+
memory_snap = *maybe_memory_snap;
7840+
}
7841+
}
7842+
// else - no access to memory stats. The problem was already reported at thread's init.
78397843

78407844
// check client caps
78417845
ceph_assert(CInode::count() == inode_map.size() + snap_inode_map.size() + num_shadow_inodes);
@@ -7844,17 +7848,17 @@ void MDCache::check_memory_usage()
78447848
caps_per_inode = (double)Capability::count() / (double)CInode::count();
78457849

78467850
dout(2) << "Memory usage: "
7847-
<< " total " << last.get_total()
7848-
<< ", rss " << last.get_rss()
7849-
<< ", heap " << last.get_heap()
7850-
<< ", baseline " << baseline.get_heap()
7851+
<< " total " << memory_snap.get_total()
7852+
<< ", rss " << memory_snap.get_rss()
7853+
<< ", heap " << memory_snap.get_heap()
7854+
<< ", baseline " << upkeep_mem_baseline.get_heap()
78517855
<< ", " << num_inodes_with_caps << " / " << CInode::count() << " inodes have caps"
78527856
<< ", " << Capability::count() << " caps, " << caps_per_inode << " caps per inode"
78537857
<< dendl;
78547858

78557859
mds->update_mlogger();
7856-
mds->mlogger->set(l_mdm_rss, last.get_rss());
7857-
mds->mlogger->set(l_mdm_heap, last.get_heap());
7860+
mds->mlogger->set(l_mdm_rss, memory_snap.get_rss());
7861+
mds->mlogger->set(l_mdm_heap, memory_snap.get_heap());
78587862
}
78597863

78607864

@@ -14190,6 +14194,27 @@ bool MDCache::is_ready_to_trim_cache(void)
1419014194
void MDCache::upkeep_main(void)
1419114195
{
1419214196
std::unique_lock lock(upkeep_mutex);
14197+
14198+
// create a "memory model" for the upkeep thread. The object maintains
14199+
// the relevant '/proc' files open. We only check for a failure to open
14200+
// the files once, and then we'll just keep the object around.
14201+
upkeep_memory_stats = MemoryModel{};
14202+
14203+
// get initial sample upon thread creation
14204+
{
14205+
auto maybe_base = upkeep_memory_stats->full_sample();
14206+
if (!maybe_base) {
14207+
dout(1) << fmt::format(
14208+
"{}: Failed to get initial memory sample ({}). No more "
14209+
"sampling will be attempted",
14210+
__func__, maybe_base.error())
14211+
<< dendl;
14212+
upkeep_memory_stats = std::nullopt;
14213+
} else {
14214+
upkeep_mem_baseline = *maybe_base;
14215+
}
14216+
}
14217+
1419314218
while (!upkeep_trim_shutdown.load()) {
1419414219
auto now = clock::now();
1419514220
auto since = now-upkeep_last_trim;

src/mds/MDCache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <thread>
2121

2222
#include "common/DecayCounter.h"
23+
#include "common/MemoryModel.h"
2324
#include "include/common_fwd.h"
2425
#include "include/types.h"
2526
#include "include/filepath.h"
@@ -1539,6 +1540,8 @@ class MDCache {
15391540
time upkeep_last_trim = clock::zero();
15401541
time upkeep_last_release = clock::zero();
15411542
std::atomic<bool> upkeep_trim_shutdown{false};
1543+
std::optional<MemoryModel> upkeep_memory_stats;
1544+
MemoryModel::mem_snap_t upkeep_mem_baseline;
15421545

15431546
uint64_t kill_shutdown_at = 0;
15441547

0 commit comments

Comments
 (0)