summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gc/unix/config.h.in9
-rw-r--r--src/gc/unix/configure.cmake30
-rw-r--r--src/gc/unix/gcenv.unix.cpp254
-rw-r--r--src/pal/src/misc/sysinfo.cpp83
4 files changed, 349 insertions, 27 deletions
diff --git a/src/gc/unix/config.h.in b/src/gc/unix/config.h.in
index ae33c02796..699135ca89 100644
--- a/src/gc/unix/config.h.in
+++ b/src/gc/unix/config.h.in
@@ -20,5 +20,14 @@
#cmakedefine01 HAVE_PTHREAD_GETAFFINITY_NP
#cmakedefine01 HAVE_PTHREAD_NP_H
#cmakedefine01 HAVE_CPUSET_T
+#cmakedefine01 HAVE__SC_AVPHYS_PAGES
+#cmakedefine01 HAVE__SC_PHYS_PAGES
+#cmakedefine01 HAVE_SYSCONF
+#cmakedefine01 HAVE_SYSCTL
+#cmakedefine01 HAVE_SYSINFO
+#cmakedefine01 HAVE_SYSINFO_WITH_MEM_UNIT
+#cmakedefine01 HAVE_XSW_USAGE
+#cmakedefine01 HAVE_XSWDEV
+
#endif // __CONFIG_H__
diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake
index 859ffa4c48..dafb0cd274 100644
--- a/src/gc/unix/configure.cmake
+++ b/src/gc/unix/configure.cmake
@@ -95,4 +95,34 @@ endif()
check_library_exists(${PTHREAD_LIBRARY} pthread_getaffinity_np "" HAVE_PTHREAD_GETAFFINITY_NP)
+check_cxx_symbol_exists(_SC_PHYS_PAGES unistd.h HAVE__SC_PHYS_PAGES)
+check_cxx_symbol_exists(_SC_AVPHYS_PAGES unistd.h HAVE__SC_AVPHYS_PAGES)
+check_function_exists(sysctl HAVE_SYSCTL)
+check_function_exists(sysinfo HAVE_SYSINFO)
+check_function_exists(sysconf HAVE_SYSCONF)
+check_struct_has_member ("struct sysinfo" mem_unit "sys/sysinfo.h" HAVE_SYSINFO_WITH_MEM_UNIT)
+
+check_cxx_source_compiles("
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <vm/vm_param.h>
+
+int main(int argc, char **argv)
+{
+ struct xswdev xsw;
+
+ return 0;
+}" HAVE_XSWDEV)
+
+check_cxx_source_compiles("
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+int main(int argc, char **argv)
+{
+ struct xsw_usage xsu;
+
+ return 0;
+}" HAVE_XSW_USAGE)
+
configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index 4a48a4a5a9..52dd673888 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -2,9 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#define _WITH_GETLINE
#include <cstdint>
#include <cstddef>
+#include <cstdio>
#include <cassert>
+#define __STDC_FORMAT_MACROS
+#include <cinttypes>
#include <memory>
#include <pthread.h>
#include <signal.h>
@@ -30,6 +34,25 @@
#error "sys/mman.h required by GC PAL"
#endif // HAVE_SYS_MMAN_H
+#if HAVE_SYSINFO
+#include <sys/sysinfo.h>
+#endif
+
+#if HAVE_XSWDEV
+#include <vm/vm_param.h>
+#endif // HAVE_XSWDEV
+
+#ifdef __APPLE__
+#include <mach/vm_types.h>
+#include <mach/vm_param.h>
+#include <mach/mach_port.h>
+#include <mach/mach_host.h>
+#endif // __APPLE__
+
+#if HAVE_SYSCTL
+#include <sys/sysctl.h>
+#endif
+
#ifdef __linux__
#include <sys/syscall.h> // __NR_membarrier
// Ensure __NR_membarrier is defined for portable builds.
@@ -63,6 +86,16 @@ typedef cpuset_t cpu_set_t;
#include "globals.h"
#include "cgroup.h"
+#ifndef __APPLE__
+#if HAVE_SYSCONF && HAVE__SC_AVPHYS_PAGES
+#define SYSCONF_PAGES _SC_AVPHYS_PAGES
+#elif HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
+#define SYSCONF_PAGES _SC_PHYS_PAGES
+#else
+#error Dont know how to get page-size on this architecture!
+#endif
+#endif // __APPLE__
+
#if HAVE_NUMA_H
#include <numa.h>
@@ -678,6 +711,58 @@ bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size,
return false;
}
+// Get memory size multiplier based on the passed in units (k = kilo, m = mega, g = giga)
+static uint64_t GetMemorySizeMultiplier(char units)
+{
+ switch(units)
+ {
+ case 'g':
+ case 'G': return 1024 * 1024 * 1024;
+ case 'm':
+ case 'M': return 1024 * 1024;
+ case 'k':
+ case 'K': return 1024;
+ }
+
+ // No units multiplier
+ return 1;
+}
+
+#ifndef __APPLE__
+// Try to read the MemAvailable entry from /proc/meminfo.
+// Return true if the /proc/meminfo existed, the entry was present and we were able to parse it.
+static bool ReadMemAvailable(uint64_t* memAvailable)
+{
+ bool foundMemAvailable = false;
+ FILE* memInfoFile = fopen("/proc/meminfo", "r");
+ if (memInfoFile != NULL)
+ {
+ char *line = nullptr;
+ size_t lineLen = 0;
+
+ while (getline(&line, &lineLen, memInfoFile) != -1)
+ {
+ char units = '\0';
+ uint64_t available;
+ int fieldsParsed = sscanf(line, "MemAvailable: %" SCNu64 " %cB", &available, &units);
+
+ if (fieldsParsed >= 1)
+ {
+ uint64_t multiplier = GetMemorySizeMultiplier(units);
+ *memAvailable = available * multiplier;
+ foundMemAvailable = true;
+ break;
+ }
+ }
+
+ free(line);
+ fclose(memInfoFile);
+ }
+
+ return foundMemAvailable;
+}
+#endif // __APPLE__
+
// Get size of the largest cache on the processor die
// Parameters:
// trueSize - true to return true cache size, false to return scaled up size based on
@@ -799,6 +884,8 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
return restricted_limit;
}
+ // Get the physical memory size
+#if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
long pages = sysconf(_SC_PHYS_PAGES);
if (pages == -1)
{
@@ -812,6 +899,124 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
}
return pages * pageSize;
+#elif HAVE_SYSCTL
+ int mib[3];
+ mib[0] = CTL_HW;
+ mib[1] = HW_MEMSIZE;
+ int64_t physical_memory = 0;
+ size_t length = sizeof(INT64);
+ int rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
+ assert(rc == 0);
+
+ return physical_memory;
+#else // HAVE_SYSCTL
+#error "Don't know how to get total physical memory on this platform"
+#endif // HAVE_SYSCTL
+}
+
+// Get amount of physical memory available for use in the system
+uint64_t GetAvailablePhysicalMemory()
+{
+ uint64_t available = 0;
+
+ // Get the physical memory available.
+#ifndef __APPLE__
+ static volatile bool tryReadMemInfo = true;
+
+ if (tryReadMemInfo)
+ {
+ // Ensure that we don't try to read the /proc/meminfo in successive calls to the GlobalMemoryStatusEx
+ // if we have failed to access the file or the file didn't contain the MemAvailable value.
+ tryReadMemInfo = ReadMemAvailable(&available);
+ }
+
+ if (!tryReadMemInfo)
+ {
+ // The /proc/meminfo doesn't exist or it doesn't contain the MemAvailable row or the format of the row is invalid
+ // Fall back to getting the available pages using sysconf.
+ available = sysconf(SYSCONF_PAGES) * sysconf(_SC_PAGE_SIZE);
+ }
+#else // __APPLE__
+ vm_size_t page_size;
+ mach_port_t mach_port;
+ mach_msg_type_number_t count;
+ vm_statistics_data_t vm_stats;
+ mach_port = mach_host_self();
+ count = sizeof(vm_stats) / sizeof(natural_t);
+ if (KERN_SUCCESS == host_page_size(mach_port, &page_size))
+ {
+ if (KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count))
+ {
+ available = (int64_t)vm_stats.free_count * (int64_t)page_size;
+ }
+ }
+ mach_port_deallocate(mach_task_self(), mach_port);
+#endif // __APPLE__
+
+ return available;
+}
+
+// Get the amount of available swap space
+uint64_t GetAvailablePageFile()
+{
+ uint64_t available = 0;
+
+ int mib[3];
+ int rc;
+
+ // Get swap file size
+#if HAVE_XSW_USAGE
+ // This is available on OSX
+ struct xsw_usage xsu;
+ mib[0] = CTL_VM;
+ mib[1] = VM_SWAPUSAGE;
+ size_t length = sizeof(xsu);
+ rc = sysctl(mib, 2, &xsu, &length, NULL, 0);
+ if (rc == 0)
+ {
+ available = xsu.xsu_avail;
+ }
+#elif HAVE_XSWDEV
+ // E.g. FreeBSD
+ struct xswdev xsw;
+
+ size_t length = 2;
+ rc = sysctlnametomib("vm.swap_info", mib, &length);
+ if (rc == 0)
+ {
+ int pagesize = getpagesize();
+ // Aggregate the information for all swap files on the system
+ for (mib[2] = 0; ; mib[2]++)
+ {
+ length = sizeof(xsw);
+ rc = sysctl(mib, 3, &xsw, &length, NULL, 0);
+ if ((rc < 0) || (xsw.xsw_version != XSWDEV_VERSION))
+ {
+ // All the swap files were processed or coreclr was built against
+ // a version of headers not compatible with the current XSWDEV_VERSION.
+ break;
+ }
+
+ uint64_t avail = xsw.xsw_nblks - xsw.xsw_used;
+ available += avail * pagesize;
+ }
+ }
+#elif HAVE_SYSINFO
+ // Linux
+ struct sysinfo info;
+ rc = sysinfo(&info);
+ if (rc == 0)
+ {
+ available = info.freeswap;
+#if HAVE_SYSINFO_WITH_MEM_UNIT
+ // A newer version of the sysinfo structure represents all the sizes
+ // in mem_unit instead of bytes
+ available *= info.mem_unit;
+#endif // HAVE_SYSINFO_WITH_MEM_UNIT
+ }
+#endif // HAVE_SYSINFO
+
+ return available;
}
// Get memory status
@@ -822,30 +1027,49 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
// available_page_file - The maximum amount of memory the current process can commit, in bytes.
void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
{
+ uint64_t available = 0;
+ uint32_t load = 0;
+
if (memory_load != nullptr || available_physical != nullptr)
{
- uint64_t total = GetPhysicalMemoryLimit();
-
- uint64_t available = 0;
- uint32_t load = 0;
size_t used;
-
- // Get the physical memory in use - from it, we can get the physical memory available.
- // We do this only when we have the total physical memory available.
- if (total > 0 && GetPhysicalMemoryUsed(&used))
+ bool isRestricted;
+ uint64_t total = GetPhysicalMemoryLimit(&isRestricted);
+ if (isRestricted)
{
- available = total > used ? total-used : 0;
- load = (uint32_t)(((float)used * 100) / (float)total);
+ // Get the physical memory in use - from it, we can get the physical memory available.
+ // We do this only when we have the total physical memory available.
+ if (GetPhysicalMemoryUsed(&used))
+ {
+ available = total > used ? total-used : 0;
+ load = (uint32_t)(((float)used * 100) / (float)total);
+ }
}
+ else
+ {
+ available = GetAvailablePhysicalMemory();
- if (memory_load != nullptr)
- *memory_load = load;
- if (available_physical != nullptr)
- *available_physical = available;
+ if (memory_load != NULL)
+ {
+ uint32_t load = 0;
+ if (total > available)
+ {
+ used = total - available;
+ load = (uint32_t)(((float)used * 100) / (float)total);
+ }
+ }
+ }
}
+ if (available_physical != NULL)
+ *available_physical = available;
+
+ if (memory_load != nullptr)
+ *memory_load = load;
+
if (available_page_file != nullptr)
- *available_page_file = 0;
+ *available_page_file = GetAvailablePageFile();
+
}
// Get a high precision performance counter
diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp
index e3d001b544..52e8ec065d 100644
--- a/src/pal/src/misc/sysinfo.cpp
+++ b/src/pal/src/misc/sysinfo.cpp
@@ -25,6 +25,8 @@ Revision History:
#include <sched.h>
#include <errno.h>
#include <unistd.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
#include <sys/types.h>
#if HAVE_SYSCTL
#include <sys/sysctl.h>
@@ -239,6 +241,58 @@ GetSystemInfo(
PERF_EXIT(GetSystemInfo);
}
+// Get memory size multiplier based on the passed in units (k = kilo, m = mega, g = giga)
+static uint64_t GetMemorySizeMultiplier(char units)
+{
+ switch(units)
+ {
+ case 'g':
+ case 'G': return 1024 * 1024 * 1024;
+ case 'm':
+ case 'M': return 1024 * 1024;
+ case 'k':
+ case 'K': return 1024;
+ }
+
+ // No units multiplier
+ return 1;
+}
+
+#ifndef __APPLE__
+// Try to read the MemAvailable entry from /proc/meminfo.
+// Return true if the /proc/meminfo existed, the entry was present and we were able to parse it.
+static bool ReadMemAvailable(uint64_t* memAvailable)
+{
+ bool foundMemAvailable = false;
+ FILE* memInfoFile = fopen("/proc/meminfo", "r");
+ if (memInfoFile != NULL)
+ {
+ char *line = nullptr;
+ size_t lineLen = 0;
+
+ while (getline(&line, &lineLen, memInfoFile) != -1)
+ {
+ char units = '\0';
+ uint64_t available;
+ int fieldsParsed = sscanf(line, "MemAvailable: %" SCNu64 " %cB", &available, &units);
+
+ if (fieldsParsed >= 1)
+ {
+ uint64_t multiplier = GetMemorySizeMultiplier(units);
+ *memAvailable = available * multiplier;
+ foundMemAvailable = true;
+ break;
+ }
+ }
+
+ free(line);
+ fclose(memInfoFile);
+ }
+
+ return foundMemAvailable;
+}
+#endif // __APPLE__
+
/*++
Function:
GlobalMemoryStatusEx
@@ -365,7 +419,22 @@ GlobalMemoryStatusEx(
if (lpBuffer->ullTotalPhys > 0)
{
#ifndef __APPLE__
- lpBuffer->ullAvailPhys = sysconf(SYSCONF_PAGES) * sysconf(_SC_PAGE_SIZE);
+ static volatile bool tryReadMemInfo = true;
+
+ if (tryReadMemInfo)
+ {
+ // Ensure that we don't try to read the /proc/meminfo in successive calls to the GlobalMemoryStatusEx
+ // if we have failed to access the file or the file didn't contain the MemAvailable value.
+ tryReadMemInfo = ReadMemAvailable(&lpBuffer->ullAvailPhys);
+ }
+
+ if (!tryReadMemInfo)
+ {
+ // The /proc/meminfo doesn't exist or it doesn't contain the MemAvailable row or the format of the row is invalid
+ // Fall back to getting the available pages using sysconf.
+ lpBuffer->ullAvailPhys = sysconf(SYSCONF_PAGES) * sysconf(_SC_PAGE_SIZE);
+ }
+
INT64 used_memory = lpBuffer->ullTotalPhys - lpBuffer->ullAvailPhys;
lpBuffer->dwMemoryLoad = (DWORD)((used_memory * 100) / lpBuffer->ullTotalPhys);
#else
@@ -445,17 +514,7 @@ ReadMemoryValueFromFile(const char* filename, size_t* val)
if (errno != 0)
goto done;
- multiplier = 1;
- switch(*endptr)
- {
- case 'g':
- case 'G': multiplier = 1024;
- case 'm':
- case 'M': multiplier = multiplier*1024;
- case 'k':
- case 'K': multiplier = multiplier*1024;
- }
-
+ multiplier = GetMemorySizeMultiplier(*endptr);
*val = num * multiplier;
result = true;
if (*val/multiplier != num)