summaryrefslogtreecommitdiff
path: root/src/pal
diff options
context:
space:
mode:
authoragoretsky <agoretsky15@gmail.com>2019-10-14 20:03:12 +0300
committerWilliam Godbe <wigodbe@microsoft.com>2019-10-14 10:03:12 -0700
commitff243be899fae73904a1bf5401ecb93b6e80d85c (patch)
tree5dce2eb3115b4972ca9fa8ced3efa72aaf745fba /src/pal
parente38f3722a98ea66b805f388cd613ac587a44d7d2 (diff)
downloadcoreclr-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.cpp83
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)