Sfoglia il codice sorgente

[OS] Implement and expose to scripting APIs `get_memory_info` method instead of old `get_free_static_memory`.

bruvzg 2 anni fa
parent
commit
628f3b2f79

+ 5 - 0
core/core_bind.cpp

@@ -425,6 +425,10 @@ uint64_t OS::get_static_memory_peak_usage() const {
 	return ::OS::get_singleton()->get_static_memory_peak_usage();
 }
 
+Dictionary OS::get_memory_info() const {
+	return ::OS::get_singleton()->get_memory_info();
+}
+
 /** This method uses a signed argument for better error reporting as it's used from the scripting API. */
 void OS::delay_usec(int p_usec) const {
 	ERR_FAIL_COND_MSG(
@@ -582,6 +586,7 @@ void OS::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("get_static_memory_usage"), &OS::get_static_memory_usage);
 	ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &OS::get_static_memory_peak_usage);
+	ClassDB::bind_method(D_METHOD("get_memory_info"), &OS::get_memory_info);
 
 	ClassDB::bind_method(D_METHOD("move_to_trash", "path"), &OS::move_to_trash);
 	ClassDB::bind_method(D_METHOD("get_user_data_dir"), &OS::get_user_data_dir);

+ 1 - 0
core/core_bind.h

@@ -190,6 +190,7 @@ public:
 
 	uint64_t get_static_memory_usage() const;
 	uint64_t get_static_memory_peak_usage() const;
+	Dictionary get_memory_info() const;
 
 	void delay_usec(int p_usec) const;
 	void delay_msec(int p_msec) const;

+ 9 - 2
core/os/os.cpp

@@ -295,8 +295,15 @@ Error OS::set_cwd(const String &p_cwd) {
 	return ERR_CANT_OPEN;
 }
 
-uint64_t OS::get_free_static_memory() const {
-	return Memory::get_mem_available();
+Dictionary OS::get_memory_info() const {
+	Dictionary meminfo;
+
+	meminfo["physical"] = -1;
+	meminfo["free"] = -1;
+	meminfo["available"] = -1;
+	meminfo["stack"] = -1;
+
+	return meminfo;
 }
 
 void OS::yield() {

+ 1 - 1
core/os/os.h

@@ -229,7 +229,7 @@ public:
 
 	virtual uint64_t get_static_memory_usage() const;
 	virtual uint64_t get_static_memory_peak_usage() const;
-	virtual uint64_t get_free_static_memory() const;
+	virtual Dictionary get_memory_info() const;
 
 	RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; }
 

+ 10 - 0
doc/classes/OS.xml

@@ -277,6 +277,16 @@
 				[b]Note:[/b] Thread IDs are not deterministic and may be reused across application restarts.
 			</description>
 		</method>
+		<method name="get_memory_info" qualifiers="const">
+			<return type="Dictionary" />
+			<description>
+				Returns the [Dictionary] with the following keys:
+				[code]"physical"[/code] - total amount of usable physical memory, in bytes or [code]-1[/code] if unknown. This value can be slightly less than the actual physical memory amount, since it does not include memory reserved by kernel and devices.
+				[code]"free"[/code] - amount of physical memory, that can be immediately allocated without disk access or other costly operation, in bytes or [code]-1[/code] if unknown. The process might be able to allocate more physical memory, but such allocation will require moving inactive pages to disk and can take some time.
+				[code]"available"[/code] - amount of memory, that can be allocated without extending the swap file(s), in bytes or [code]-1[/code] if unknown. This value include both physical memory and swap.
+				[code]"stack"[/code] - size of the current thread stack, in bytes or [code]-1[/code] if unknown.
+			</description>
+		</method>
 		<method name="get_model_name" qualifiers="const">
 			<return type="String" />
 			<description>

+ 204 - 1
drivers/unix/os_unix.cpp

@@ -41,9 +41,12 @@
 #include "drivers/unix/thread_posix.h"
 #include "servers/rendering_server.h"
 
-#ifdef __APPLE__
+#if defined(__APPLE__)
 #include <mach-o/dyld.h>
+#include <mach/host_info.h>
+#include <mach/mach_host.h>
 #include <mach/mach_time.h>
+#include <sys/sysctl.h>
 #endif
 
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
@@ -51,6 +54,19 @@
 #include <sys/sysctl.h>
 #endif
 
+#if defined(__FreeBSD__)
+#include <kvm.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <sys/swap.h>
+#include <uvm/uvmexp.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <uvm/uvm_extern.h>
+#endif
+
 #include <dlfcn.h>
 #include <errno.h>
 #include <poll.h>
@@ -59,6 +75,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/resource.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <time.h>
@@ -274,6 +291,192 @@ uint64_t OS_Unix::get_ticks_usec() const {
 	return longtime;
 }
 
+Dictionary OS_Unix::get_memory_info() const {
+	Dictionary meminfo;
+
+	meminfo["physical"] = -1;
+	meminfo["free"] = -1;
+	meminfo["available"] = -1;
+	meminfo["stack"] = -1;
+
+#if defined(__APPLE__)
+	int pagesize = 0;
+	size_t len = sizeof(pagesize);
+	if (sysctlbyname("vm.pagesize", &pagesize, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get vm.pagesize, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	int64_t phy_mem = 0;
+	len = sizeof(phy_mem);
+	if (sysctlbyname("hw.memsize", &phy_mem, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get hw.memsize, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
+	vm_statistics64_data_t vmstat;
+	if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &count) != KERN_SUCCESS) {
+		ERR_PRINT(vformat("Could not get host vm statistics."));
+	}
+	struct xsw_usage swap_used;
+	len = sizeof(swap_used);
+	if (sysctlbyname("vm.swapusage", &swap_used, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get vm.swapusage, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	if (phy_mem != 0) {
+		meminfo["physical"] = phy_mem;
+	}
+	if (vmstat.free_count * (int64_t)pagesize != 0) {
+		meminfo["free"] = vmstat.free_count * (int64_t)pagesize;
+	}
+	if (swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize != 0) {
+		meminfo["available"] = swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize;
+	}
+#elif defined(__FreeBSD__)
+	int pagesize = 0;
+	size_t len = sizeof(pagesize);
+	if (sysctlbyname("vm.stats.vm.v_page_size", &pagesize, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_size, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	uint64_t mtotal = 0;
+	len = sizeof(mtotal);
+	if (sysctlbyname("vm.stats.vm.v_page_count", &mtotal, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_count, error code: %d - %s", errno, strerror(errno)));
+	}
+	uint64_t mfree = 0;
+	len = sizeof(mfree);
+	if (sysctlbyname("vm.stats.vm.v_free_count", &mfree, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get vm.stats.vm.v_free_count, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	uint64_t stotal = 0;
+	uint64_t sused = 0;
+	char errmsg[_POSIX2_LINE_MAX] = {};
+	kvm_t *kd = kvm_openfiles(nullptr, "/dev/null", nullptr, 0, errmsg);
+	if (kd == nullptr) {
+		ERR_PRINT(vformat("kvm_openfiles failed, error: %s", errmsg));
+	} else {
+		struct kvm_swap swap_info[32];
+		int count = kvm_getswapinfo(kd, swap_info, 32, 0);
+		for (int i = 0; i < count; i++) {
+			stotal += swap_info[i].ksw_total;
+			sused += swap_info[i].ksw_used;
+		}
+		kvm_close(kd);
+	}
+
+	if (mtotal * pagesize != 0) {
+		meminfo["physical"] = mtotal * pagesize;
+	}
+	if (mfree * pagesize != 0) {
+		meminfo["free"] = mfree * pagesize;
+	}
+	if ((mfree + stotal - sused) * pagesize != 0) {
+		meminfo["available"] = (mfree + stotal - sused) * pagesize;
+	}
+#elif defined(__OpenBSD__)
+	int pagesize = sysconf(_SC_PAGESIZE);
+
+	const int mib[] = { CTL_VM, VM_UVMEXP };
+	uvmexp uvmexp_info;
+	size_t len = sizeof(uvmexp_info);
+	if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	uint64_t stotal = 0;
+	uint64_t sused = 0;
+	int count = swapctl(SWAP_NSWAP, 0, 0);
+	if (count > 0) {
+		swapent swap_info[count];
+		count = swapctl(SWAP_STATS, swap_info, count);
+
+		for (int i = 0; i < count; i++) {
+			if (swap_info[i].se_flags & SWF_ENABLE) {
+				sused += swap_info[i].se_inuse;
+				stotal += swap_info[i].se_nblks;
+			}
+		}
+	}
+
+	if (uvmexp_info.npages * pagesize != 0) {
+		meminfo["physical"] = uvmexp_info.npages * pagesize;
+	}
+	if (uvmexp_info.free * pagesize != 0) {
+		meminfo["free"] = uvmexp_info.free * pagesize;
+	}
+	if ((uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE != 0) {
+		meminfo["available"] = (uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE;
+	}
+#elif defined(__NetBSD__)
+	int pagesize = sysconf(_SC_PAGESIZE);
+
+	const int mib[] = { CTL_VM, VM_UVMEXP2 };
+	uvmexp_sysctl uvmexp_info;
+	size_t len = sizeof(uvmexp_info);
+	if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) {
+		ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP2, error code: %d - %s", errno, strerror(errno)));
+	}
+
+	if (uvmexp_info.npages * pagesize != 0) {
+		meminfo["physical"] = uvmexp_info.npages * pagesize;
+	}
+	if (uvmexp_info.free * pagesize != 0) {
+		meminfo["free"] = uvmexp_info.free * pagesize;
+	}
+	if ((uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize != 0) {
+		meminfo["available"] = (uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize;
+	}
+#else
+	Error err;
+	Ref<FileAccess> f = FileAccess::open("/proc/meminfo", FileAccess::READ, &err);
+	uint64_t mtotal = 0;
+	uint64_t mfree = 0;
+	uint64_t sfree = 0;
+	while (f.is_valid() && !f->eof_reached()) {
+		String s = f->get_line().strip_edges();
+		if (s.begins_with("MemTotal:")) {
+			Vector<String> stok = s.replace("MemTotal:", "").strip_edges().split(" ");
+			if (stok.size() == 2) {
+				mtotal = stok[0].to_int() * 1024;
+			}
+		}
+		if (s.begins_with("MemFree:")) {
+			Vector<String> stok = s.replace("MemFree:", "").strip_edges().split(" ");
+			if (stok.size() == 2) {
+				mfree = stok[0].to_int() * 1024;
+			}
+		}
+		if (s.begins_with("SwapFree:")) {
+			Vector<String> stok = s.replace("SwapFree:", "").strip_edges().split(" ");
+			if (stok.size() == 2) {
+				sfree = stok[0].to_int() * 1024;
+			}
+		}
+	}
+
+	if (mtotal != 0) {
+		meminfo["physical"] = mtotal;
+	}
+	if (mfree != 0) {
+		meminfo["free"] = mfree;
+	}
+	if (mfree + sfree != 0) {
+		meminfo["available"] = mfree + sfree;
+	}
+#endif
+
+	rlimit stackinfo = {};
+	getrlimit(RLIMIT_STACK, &stackinfo);
+
+	if (stackinfo.rlim_cur != 0) {
+		meminfo["stack"] = (int64_t)stackinfo.rlim_cur;
+	}
+
+	return meminfo;
+}
+
 Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
 #ifdef __EMSCRIPTEN__
 	// Don't compile this code at all to avoid undefined references.

+ 2 - 0
drivers/unix/os_unix.h

@@ -73,6 +73,8 @@ public:
 	virtual void delay_usec(uint32_t p_usec) const override;
 	virtual uint64_t get_ticks_usec() const override;
 
+	virtual Dictionary get_memory_info() const override;
+
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
 	virtual Error kill(const ProcessID &p_pid) override;

+ 3 - 0
platform/linuxbsd/detect.py

@@ -463,6 +463,9 @@ def configure(env: "Environment"):
             else:
                 env.Append(LINKFLAGS=["-T", "platform/linuxbsd/pck_embed.legacy.ld"])
 
+    if platform.system() == "FreeBSD":
+        env.Append(LINKFLAGS=["-lkvm"])
+
     ## Cross-compilation
     # TODO: Support cross-compilation on architectures other than x86.
     host_is_64_bit = sys.maxsize > 2**32

+ 38 - 0
platform/windows/os_windows.cpp

@@ -51,6 +51,7 @@
 #include <direct.h>
 #include <knownfolders.h>
 #include <process.h>
+#include <psapi.h>
 #include <regstr.h>
 #include <shlobj.h>
 #include <wbemcli.h>
@@ -683,6 +684,43 @@ static void _append_to_pipe(char *p_bytes, int p_size, String *r_pipe, Mutex *p_
 	}
 }
 
+Dictionary OS_Windows::get_memory_info() const {
+	Dictionary meminfo;
+
+	meminfo["physical"] = -1;
+	meminfo["free"] = -1;
+	meminfo["available"] = -1;
+	meminfo["stack"] = -1;
+
+	PERFORMANCE_INFORMATION pref_info;
+	pref_info.cb = sizeof(pref_info);
+	GetPerformanceInfo(&pref_info, sizeof(pref_info));
+
+	typedef void(WINAPI * PGetCurrentThreadStackLimits)(PULONG_PTR, PULONG_PTR);
+	PGetCurrentThreadStackLimits GetCurrentThreadStackLimits = (PGetCurrentThreadStackLimits)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetCurrentThreadStackLimits");
+
+	ULONG_PTR LowLimit = 0;
+	ULONG_PTR HighLimit = 0;
+	if (GetCurrentThreadStackLimits) {
+		GetCurrentThreadStackLimits(&LowLimit, &HighLimit);
+	}
+
+	if (pref_info.PhysicalTotal * pref_info.PageSize != 0) {
+		meminfo["physical"] = pref_info.PhysicalTotal * pref_info.PageSize;
+	}
+	if (pref_info.PhysicalAvailable * pref_info.PageSize != 0) {
+		meminfo["free"] = pref_info.PhysicalAvailable * pref_info.PageSize;
+	}
+	if (pref_info.CommitLimit * pref_info.PageSize != 0) {
+		meminfo["available"] = pref_info.CommitLimit * pref_info.PageSize;
+	}
+	if (HighLimit - LowLimit != 0) {
+		meminfo["stack"] = HighLimit - LowLimit;
+	}
+
+	return meminfo;
+}
+
 Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
 	String path = p_path.replace("/", "\\");
 	String command = _quote_command_line_argument(path);

+ 2 - 0
platform/windows/os_windows.h

@@ -178,6 +178,8 @@ public:
 	virtual void delay_usec(uint32_t p_usec) const override;
 	virtual uint64_t get_ticks_usec() const override;
 
+	virtual Dictionary get_memory_info() const override;
+
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
 	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
 	virtual Error kill(const ProcessID &p_pid) override;