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)
This commit is contained in:
Nightprince 2026-06-29 12:06:31 +03:30
parent adfe27d1be
commit 91facd3a20
4 changed files with 266 additions and 5 deletions

View file

@ -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")

View file

@ -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);

View file

@ -13,12 +13,19 @@
#if PLATFORM == PLATFORM_WINDOWS
#include <windows.h>
#include <psapi.h>
#include <sysinfoapi.h>
#elif PLATFORM == PLATFORM_UNIX || PLATFORM == PLATFORM_APPLE
#include <sys/resource.h>
#include <unistd.h>
#if PLATFORM == PLATFORM_UNIX
#include <fstream>
#include <sys/sysinfo.h>
#endif
#if PLATFORM == PLATFORM_APPLE
#include <mach/mach.h>
#include <sys/sysctl.h>
#endif
#endif
@ -30,6 +37,13 @@ namespace
stream << label << ' ' << Skyfire::FormatMemorySize(bytes);
fields.push_back(stream.str());
}
void AppendPercentageField(std::vector<std::string>& 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<double>(usage.usedPhysicalBytes) /
static_cast<double>(usage.totalPhysicalBytes)) * 100.0;
}
if (usage.totalVirtualBytes > 0)
{
usage.virtualUsagePercent = (static_cast<double>(usage.usedVirtualBytes) /
static_cast<double>(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<double>(usage.usedPhysicalBytes) /
static_cast<double>(usage.totalPhysicalBytes)) * 100.0;
}
if (usage.totalVirtualBytes > 0)
{
usage.virtualUsagePercent = (static_cast<double>(usage.usedVirtualBytes) /
static_cast<double>(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<host_info_t>(&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<uint64>(vmStats.active_count +
vmStats.wire_count +
vmStats.compressor_page_count) * pageSize;
uint64 availableMemory = static_cast<uint64>(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<double>(usage.usedPhysicalBytes) /
static_cast<double>(usage.totalPhysicalBytes)) * 100.0;
}
if (usage.totalVirtualBytes > 0)
{
usage.virtualUsagePercent = (static_cast<double>(usage.usedVirtualBytes) /
static_cast<double>(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<std::string> 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<double>(systemUsage.usedSwapBytes) /
static_cast<double>(systemUsage.totalSwapBytes) * 100.0) : 0.0) << "%";
}
}
else
{
stream << " | System info unavailable";
}
return stream.str();
}

View file

@ -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