diff options
-rw-r--r-- | src/gc/unix/config.h.in | 9 | ||||
-rw-r--r-- | src/gc/unix/configure.cmake | 30 | ||||
-rw-r--r-- | src/gc/unix/gcenv.unix.cpp | 254 | ||||
-rw-r--r-- | src/pal/src/misc/sysinfo.cpp | 83 |
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) |