diff options
author | agoretsky <agoretsky15@gmail.com> | 2019-10-14 20:03:12 +0300 |
---|---|---|
committer | William Godbe <wigodbe@microsoft.com> | 2019-10-14 10:03:12 -0700 |
commit | ff243be899fae73904a1bf5401ecb93b6e80d85c (patch) | |
tree | 5dce2eb3115b4972ca9fa8ced3efa72aaf745fba /src/pal | |
parent | e38f3722a98ea66b805f388cd613ac587a44d7d2 (diff) | |
download | coreclr-ff243be899fae73904a1bf5401ecb93b6e80d85c.tar.gz coreclr-ff243be899fae73904a1bf5401ecb93b6e80d85c.tar.bz2 coreclr-ff243be899fae73904a1bf5401ecb93b6e80d85c.zip |
Fix available memory extraction on Linux (#26764) (#26938)
* Fix available memory extraction on Linux
The GlobalMemoryStatusEx in PAL is returning number of free physical pages in
the ullAvailPhys member. But there are additional pages that are allocated
as buffers and caches that get released when there is a memory pressure and
thus they are effectively available too.
This change extracts the available memory on Linux from the /proc/meminfo
MemAvailable row, which is reported by the kernel as the most precise
amount of available memory.
Diffstat (limited to 'src/pal')
-rw-r--r-- | src/pal/src/misc/sysinfo.cpp | 83 |
1 files changed, 71 insertions, 12 deletions
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) |