This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix available memory extraction on Linux #26764
Merged
janvorli
merged 4 commits into
dotnet:master
from
janvorli:fix-available-memory-extraction
Sep 24, 2019
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
d18a70f
Fix available memory extraction on Linux
janvorli cbcc870
Fix sscanf format for uint64_t on some platforms
janvorli 6632e71
Fix used memory detection for standalone GC too
janvorli d897f15
Fix standalone GC to correctly recognize restricted memory
janvorli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ Revision History: | |
#include <sched.h> | ||
#include <errno.h> | ||
#include <unistd.h> | ||
#include <inttypes.h> | ||
#include <sys/types.h> | ||
#if HAVE_SYSCTL | ||
#include <sys/sysctl.h> | ||
|
@@ -239,6 +240,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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be >=2 since we need to parse values for 2 fields? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, since the units fields can be missing. So we will either get just the "available" or "available" and "units". |
||
{ | ||
uint64_t multiplier = GetMemorySizeMultiplier(units); | ||
*memAvailable = available * multiplier; | ||
foundMemAvailable = true; | ||
break; | ||
} | ||
} | ||
|
||
free(line); | ||
fclose(memInfoFile); | ||
jkotas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return foundMemAvailable; | ||
} | ||
#endif // __APPLE__ | ||
|
||
/*++ | ||
Function: | ||
GlobalMemoryStatusEx | ||
|
@@ -365,7 +418,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 +513,7 @@ ReadMemoryValueFromFile(const char* filename, uint64_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) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it better to use platform-specific #ifdefs here? This should be built on FreeBSD as well, where it's going to fail during runtime (and when it's time to add support to other OSes, it's easier to have the preprocessor #error on an #else).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that procfs exists on other Unix systems too. And here we just try once and then fall back to using sysconf, so we don't fail.