From 3fdd42af63dc8358a2270e605cb98c7ffd048981 Mon Sep 17 00:00:00 2001 From: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:19:50 +0000 Subject: [PATCH] encapsulate and instrument --- include/xrpl/basics/MallocTrim.h | 42 +++++++++++++ include/xrpl/basics/TaggedCache.ipp | 9 +-- src/libxrpl/basics/MallocTrim.cpp | 95 +++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 include/xrpl/basics/MallocTrim.h create mode 100644 src/libxrpl/basics/MallocTrim.cpp diff --git a/include/xrpl/basics/MallocTrim.h b/include/xrpl/basics/MallocTrim.h new file mode 100644 index 0000000000..5371171ce9 --- /dev/null +++ b/include/xrpl/basics/MallocTrim.h @@ -0,0 +1,42 @@ +#ifndef XRPL_BASICS_MALLOCTRIM_H_INCLUDED +#define XRPL_BASICS_MALLOCTRIM_H_INCLUDED + +#include + +#include +#include + +namespace ripple { + +struct MallocTrimReport +{ + bool supported{false}; + int trimResult{-1}; + long rssBeforeKB{-1}; + long rssAfterKB{-1}; + + long + deltaKB() const + { + return rssAfterKB - rssBeforeKB; + } +}; + +/** Attempt to return freed memory to the operating system. + * + * This function is only effective on Linux with glibc. On other platforms, + * it returns a report indicating the operation is unsupported. + * + * @param tag Optional identifier for logging/debugging purposes + * @param journal Journal for logging diagnostic information + * @return Report containing before/after memory metrics + * + * @note This is intended for use after cache sweeps or other operations + * that free significant amounts of memory. + */ +MallocTrimReport +mallocTrim(std::optional const& tag, beast::Journal journal); + +} // namespace ripple + +#endif diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index bb9efd1cdc..bad58b0470 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -2,12 +2,9 @@ #define XRPL_BASICS_TAGGEDCACHE_IPP_INCLUDED #include +#include #include -#ifdef __GLIBC__ -#include -#endif - namespace ripple { template < @@ -331,9 +328,7 @@ TaggedCache< .count() << "ms"; -#ifdef __GLIBC__ - (void)malloc_trim(0); -#endif + mallocTrim(std::optional(m_name), m_journal); } template < diff --git a/src/libxrpl/basics/MallocTrim.cpp b/src/libxrpl/basics/MallocTrim.cpp new file mode 100644 index 0000000000..38dcd5e968 --- /dev/null +++ b/src/libxrpl/basics/MallocTrim.cpp @@ -0,0 +1,95 @@ +#include +#include + +#include + +#include +#include + +#if defined(__GLIBC__) && BOOST_OS_LINUX +#include +#include +#endif + +namespace ripple { + +namespace detail { + +#if defined(__GLIBC__) && BOOST_OS_LINUX + +std::string +readFile(std::string const& path) +{ + std::ifstream ifs(path); + if (!ifs.is_open()) + return {}; + return std::string( + std::istreambuf_iterator(ifs), std::istreambuf_iterator()); +} + +long +parseVmRSSkB(std::string const& status) +{ + // "VmRSS: 123456 kB" + auto pos = status.find("VmRSS:"); + if (pos == std::string::npos) + return -1; + + pos += 6; // past "VmRSS:" + while (pos < status.size() && status[pos] == ' ') + ++pos; + + long value = -1; + std::sscanf(status.c_str() + pos, "%ld", &value); + return value; // in kB +} + +#endif // __GLIBC__ && BOOST_OS_LINUX + +} // namespace detail + +MallocTrimReport +mallocTrim( + [[maybe_unused]] std::optional const& tag, + beast::Journal journal) +{ + MallocTrimReport report; + +#if !(defined(__GLIBC__) && BOOST_OS_LINUX) + JLOG(journal.debug()) << "malloc_trim not supported on this platform"; + return report; +#else + + report.supported = true; + + if (journal.debug()) + { + std::string const tagStr = tag.value_or("default"); + pid_t const pid = ::getpid(); + std::string const statusPath = + "/proc/" + std::to_string(pid) + "/status"; + + auto const statusBefore = detail::readFile(statusPath); + report.rssBeforeKB = detail::parseVmRSSkB(statusBefore); + + report.trimResult = ::malloc_trim(0); + + auto const statusAfter = detail::readFile(statusPath); + report.rssAfterKB = detail::parseVmRSSkB(statusAfter); + + JLOG(journal.debug()) + << "malloc_trim tag=" << tagStr << " result=" << report.trimResult + << " rss_before=" << report.rssBeforeKB << "kB" + << " rss_after=" << report.rssAfterKB << "kB" + << " delta=" << report.deltaKB() << "kB"; + } + else + { + report.trimResult = ::malloc_trim(0); + } + + return report; +#endif +} + +} // namespace ripple