rippled
Loading...
Searching...
No Matches
libxrpl/basics/MallocTrim.cpp
1#include <xrpl/basics/Log.h>
2#include <xrpl/basics/MallocTrim.h>
3
4#include <boost/predef.h>
5
6#include <chrono>
7#include <cstdint>
8#include <cstdio>
9#include <fstream>
10#include <sstream>
11
12#if defined(__GLIBC__) && BOOST_OS_LINUX
13#include <sys/resource.h>
14
15#include <malloc.h>
16#include <unistd.h>
17
18// Require RUSAGE_THREAD for thread-scoped page fault tracking
19#ifndef RUSAGE_THREAD
20#error "MallocTrim rusage instrumentation requires RUSAGE_THREAD on Linux/glibc"
21#endif
22
23namespace {
24
25bool
26getRusageThread(struct rusage& ru)
27{
28 return ::getrusage(RUSAGE_THREAD, &ru) == 0; // LCOV_EXCL_LINE
29}
30
31} // namespace
32#endif
33
34namespace xrpl {
35
36namespace detail {
37
38// cSpell:ignore statm
39
40#if defined(__GLIBC__) && BOOST_OS_LINUX
41
42inline int
43mallocTrimWithPad(std::size_t padBytes)
44{
45 return ::malloc_trim(padBytes);
46}
47
48long
49parseStatmRSSkB(std::string const& statm)
50{
51 // /proc/self/statm format: size resident shared text lib data dt
52 // We want the second field (resident) which is in pages
53 std::istringstream iss(statm);
54 long size = 0, resident = 0;
55 if (!(iss >> size >> resident))
56 return -1;
57
58 // Convert pages to KB
59 long const pageSize = ::sysconf(_SC_PAGESIZE);
60 if (pageSize <= 0)
61 return -1;
62
63 return (resident * pageSize) / 1024;
64}
65
66#endif // __GLIBC__ && BOOST_OS_LINUX
67
68} // namespace detail
69
70MallocTrimReport
72{
73 // LCOV_EXCL_START
74
75 MallocTrimReport report;
76
77#if !(defined(__GLIBC__) && BOOST_OS_LINUX)
78 JLOG(journal.debug()) << "malloc_trim not supported on this platform (tag=" << tag << ")";
79#else
80 // Keep glibc malloc_trim padding at 0 (default): 12h Mainnet tests across 0/256KB/1MB/16MB
81 // showed no clear, consistent benefit from custom padding—0 provided the best overall balance
82 // of RSS reduction and trim-latency stability without adding a tuning surface.
83 constexpr std::size_t TRIM_PAD = 0;
84
85 report.supported = true;
86
87 if (journal.debug())
88 {
89 auto readFile = [](std::string const& path) -> std::string {
90 std::ifstream ifs(path, std::ios::in | std::ios::binary);
91 if (!ifs.is_open())
92 return {};
93
94 // /proc files are often not seekable; read as a stream.
96 oss << ifs.rdbuf();
97 return oss.str();
98 };
99
100 std::string const tagStr{tag};
101 std::string const statmPath = "/proc/self/statm";
102
103 auto const statmBefore = readFile(statmPath);
104 long const rssBeforeKB = detail::parseStatmRSSkB(statmBefore);
105
106 struct rusage ru0{};
107 bool const have_ru0 = getRusageThread(ru0);
108
109 auto const t0 = std::chrono::steady_clock::now();
110
111 report.trimResult = detail::mallocTrimWithPad(TRIM_PAD);
112
113 auto const t1 = std::chrono::steady_clock::now();
114
115 struct rusage ru1{};
116 bool const have_ru1 = getRusageThread(ru1);
117
118 auto const statmAfter = readFile(statmPath);
119 long const rssAfterKB = detail::parseStatmRSSkB(statmAfter);
120
121 // Populate report fields
122 report.rssBeforeKB = rssBeforeKB;
123 report.rssAfterKB = rssAfterKB;
124 report.durationUs = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);
125
126 if (have_ru0 && have_ru1)
127 {
128 report.minfltDelta = ru1.ru_minflt - ru0.ru_minflt;
129 report.majfltDelta = ru1.ru_majflt - ru0.ru_majflt;
130 }
131
132 std::int64_t const deltaKB = (rssBeforeKB < 0 || rssAfterKB < 0)
133 ? 0
134 : (static_cast<std::int64_t>(rssAfterKB) - static_cast<std::int64_t>(rssBeforeKB));
135
136 JLOG(journal.debug()) << "malloc_trim tag=" << tagStr << " result=" << report.trimResult
137 << " pad=" << TRIM_PAD << " bytes"
138 << " rss_before=" << rssBeforeKB << "kB"
139 << " rss_after=" << rssAfterKB << "kB"
140 << " delta=" << deltaKB << "kB"
141 << " duration_us=" << report.durationUs.count()
142 << " minflt_delta=" << report.minfltDelta
143 << " majflt_delta=" << report.majfltDelta;
144 }
145 else
146 {
147 report.trimResult = detail::mallocTrimWithPad(TRIM_PAD);
148 }
149
150#endif
151
152 return report;
153
154 // LCOV_EXCL_STOP
155}
156
157} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:301
T is_open(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
MallocTrimReport mallocTrim(std::string_view tag, beast::Journal journal)
Attempt to return freed memory to the operating system.
T rdbuf(T... args)
T size(T... args)
T str(T... args)
std::int64_t majfltDelta
Definition MallocTrim.h:35
std::int64_t minfltDelta
Definition MallocTrim.h:34
std::chrono::microseconds durationUs
Definition MallocTrim.h:33
std::int64_t rssAfterKB
Definition MallocTrim.h:32
std::int64_t rssBeforeKB
Definition MallocTrim.h:31