Skip to content

Commit dd571b4

Browse files
committed
Add shared function getProcessMemoryStats (Fixes #1563)
I have tried adding system-wide memory stats, but these were very unreliable for the 32-bit client/server. The numbers would only confuse users of that API and they might assume that there is less memory available. The system-wide memory stats include every running 32-bit process on a x64 host system, not just the one running in WOW64.
1 parent d81cc9e commit dd571b4

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

Shared/mods/deathmatch/logic/lua/LuaBasic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ namespace lua
4444

4545
inline void Push(lua_State* L, nullptr_t) { lua_pushnil(L); }
4646

47+
inline void Push(lua_State* L, const char* value) { lua_pushstring(L, value); }
48+
4749
inline void Push(lua_State* L, const std::string& value) { lua_pushlstring(L, value.data(), value.length()); }
4850

4951
inline void Push(lua_State* L, const std::string_view& value) { lua_pushlstring(L, value.data(), value.length()); }

Shared/mods/deathmatch/logic/luadefs/CLuaUtilDefs.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,30 @@
1212
#include "CLuaUtilDefs.h"
1313
#include "CScriptArgReader.h"
1414
#include "Utils.h"
15+
#include <lua/CLuaFunctionParser.h>
16+
#include <SharedUtil.Memory.h>
1517

1618
#ifndef MTA_CLIENT
1719
#include "CRemoteCalls.h"
1820
#endif
1921

22+
static auto GetProcessMemoryStats() -> std::optional<std::unordered_map<const char*, lua_Number>>
23+
{
24+
ProcessMemoryStats memoryStats{};
25+
26+
if (TryGetProcessMemoryStats(memoryStats))
27+
{
28+
return std::unordered_map<const char*, lua_Number>{
29+
{"virtual", memoryStats.virtualMemorySize},
30+
{"resident", memoryStats.residentMemorySize},
31+
{"shared", memoryStats.sharedMemorySize},
32+
{"private", memoryStats.privateMemorySize},
33+
};
34+
}
35+
36+
return std::nullopt;
37+
}
38+
2039
void CLuaUtilDefs::LoadFunctions()
2140
{
2241
constexpr static const std::pair<const char*, lua_CFunction> functions[]{
@@ -53,6 +72,7 @@ void CLuaUtilDefs::LoadFunctions()
5372
// Utility functions
5473
{"gettok", GetTok},
5574
{"tocolor", tocolor},
75+
{"getProcessMemoryStats", ArgumentParser<GetProcessMemoryStats>},
5676
};
5777

5878
// Add functions

Shared/sdk/SharedUtil.Memory.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: SharedUtil.Memory.h
6+
*
7+
* Multi Theft Auto is available from https://multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
11+
#pragma once
12+
13+
#include <stddef.h>
14+
15+
namespace SharedUtil
16+
{
17+
struct ProcessMemoryStats
18+
{
19+
size_t virtualMemorySize;
20+
size_t residentMemorySize;
21+
size_t sharedMemorySize;
22+
size_t privateMemorySize;
23+
};
24+
25+
bool TryGetProcessMemoryStats(ProcessMemoryStats& out);
26+
}

Shared/sdk/SharedUtil.Memory.hpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: SharedUtil.Memory.hpp
6+
*
7+
* Multi Theft Auto is available from https://multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
11+
#include "SharedUtil.Memory.h"
12+
13+
#ifdef _WIN32
14+
#include <Windows.h>
15+
#include <Psapi.h>
16+
#include <vector>
17+
#elif defined(__APPLE__)
18+
#include <mach/mach.h>
19+
#include <unistd.h>
20+
#else
21+
#include <fstream>
22+
#include <unistd.h>
23+
#include <sys/sysinfo.h>
24+
#endif
25+
26+
namespace SharedUtil
27+
{
28+
#ifdef _WIN32
29+
static HANDLE g_process = GetCurrentProcess();
30+
#endif
31+
32+
bool TryGetProcessMemoryStats(ProcessMemoryStats& out)
33+
{
34+
out = {};
35+
36+
#ifdef _WIN32
37+
MEMORYSTATUSEX memoryStatus{};
38+
memoryStatus.dwLength = sizeof(memoryStatus);
39+
40+
if (!GlobalMemoryStatusEx(&memoryStatus))
41+
return false;
42+
43+
out.virtualMemorySize = static_cast<size_t>(memoryStatus.ullTotalVirtual - memoryStatus.ullAvailVirtual);
44+
45+
static std::vector<char> workingSetBuffer(2048 * sizeof(PSAPI_WORKING_SET_BLOCK) + sizeof(PSAPI_WORKING_SET_INFORMATION));
46+
auto workingSetInfo = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(workingSetBuffer.data());
47+
BOOL success = QueryWorkingSet(g_process, workingSetBuffer.data(), workingSetBuffer.size());
48+
49+
if (!success && GetLastError() == ERROR_BAD_LENGTH)
50+
{
51+
workingSetInfo->NumberOfEntries += 64; // Insurance in case the number of entries changes.
52+
workingSetBuffer.resize(workingSetInfo->NumberOfEntries * sizeof(PSAPI_WORKING_SET_BLOCK) + sizeof(PSAPI_WORKING_SET_INFORMATION));
53+
workingSetInfo = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(workingSetBuffer.data());
54+
success = QueryWorkingSet(g_process, workingSetBuffer.data(), workingSetBuffer.size());
55+
}
56+
57+
if (success)
58+
{
59+
for (ULONG_PTR i = 0; i < workingSetInfo->NumberOfEntries; ++i)
60+
{
61+
const PSAPI_WORKING_SET_BLOCK& block = workingSetInfo->WorkingSetInfo[i];
62+
63+
if (block.Shared)
64+
{
65+
out.sharedMemorySize++;
66+
}
67+
else
68+
{
69+
out.privateMemorySize++;
70+
}
71+
}
72+
73+
static size_t pageSize = ([]() {
74+
SYSTEM_INFO sysInfo{};
75+
GetSystemInfo(&sysInfo);
76+
return static_cast<size_t>(sysInfo.dwPageSize);
77+
})();
78+
79+
out.sharedMemorySize *= pageSize;
80+
out.privateMemorySize *= pageSize;
81+
out.residentMemorySize = out.sharedMemorySize + out.privateMemorySize;
82+
}
83+
else
84+
{
85+
PROCESS_MEMORY_COUNTERS_EX memoryInfo{};
86+
87+
if (!GetProcessMemoryInfo(g_process, reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&memoryInfo), sizeof(memoryInfo)))
88+
return false;
89+
90+
out.residentMemorySize = memoryInfo.WorkingSetSize;
91+
}
92+
93+
return true;
94+
#elif defined(__APPLE__)
95+
struct task_basic_info info;
96+
mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;
97+
98+
if (task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &info_count) != KERN_SUCCESS)
99+
return false;
100+
101+
static const size_t pageSize = static_cast<size_t>(getpagesize());
102+
out.virtualMemorySize = info.virtual_size * pageSize;
103+
out.residentMemorySize = info.resident_size * pageSize;
104+
return true;
105+
#else
106+
long rawPageSize = sysconf(_SC_PAGE_SIZE);
107+
108+
if (rawPageSize <= 0)
109+
return false;
110+
111+
auto pageSize = static_cast<size_t>(rawPageSize);
112+
113+
std::ifstream stat("/proc/self/statm");
114+
115+
if (!stat.is_open())
116+
return false;
117+
118+
size_t size = 0, resident = 0, shared = 0;
119+
stat >> size >> resident >> shared;
120+
stat.close();
121+
122+
out.virtualMemorySize = size * pageSize;
123+
out.residentMemorySize = resident * pageSize;
124+
out.sharedMemorySize = shared * pageSize;
125+
126+
if (out.residentMemorySize > out.sharedMemorySize)
127+
out.privateMemorySize = out.residentMemorySize - out.sharedMemorySize;
128+
return true;
129+
#endif
130+
}
131+
}

Shared/sdk/SharedUtil.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
#if defined(SHARED_UTIL_WITH_SYS_INFO)
1818
#include "SharedUtil.SysInfo.hpp"
1919
#endif
20+
#include "SharedUtil.Memory.hpp"

Shared/sdk/SharedUtil.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@
2424
#include "SharedUtil.Profiling.hpp"
2525
#include "SharedUtil.Logging.hpp"
2626
#include "SharedUtil.AsyncTaskScheduler.hpp"
27+
#include "SharedUtil.Memory.hpp"

0 commit comments

Comments
 (0)