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
This commit is contained in:
Lőrinc 2025-09-14 11:39:19 -07:00
parent 1444ed855f
commit 6c720459be
3 changed files with 40 additions and 4 deletions

View File

@ -11,19 +11,25 @@
#include <util/string.h>
#include <util/time.h>
#ifndef WIN32
#include <sys/stat.h>
#else
#include <compat/compat.h>
#ifdef WIN32
#include <codecvt>
#include <compat/compat.h>
#include <windows.h>
#else
#include <sys/stat.h>
#include <unistd.h>
#endif
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <locale>
#include <optional>
#include <stdexcept>
#include <string>
#include <thread>
@ -105,6 +111,17 @@ int GetNumCores()
return std::thread::hardware_concurrency();
}
std::optional<size_t> GetTotalRAM()
{
auto clamp{[](uint64_t v) { return size_t(std::min(v, uint64_t{std::numeric_limits<size_t>::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()
{

View File

@ -9,6 +9,7 @@
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <cstdint>
#include <optional>
#include <string>
// 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<size_t> GetTotalRAM();
#endif // BITCOIN_COMMON_SYSTEM_H

View File

@ -8,6 +8,8 @@
#include <common/run_command.h>
#include <univalue.h>
#include <common/system.h>
#ifdef ENABLE_EXTERNAL_SIGNER
#include <util/subprocess.h>
#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)