From 49d4ebcbfe4c71dbf05f6bb777bf976a7416a04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 14 Sep 2025 11:39:19 -0700 Subject: [PATCH] system: add helper for fetching total system memory Added a minimal system helper to query total physical RAM on [Linux/macOS/Windows](https://stackoverflow.com/a/2513561) (on other platforms we just return an empty optional). The added test checks if the value is roughly correct by checking if the CI platforms are returning any value and if the value is at least 1 GiB and not more than 10 TiB. The max value is only validated on 64 bits, since it's not unreasonable for 32 bits to have max memory, but on 64 bits it's likely an error. https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex > ullTotalPhys The amount of actual physical memory, in bytes. https://man7.org/linux/man-pages/man3/sysconf.3.html: > _SC_PHYS_PAGES The number of pages of physical memory. Note that it is possible for the product of this value and the value of _SC_PAGESIZE to overflow. > _SC_PAGESIZE Size of a page in bytes. Must not be less than 1. See https://godbolt.org/z/ec81Tjvrj for further details Github-Pull: #33333 Rebased-From: 6c720459beead5c825b354a1d5c11969b6e3a170 --- src/common/system.cpp | 25 +++++++++++++++++++++---- src/common/system.h | 6 ++++++ src/test/system_tests.cpp | 13 +++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/common/system.cpp b/src/common/system.cpp index 7af792db44c..cf032adfced 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -11,19 +11,25 @@ #include #include -#ifndef WIN32 -#include -#else -#include +#ifdef WIN32 #include +#include +#include +#else +#include +#include #endif #ifdef HAVE_MALLOPT_ARENA_MAX #include #endif +#include +#include +#include #include #include +#include #include #include #include @@ -105,6 +111,17 @@ int GetNumCores() return std::thread::hardware_concurrency(); } +std::optional GetTotalRAM() +{ + auto clamp{[](uint64_t v) { return size_t(std::min(v, uint64_t{std::numeric_limits::max()})); }}; +#ifdef WIN32 + if (MEMORYSTATUSEX m{}; (m.dwLength = sizeof(m), GlobalMemoryStatusEx(&m))) return clamp(m.ullTotalPhys); +#elif defined(__linux__) || defined(__APPLE__) + if (long p{sysconf(_SC_PHYS_PAGES)}, s{sysconf(_SC_PAGESIZE)}; p > 0 && s > 0) return clamp(1ULL * p * s); +#endif + return std::nullopt; +} + // Obtain the application startup time (used for uptime calculation) int64_t GetStartupTime() { diff --git a/src/common/system.h b/src/common/system.h index a4b56be9ac9..2184f1d4c9e 100644 --- a/src/common/system.h +++ b/src/common/system.h @@ -9,6 +9,7 @@ #include // IWYU pragma: keep #include +#include #include // Application startup time (used for uptime calculation) @@ -29,4 +30,9 @@ void runCommand(const std::string& strCommand); */ int GetNumCores(); +/** + * Return the total RAM available on the current system, if detectable. + */ +std::optional GetTotalRAM(); + #endif // BITCOIN_COMMON_SYSTEM_H diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index dec4d418290..53db32007ee 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -8,6 +8,8 @@ #include #include +#include + #ifdef ENABLE_EXTERNAL_SIGNER #include #endif // ENABLE_EXTERNAL_SIGNER @@ -16,6 +18,17 @@ BOOST_FIXTURE_TEST_SUITE(system_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(total_ram) +{ + BOOST_CHECK_GE(GetTotalRAM(), 1000_MiB); + + if constexpr (SIZE_MAX == UINT64_MAX) { + // Upper bound check only on 64-bit: 32-bit systems can reasonably have max memory, + // but extremely large values on 64-bit likely indicate detection errors + BOOST_CHECK_LT(GetTotalRAM(), 10'000'000_MiB); // >10 TiB memory is unlikely + } +} + #ifdef ENABLE_EXTERNAL_SIGNER BOOST_AUTO_TEST_CASE(run_command)