mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-13 19:25:24 +00:00
Compare commits
3 Commits
vlntb/mall
...
ximinez/pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ed020ffd6 | ||
|
|
9686604963 | ||
|
|
3f9c9fff88 |
@@ -10,7 +10,7 @@
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"openssl/3.5.4#58f5173c2ee51d6fc0f0c61b4eddadbb%1768259092.666",
|
||||
"openssl/3.5.4#1b986e61b38fdfda3b40bebc1b234393%1768312656.257",
|
||||
"nudb/2.0.9#fb8dfd1a5557f5e0528114c2da17721e%1765850143.957",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#ifndef XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||
#define XRPL_BASICS_MALLOCTRIM_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// cSpell:ignore ptmalloc
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Allocator interaction note:
|
||||
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
|
||||
// ptmalloc return free heap pages to the OS.
|
||||
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
|
||||
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
|
||||
// on the *active* heap. The call is harmless but may not reclaim memory
|
||||
// because those allocators manage their own arenas.
|
||||
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
|
||||
// allocations are usually returned to the OS on free regardless of trimming.
|
||||
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
|
||||
// and consider rate limiting to avoid churn.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct MallocTrimReport
|
||||
{
|
||||
bool supported{false};
|
||||
int trimResult{-1};
|
||||
long rssBeforeKB{-1};
|
||||
long rssAfterKB{-1};
|
||||
|
||||
[[nodiscard]] long
|
||||
deltaKB() const noexcept
|
||||
{
|
||||
if (rssBeforeKB < 0 || rssAfterKB < 0)
|
||||
return 0;
|
||||
return rssAfterKB - rssBeforeKB;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Attempt to return freed memory to the operating system.
|
||||
*
|
||||
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
|
||||
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
|
||||
* a different allocator is in use, this function is a no-op and the report will
|
||||
* indicate that trimming is unsupported or had no effect.
|
||||
*
|
||||
* @param tag Optional identifier for logging/debugging purposes.
|
||||
* @param journal Journal for diagnostic logging.
|
||||
* @return Report containing before/after metrics and the trim result.
|
||||
*
|
||||
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
|
||||
* calling glibc's malloc_trim may have no effect on the active heap. The
|
||||
* call is harmless but typically does not reclaim memory under those
|
||||
* allocators.
|
||||
*
|
||||
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
|
||||
* Large allocations satisfied via mmap are usually returned on free
|
||||
* independently of trimming.
|
||||
*
|
||||
* @note Intended for use after operations that free significant memory (e.g.,
|
||||
* cache sweeps, ledger cleanup, online delete). Consider rate limiting.
|
||||
*/
|
||||
MallocTrimReport
|
||||
mallocTrim(std::optional<std::string> const& tag, beast::Journal journal);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -1,121 +0,0 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/MallocTrim.h>
|
||||
|
||||
#include <boost/predef.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
pid_t const cachedPid = ::getpid();
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
|
||||
long
|
||||
parseVmRSSkB(std::string const& status)
|
||||
{
|
||||
std::istringstream iss(status);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(iss, line))
|
||||
{
|
||||
// Allow leading spaces/tabs before the key.
|
||||
auto const firstNonWs = line.find_first_not_of(" \t");
|
||||
if (firstNonWs == std::string::npos)
|
||||
continue;
|
||||
|
||||
constexpr char key[] = "VmRSS:";
|
||||
constexpr auto keyLen = sizeof(key) - 1;
|
||||
|
||||
// Require the line (after leading whitespace) to start with "VmRSS:".
|
||||
// Check if we have enough characters and the substring matches.
|
||||
if (firstNonWs + keyLen > line.size() ||
|
||||
line.substr(firstNonWs, keyLen) != key)
|
||||
continue;
|
||||
|
||||
// Move past "VmRSS:" and any following whitespace.
|
||||
auto pos = firstNonWs + keyLen;
|
||||
while (pos < line.size() &&
|
||||
std::isspace(static_cast<unsigned char>(line[pos])))
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
|
||||
long value = -1;
|
||||
if (std::sscanf(line.c_str() + pos, "%ld", &value) == 1)
|
||||
return value;
|
||||
|
||||
// Found the key but couldn't parse a number.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// No VmRSS line found.
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif // __GLIBC__ && BOOST_OS_LINUX
|
||||
|
||||
} // namespace detail
|
||||
|
||||
MallocTrimReport
|
||||
mallocTrim(
|
||||
[[maybe_unused]] std::optional<std::string> const& tag,
|
||||
beast::Journal journal)
|
||||
{
|
||||
MallocTrimReport report;
|
||||
|
||||
#if !(defined(__GLIBC__) && BOOST_OS_LINUX)
|
||||
JLOG(journal.debug()) << "malloc_trim not supported on this platform";
|
||||
#else
|
||||
|
||||
report.supported = true;
|
||||
|
||||
if (journal.debug())
|
||||
{
|
||||
auto readFile = [](std::string const& path) -> std::string {
|
||||
std::ifstream ifs(path);
|
||||
if (!ifs.is_open())
|
||||
return {};
|
||||
return std::string(
|
||||
std::istreambuf_iterator<char>(ifs),
|
||||
std::istreambuf_iterator<char>());
|
||||
};
|
||||
|
||||
std::string const tagStr = tag.value_or("default");
|
||||
std::string const statusPath =
|
||||
"/proc/" + std::to_string(cachedPid) + "/status";
|
||||
|
||||
auto const statusBefore = readFile(statusPath);
|
||||
report.rssBeforeKB = detail::parseVmRSSkB(statusBefore);
|
||||
|
||||
report.trimResult = ::malloc_trim(0);
|
||||
|
||||
auto const statusAfter = 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -427,6 +427,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||
auto tx1 = batch::inner(pay(alice, bob, XRP(1)), seq + 1);
|
||||
tx1[jss::Fee] = "1.5";
|
||||
env.set_parse_failure_expected(true);
|
||||
try
|
||||
{
|
||||
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||
@@ -438,6 +439,7 @@ class Batch_test : public beast::unit_test::suite
|
||||
{
|
||||
BEAST_EXPECT(true);
|
||||
}
|
||||
env.set_parse_failure_expected(false);
|
||||
}
|
||||
|
||||
// temSEQ_AND_TICKET: Batch: inner txn cannot have both Sequence
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
#include <xrpl/basics/MallocTrim.h>
|
||||
|
||||
#include <boost/predef.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
namespace xrpl::detail {
|
||||
long
|
||||
parseVmRSSkB(std::string const& status);
|
||||
} // namespace xrpl::detail
|
||||
#endif
|
||||
|
||||
TEST_CASE("MallocTrimReport structure")
|
||||
{
|
||||
// Test default construction
|
||||
MallocTrimReport report;
|
||||
CHECK(report.supported == false);
|
||||
CHECK(report.trimResult == -1);
|
||||
CHECK(report.rssBeforeKB == -1);
|
||||
CHECK(report.rssAfterKB == -1);
|
||||
CHECK(report.deltaKB() == 0);
|
||||
|
||||
// Test deltaKB calculation - memory freed
|
||||
report.rssBeforeKB = 1000;
|
||||
report.rssAfterKB = 800;
|
||||
CHECK(report.deltaKB() == -200);
|
||||
|
||||
// Test deltaKB calculation - memory increased
|
||||
report.rssBeforeKB = 500;
|
||||
report.rssAfterKB = 600;
|
||||
CHECK(report.deltaKB() == 100);
|
||||
|
||||
// Test deltaKB calculation - no change
|
||||
report.rssBeforeKB = 1234;
|
||||
report.rssAfterKB = 1234;
|
||||
CHECK(report.deltaKB() == 0);
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
TEST_CASE("parseVmRSSkB")
|
||||
{
|
||||
using xrpl::detail::parseVmRSSkB;
|
||||
|
||||
// Test standard format
|
||||
{
|
||||
std::string status = "VmRSS: 123456 kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == 123456);
|
||||
}
|
||||
|
||||
// Test with multiple lines
|
||||
{
|
||||
std::string status =
|
||||
"Name: xrpld\n"
|
||||
"VmPeak: 1234567 kB\n"
|
||||
"VmSize: 1234567 kB\n"
|
||||
"VmRSS: 987654 kB\n"
|
||||
"VmData: 123456 kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == 987654);
|
||||
}
|
||||
|
||||
// Test with minimal whitespace
|
||||
{
|
||||
std::string status = "VmRSS: 42 kB";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == 42);
|
||||
}
|
||||
|
||||
// Test with extra whitespace
|
||||
{
|
||||
std::string status = "VmRSS: 999999 kB";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == 999999);
|
||||
}
|
||||
|
||||
// Test with tabs
|
||||
{
|
||||
std::string status = "VmRSS:\t\t12345 kB";
|
||||
long result = parseVmRSSkB(status);
|
||||
// Note: tabs are not explicitly handled as spaces, this documents
|
||||
// current behavior
|
||||
CHECK(result == 12345);
|
||||
}
|
||||
|
||||
// Test zero value
|
||||
{
|
||||
std::string status = "VmRSS: 0 kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == 0);
|
||||
}
|
||||
|
||||
// Test missing VmRSS
|
||||
{
|
||||
std::string status =
|
||||
"Name: xrpld\n"
|
||||
"VmPeak: 1234567 kB\n"
|
||||
"VmSize: 1234567 kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == -1);
|
||||
}
|
||||
|
||||
// Test empty string
|
||||
{
|
||||
std::string status = "";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == -1);
|
||||
}
|
||||
|
||||
// Test malformed data (VmRSS but no number)
|
||||
{
|
||||
std::string status = "VmRSS: \n";
|
||||
long result = parseVmRSSkB(status);
|
||||
// sscanf should fail to parse and return -1 unchanged
|
||||
CHECK(result == -1);
|
||||
}
|
||||
|
||||
// Test malformed data (VmRSS but invalid number)
|
||||
{
|
||||
std::string status = "VmRSS: abc kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
// sscanf should fail and return -1 unchanged
|
||||
CHECK(result == -1);
|
||||
}
|
||||
|
||||
// Test partial match (should not match "NotVmRSS:")
|
||||
{
|
||||
std::string status = "NotVmRSS: 123456 kB\n";
|
||||
long result = parseVmRSSkB(status);
|
||||
CHECK(result == -1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("mallocTrim basic functionality")
|
||||
{
|
||||
beast::Journal journal{beast::Journal::getNullSink()};
|
||||
|
||||
// Test with no tag
|
||||
{
|
||||
MallocTrimReport report = mallocTrim(std::nullopt, journal);
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
// On Linux with glibc, should be supported
|
||||
CHECK(report.supported == true);
|
||||
// trimResult should be 0 or 1 (success indicators)
|
||||
CHECK(report.trimResult >= 0);
|
||||
#else
|
||||
// On other platforms, should be unsupported
|
||||
CHECK(report.supported == false);
|
||||
CHECK(report.trimResult == -1);
|
||||
CHECK(report.rssBeforeKB == -1);
|
||||
CHECK(report.rssAfterKB == -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Test with tag
|
||||
{
|
||||
MallocTrimReport report =
|
||||
mallocTrim(std::optional<std::string>("test_tag"), journal);
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
CHECK(report.supported == true);
|
||||
CHECK(report.trimResult >= 0);
|
||||
#else
|
||||
CHECK(report.supported == false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mallocTrim with debug logging")
|
||||
{
|
||||
beast::Journal journal{beast::Journal::getNullSink()};
|
||||
|
||||
MallocTrimReport report =
|
||||
mallocTrim(std::optional<std::string>("debug_test"), journal);
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
CHECK(report.supported == true);
|
||||
// The function should complete without crashing
|
||||
#else
|
||||
CHECK(report.supported == false);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("mallocTrim repeated calls")
|
||||
{
|
||||
beast::Journal journal{beast::Journal::getNullSink()};
|
||||
|
||||
// Call malloc_trim multiple times to ensure it's safe
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
MallocTrimReport report = mallocTrim(
|
||||
std::optional<std::string>("iteration_" + std::to_string(i)),
|
||||
journal);
|
||||
|
||||
#if defined(__GLIBC__) && BOOST_OS_LINUX
|
||||
CHECK(report.supported == true);
|
||||
CHECK(report.trimResult >= 0);
|
||||
#else
|
||||
CHECK(report.supported == false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <xrpld/shamap/NodeFamily.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/basics/MallocTrim.h>
|
||||
#include <xrpl/basics/ResolverAsio.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/asio/io_latency_probe.h>
|
||||
@@ -1106,8 +1105,6 @@ public:
|
||||
<< "; size after: " << cachedSLEs_.size();
|
||||
}
|
||||
|
||||
mallocTrim(std::optional<std::string>("doSweep"), m_journal);
|
||||
|
||||
// Set timer to do another sweep later.
|
||||
setSweepTimer();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <xrpld/rpc/MPTokenIssuanceID.h>
|
||||
#include <xrpld/rpc/ServerHandler.h>
|
||||
|
||||
#include <xrpl/basics/MallocTrim.h>
|
||||
#include <xrpl/basics/UptimeClock.h>
|
||||
#include <xrpl/basics/mulDiv.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
@@ -2552,14 +2551,10 @@ NetworkOPsImp::setMode(OperatingMode om)
|
||||
if (mMode == om)
|
||||
return;
|
||||
|
||||
auto const oldMode = mMode.load(std::memory_order_relaxed);
|
||||
mMode = om;
|
||||
|
||||
accounting_.mode(om);
|
||||
|
||||
if (oldMode != OperatingMode::FULL && om == OperatingMode::FULL)
|
||||
mallocTrim(std::optional<std::string>("SyncComplete"), m_journal);
|
||||
|
||||
JLOG(m_journal.info()) << "STATE->" << strOperatingMode();
|
||||
pubServer();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
|
||||
#include <xrpld/core/ConfigSections.h>
|
||||
|
||||
#include <xrpl/basics/MallocTrim.h>
|
||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
|
||||
@@ -546,8 +545,6 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
|
||||
{
|
||||
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
|
||||
fullBelowCache_->clear();
|
||||
|
||||
mallocTrim(std::optional<std::string>("clearCaches"), journal_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -613,8 +610,6 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
|
||||
});
|
||||
if (healthWait() == stopping)
|
||||
return;
|
||||
|
||||
mallocTrim(std::optional<std::string>("clearPrior"), journal_);
|
||||
}
|
||||
|
||||
SHAMapStoreImp::HealthResult
|
||||
|
||||
Reference in New Issue
Block a user