From 91facd3a20668ec1452a6efee47057fee8197db2 Mon Sep 17 00:00:00 2001 From: Nightprince Date: Mon, 29 Jun 2026 12:06:31 +0330 Subject: [PATCH] Core/Command: Show system memory in server info sample result : Memory Summary: Process: RSS 3.76 GB, Private 3.90 GB, Peak RSS 3.76 GB | System: RAM 32.6% (20.83 GB/63.83 GB) --- cmake/compiler/msvc/settings.cmake | 4 +- src/server/scripts/Commands/cs_server.cpp | 9 +- src/server/shared/Utilities/MemoryUsage.cpp | 239 ++++++++++++++++++++ src/server/shared/Utilities/MemoryUsage.h | 19 ++ 4 files changed, 266 insertions(+), 5 deletions(-) diff --git a/cmake/compiler/msvc/settings.cmake b/cmake/compiler/msvc/settings.cmake index e98cc6a4f8..6208054e01 100644 --- a/cmake/compiler/msvc/settings.cmake +++ b/cmake/compiler/msvc/settings.cmake @@ -137,5 +137,5 @@ sf_add_msvc_cxx_compile_option(/Zm50) sf_add_msvc_cxx_compile_option(/we4263) sf_add_msvc_cxx_compile_option(/we4264) -set(CMAKE_CXX_STANDARD_LIBRARIES "ws2_32.lib iphlpapi.lib netapi32.lib mswsock.lib psapi.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") -set(CMAKE_C_STANDARD_LIBRARIES "ws2_32.lib iphlpapi.lib netapi32.lib mswsock.lib psapi.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") +set(CMAKE_CXX_STANDARD_LIBRARIES "ws2_32.lib iphlpapi.lib netapi32.lib mswsock.lib psapi.lib sysinfoapi.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") +set(CMAKE_C_STANDARD_LIBRARIES "ws2_32.lib iphlpapi.lib netapi32.lib mswsock.lib psapi.lib sysinfoapi.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index cf399dad20..3d9505b95e 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -137,7 +137,10 @@ public: uint32 maxQueuedClientsNum = sWorld->GetMaxQueuedSessionCount(); std::string uptime = secsToTimeString(sWorld->GetUptime()); uint32 updateTime = sWorld->GetUpdateTime(); - Skyfire::ProcessMemoryUsage memoryUsage; + Skyfire::ProcessMemoryUsage processMemory; + Skyfire::GetProcessMemoryUsage(processMemory); + Skyfire::SystemMemoryUsage systemMemory; + Skyfire::GetSystemMemoryUsage(systemMemory); handler->PSendSysMessage("Open Source Emulation https://www.projectskyfire.org"); handler->PSendSysMessage("www.github.com/ProjectSkyfire/SkyFire_548"); @@ -145,8 +148,8 @@ public: handler->PSendSysMessage(LANG_CONNECTED_PLAYERS, playersNum, maxPlayersNum); handler->PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum); handler->PSendSysMessage(LANG_UPTIME, uptime.c_str()); - std::string memoryInfo = Skyfire::GetProcessMemoryUsage(memoryUsage) ? Skyfire::FormatProcessMemoryUsage(memoryUsage) : "unavailable"; - handler->PSendSysMessage("Memory: %s", memoryInfo.c_str()); + std::string summary = Skyfire::FormatMemoryUsageSummary(processMemory, systemMemory); + handler->PSendSysMessage("Memory Summary: %s", summary.c_str()); SendRuntimeMetrics(handler); handler->PSendSysMessage(LANG_UPDATE_DIFF, updateTime); diff --git a/src/server/shared/Utilities/MemoryUsage.cpp b/src/server/shared/Utilities/MemoryUsage.cpp index 07d2d9887b..9050b4e5f4 100644 --- a/src/server/shared/Utilities/MemoryUsage.cpp +++ b/src/server/shared/Utilities/MemoryUsage.cpp @@ -13,12 +13,19 @@ #if PLATFORM == PLATFORM_WINDOWS #include #include +#include #elif PLATFORM == PLATFORM_UNIX || PLATFORM == PLATFORM_APPLE #include #include #if PLATFORM == PLATFORM_UNIX #include +#include +#endif + +#if PLATFORM == PLATFORM_APPLE +#include +#include #endif #endif @@ -30,6 +37,13 @@ namespace stream << label << ' ' << Skyfire::FormatMemorySize(bytes); fields.push_back(stream.str()); } + + void AppendPercentageField(std::vector& fields, char const* label, double percent) + { + std::ostringstream stream; + stream << label << ' ' << std::fixed << std::setprecision(1) << percent << '%'; + fields.push_back(stream.str()); + } } bool Skyfire::GetProcessMemoryUsage(ProcessMemoryUsage& usage) @@ -91,6 +105,163 @@ bool Skyfire::GetProcessMemoryUsage(ProcessMemoryUsage& usage) #endif } +bool Skyfire::GetSystemMemoryUsage(SystemMemoryUsage& usage) +{ + usage = SystemMemoryUsage(); + +#if PLATFORM == PLATFORM_WINDOWS + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + + if (!GlobalMemoryStatusEx(&memoryStatus)) + return false; + + usage.totalPhysicalBytes = memoryStatus.ullTotalPhys; + usage.availablePhysicalBytes = memoryStatus.ullAvailPhys; + usage.usedPhysicalBytes = memoryStatus.ullTotalPhys - memoryStatus.ullAvailPhys; + usage.totalVirtualBytes = memoryStatus.ullTotalPageFile; + usage.usedVirtualBytes = memoryStatus.ullTotalPageFile - memoryStatus.ullAvailPageFile; + usage.totalSwapBytes = 0; + usage.usedSwapBytes = 0; + + if (usage.totalPhysicalBytes > 0) + { + usage.physicalUsagePercent = (static_cast(usage.usedPhysicalBytes) / + static_cast(usage.totalPhysicalBytes)) * 100.0; + } + + if (usage.totalVirtualBytes > 0) + { + usage.virtualUsagePercent = (static_cast(usage.usedVirtualBytes) / + static_cast(usage.totalVirtualBytes)) * 100.0; + } + + usage.hasValidData = true; + return true; + +#elif PLATFORM == PLATFORM_UNIX + std::ifstream meminfo("/proc/meminfo"); + if (!meminfo.is_open()) + return false; + + std::string key; + uint64 value; + std::string unit; + uint64 memTotal = 0, memAvailable = 0, swapTotal = 0, swapFree = 0; + + while (meminfo >> key >> value >> unit) + { + if (key == "MemTotal:") + memTotal = value * 1024; + else if (key == "MemAvailable:") + memAvailable = value * 1024; + else if (key == "SwapTotal:") + swapTotal = value * 1024; + else if (key == "SwapFree:") + swapFree = value * 1024; + } + + if (memTotal == 0) + return false; + + usage.totalPhysicalBytes = memTotal; + usage.availablePhysicalBytes = memAvailable > 0 ? memAvailable : 0; + usage.usedPhysicalBytes = memTotal - (memAvailable > 0 ? memAvailable : 0); + usage.totalSwapBytes = swapTotal; + usage.usedSwapBytes = swapTotal - swapFree; + usage.totalVirtualBytes = memTotal + swapTotal; + usage.usedVirtualBytes = usage.usedPhysicalBytes + usage.usedSwapBytes; + + if (usage.totalPhysicalBytes > 0) + { + usage.physicalUsagePercent = (static_cast(usage.usedPhysicalBytes) / + static_cast(usage.totalPhysicalBytes)) * 100.0; + } + + if (usage.totalVirtualBytes > 0) + { + usage.virtualUsagePercent = (static_cast(usage.usedVirtualBytes) / + static_cast(usage.totalVirtualBytes)) * 100.0; + } + + usage.hasValidData = true; + return true; + +#elif PLATFORM == PLATFORM_APPLE + int mib[2]; + size_t length; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + length = sizeof(uint64_t); + if (sysctl(mib, 2, &usage.totalPhysicalBytes, &length, nullptr, 0) != 0) + return false; + + vm_statistics64_data_t vmStats; + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + kern_return_t kernReturn = host_statistics64(mach_host_self(), + HOST_VM_INFO64, + reinterpret_cast(&vmStats), + &count); + + if (kernReturn != KERN_SUCCESS) + return false; + + int pageSize; + mib[0] = CTL_HW; + mib[1] = HW_PAGESIZE; + length = sizeof(int); + if (sysctl(mib, 2, &pageSize, &length, nullptr, 0) != 0) + return false; + + uint64 usedMemory = static_cast(vmStats.active_count + + vmStats.wire_count + + vmStats.compressor_page_count) * pageSize; + uint64 availableMemory = static_cast(vmStats.free_count + + vmStats.inactive_count + + vmStats.speculative_count) * pageSize; + + usage.usedPhysicalBytes = usedMemory; + usage.availablePhysicalBytes = availableMemory; + + struct xsw_usage swapUsage; + mib[0] = CTL_VM; + mib[1] = VM_SWAPUSAGE; + length = sizeof(swapUsage); + if (sysctl(mib, 2, &swapUsage, &length, nullptr, 0) == 0) + { + usage.totalSwapBytes = swapUsage.xsu_total; + usage.usedSwapBytes = swapUsage.xsu_used; + } + else + { + usage.totalSwapBytes = 0; + usage.usedSwapBytes = 0; + } + + usage.totalVirtualBytes = usage.totalPhysicalBytes + usage.totalSwapBytes; + usage.usedVirtualBytes = usage.usedPhysicalBytes + usage.usedSwapBytes; + + if (usage.totalPhysicalBytes > 0) + { + usage.physicalUsagePercent = (static_cast(usage.usedPhysicalBytes) / + static_cast(usage.totalPhysicalBytes)) * 100.0; + } + + if (usage.totalVirtualBytes > 0) + { + usage.virtualUsagePercent = (static_cast(usage.usedVirtualBytes) / + static_cast(usage.totalVirtualBytes)) * 100.0; + } + + usage.hasValidData = true; + return true; + +#else + return false; +#endif +} + std::string Skyfire::FormatMemorySize(uint64 bytes) { char const* units[] = { "B", "KB", "MB", "GB", "TB" }; @@ -142,3 +313,71 @@ std::string Skyfire::FormatProcessMemoryUsage(ProcessMemoryUsage const& usage) return stream.str(); } + +std::string Skyfire::FormatSystemMemoryUsage(SystemMemoryUsage const& usage) +{ + if (!usage.hasValidData) + return "System memory info unavailable"; + + std::vector fields; + + AppendMemoryField(fields, "Total RAM", usage.totalPhysicalBytes); + AppendMemoryField(fields, "Used RAM", usage.usedPhysicalBytes); + AppendMemoryField(fields, "Available RAM", usage.availablePhysicalBytes); + AppendPercentageField(fields, "RAM Usage", usage.physicalUsagePercent); + + if (usage.totalSwapBytes > 0) + { + AppendMemoryField(fields, "Total Swap", usage.totalSwapBytes); + AppendMemoryField(fields, "Used Swap", usage.usedSwapBytes); + } + + if (usage.totalVirtualBytes > 0) + { + AppendMemoryField(fields, "Total Virtual", usage.totalVirtualBytes); + AppendMemoryField(fields, "Used Virtual", usage.usedVirtualBytes); + AppendPercentageField(fields, "Virtual Usage", usage.virtualUsagePercent); + } + + std::ostringstream stream; + for (size_t i = 0; i < fields.size(); ++i) + { + if (i != 0) + stream << ", "; + + stream << fields[i]; + } + + return stream.str(); +} + +std::string Skyfire::FormatMemoryUsageSummary(ProcessMemoryUsage const& processUsage, + SystemMemoryUsage const& systemUsage) +{ + std::ostringstream stream; + + stream << "Process: " << FormatProcessMemoryUsage(processUsage); + + if (systemUsage.hasValidData) + { + stream << " | System: "; + stream << "RAM " << std::fixed << std::setprecision(1) + << systemUsage.physicalUsagePercent << "% "; + stream << "(" << FormatMemorySize(systemUsage.usedPhysicalBytes) << "/" + << FormatMemorySize(systemUsage.totalPhysicalBytes) << ")"; + + if (systemUsage.totalSwapBytes > 0) + { + stream << ", Swap " << std::fixed << std::setprecision(1) + << ((systemUsage.totalSwapBytes > 0) ? + (static_cast(systemUsage.usedSwapBytes) / + static_cast(systemUsage.totalSwapBytes) * 100.0) : 0.0) << "%"; + } + } + else + { + stream << " | System info unavailable"; + } + + return stream.str(); +} \ No newline at end of file diff --git a/src/server/shared/Utilities/MemoryUsage.h b/src/server/shared/Utilities/MemoryUsage.h index 342c1d7d19..78a623fb3d 100644 --- a/src/server/shared/Utilities/MemoryUsage.h +++ b/src/server/shared/Utilities/MemoryUsage.h @@ -25,9 +25,28 @@ namespace Skyfire bool hasPrivateBytes = false; }; + struct SystemMemoryUsage + { + uint64 totalPhysicalBytes = 0; + uint64 availablePhysicalBytes = 0; + uint64 usedPhysicalBytes = 0; + uint64 totalVirtualBytes = 0; + uint64 usedVirtualBytes = 0; + uint64 totalSwapBytes = 0; + uint64 usedSwapBytes = 0; + double physicalUsagePercent = 0.0; + double virtualUsagePercent = 0.0; + + bool hasValidData = false; + }; + bool GetProcessMemoryUsage(ProcessMemoryUsage& usage); + bool GetSystemMemoryUsage(SystemMemoryUsage& usage); + std::string FormatMemorySize(uint64 bytes); std::string FormatProcessMemoryUsage(ProcessMemoryUsage const& usage); + std::string FormatSystemMemoryUsage(SystemMemoryUsage const& usage); + std::string FormatMemoryUsageSummary(ProcessMemoryUsage const& processUsage, SystemMemoryUsage const& systemUsage); } #endif