From 11d92a7ec9aeb8425058fe8a2dc0f977f6a9221f Mon Sep 17 00:00:00 2001 From: rahul Date: Wed, 8 Mar 2017 18:55:24 -0800 Subject: [PATCH] memory limit in containers on linux --- src/gc/unix/CMakeLists.txt | 3 +- src/gc/unix/cgroup.cpp | 271 ++++++++++++++++++ src/gc/unix/gcenv.unix.cpp | 27 +- src/pal/inc/pal.h | 11 +- src/pal/src/CMakeLists.txt | 1 + src/pal/src/misc/cgroup.cpp | 268 +++++++++++++++++ .../miscellaneous/CGroup/CMakeLists.txt | 4 + .../miscellaneous/CGroup/test1/CMakeLists.txt | 17 ++ .../miscellaneous/CGroup/test1/test.cpp | 52 ++++ .../miscellaneous/CGroup/test1/testinfo.dat | 12 + .../palsuite/miscellaneous/CMakeLists.txt | 1 + src/vm/gcenv.os.cpp | 37 ++- 12 files changed, 690 insertions(+), 14 deletions(-) create mode 100644 src/gc/unix/cgroup.cpp create mode 100644 src/pal/src/misc/cgroup.cpp create mode 100644 src/pal/tests/palsuite/miscellaneous/CGroup/CMakeLists.txt create mode 100644 src/pal/tests/palsuite/miscellaneous/CGroup/test1/CMakeLists.txt create mode 100644 src/pal/tests/palsuite/miscellaneous/CGroup/test1/test.cpp create mode 100644 src/pal/tests/palsuite/miscellaneous/CGroup/test1/testinfo.dat diff --git a/src/gc/unix/CMakeLists.txt b/src/gc/unix/CMakeLists.txt index ef66abf32ab7..3e1aa5ad19ac 100644 --- a/src/gc/unix/CMakeLists.txt +++ b/src/gc/unix/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories("../env") include(configure.cmake) set(GC_PAL_SOURCES - gcenv.unix.cpp) + gcenv.unix.cpp + cgroup.cpp) add_library(gc_unix STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH}) diff --git a/src/gc/unix/cgroup.cpp b/src/gc/unix/cgroup.cpp new file mode 100644 index 000000000000..e073f9770316 --- /dev/null +++ b/src/gc/unix/cgroup.cpp @@ -0,0 +1,271 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + +Module Name: + + cgroup.cpp + +Abstract: + Read memory limits for the current process +--*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PATH 1024 +#define SIZE_T_MAX (~(size_t)0) +#define PROC_MOUNTINFO_FILENAME "/proc/self/mountinfo" +#define PROC_CGROUP_FILENAME "/proc/self/cgroup" +#define PROC_STATM_FILENAME "/proc/self/statm" +#define MEM_LIMIT_FILENAME "/memory.limit_in_bytes" +#define MEM_SWAP_LIMIT_FILENAME "/memory.memsw.limit_in_bytes" + +class CGroup +{ + char _memory_cgroup_path[MAX_PATH]; +public: + CGroup() + { + if (!FindMemoryHierarchyMount(_memory_cgroup_path)) + { + _memory_cgroup_path[0] = '\0'; + return; + } + char cgroup_path_relative_to_mount[MAX_PATH]; + if (!FindCGroupPathForMemorySubsystem(cgroup_path_relative_to_mount)) + { + _memory_cgroup_path[0] = '\0'; + return; + } + strcat(_memory_cgroup_path, cgroup_path_relative_to_mount); + } + + bool GetPhysicalMemoryLimit(size_t *val) + { + char mem_limit_filename[MAX_PATH]; + + if (_memory_cgroup_path[0] == 0) + return false; + + strcpy(mem_limit_filename, _memory_cgroup_path); + strcat(mem_limit_filename, MEM_LIMIT_FILENAME); + return ReadMemoryValueFromFile(mem_limit_filename, val); + } + + bool GetSwapMemoryLimit(size_t *val) + { + char memsw_limit_filename[MAX_PATH]; + + if (_memory_cgroup_path[0] == 0) + return false; + + strcpy(memsw_limit_filename, _memory_cgroup_path); + strcat(memsw_limit_filename, MEM_SWAP_LIMIT_FILENAME); + return ReadMemoryValueFromFile(memsw_limit_filename, val); + } +private: + bool FindMemoryHierarchyMount(char* mountpath) + { + bool retval = false; + char *line = nullptr; + size_t lineLen = 0; + FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r"); + if (mountinfofile == NULL) + goto done; + + while (!retval && getline(&line, &lineLen, mountinfofile) != -1) + { + char filesystemType[100]; + char options[MAX_PATH]; + + char* separatorChar = strchr(line, '-'); + + // See man page of proc to get format for /proc/self/mountinfo file + int sscanfRet = sscanf(separatorChar, + "- %s %*s %s", + filesystemType, + options); + if (sscanfRet != 2) + { + assert(!"Failed to parse mount info file contents with sscanf."); + goto done; + } + + if (strncmp(filesystemType, "cgroup", 6) == 0) + { + char* context = NULL; + char* strTok = strtok_r(options, ",", &context); + while (strTok != NULL) + { + if (strncmp("memory", strTok, 6) == 0) + { + sscanfRet = sscanf(line, + "%*d %*d %*s %*s %s ", + mountpath); + if (sscanfRet != 1) + { + assert(!"Failed to parse mount info file contents with sscanf."); + goto done; + } + retval = true; + break; + } + strTok = strtok_r(NULL, ",", &context); + } + } + } + done: + if (line) + free(line); + if (mountinfofile) + fclose(mountinfofile); + return retval; + } + + bool FindCGroupPathForMemorySubsystem(char* cgrouppath) + { + bool retval = false; + char *line = nullptr; + size_t lineLen = 0; + FILE *cgroupfile = fopen(PROC_CGROUP_FILENAME, "r"); + if (cgroupfile == NULL) + goto done; + + while (!retval && getline(&line, &lineLen, cgroupfile) != -1) + { + char subsystem_list[100]; + char cgroup_path[MAX_PATH]; + + // See man page of proc to get format for /proc/self/cgroup file + int sscanfRet = sscanf(line, + "%*[^:]:%[^:]:%s", + subsystem_list, + cgroup_path); + if (sscanfRet != 2) + { + assert(!"Failed to parse cgroup info file contents with sscanf."); + goto done; + } + + char* context = NULL; + char* strTok = strtok_r(subsystem_list, ",", &context); + while (strTok != NULL) + { + if (strncmp("memory", strTok, 6) == 0) + { + strcpy(cgrouppath, cgroup_path); + retval = true; + break; + } + strTok = strtok_r(NULL, ",", &context); + } + } + done: + if (line) + free(line); + if (cgroupfile) + fclose(cgroupfile); + return retval; + } + + bool ReadMemoryValueFromFile(const char* filename, size_t* val) + { + bool result = false; + char *line = nullptr; + size_t lineLen = 0; + char* endptr = NULL; + size_t num = 0, l, multiplier; + FILE* file = NULL; + + if (val == NULL) + goto done; + + file = fopen(filename, "r"); + if (file == NULL) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + errno = 0; + num = strtoull(line, &endptr, 0); + 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; + } + + *val = num * multiplier; + result = true; + if (*val/multiplier != num) + result = false; + done: + if (file) + fclose(file); + if (line) + free(line); + return result; + } +}; + +size_t GetRestrictedPhysicalMemoryLimit() +{ + CGroup cgroup; + size_t result; + size_t physical_memory_limit; + size_t swap_memory_limit; + + if (!cgroup.GetPhysicalMemoryLimit(&physical_memory_limit)) + physical_memory_limit = SIZE_T_MAX; + if (cgroup.GetSwapMemoryLimit(&swap_memory_limit)) + { + assert(swap_memory_limit >= physical_memory_limit); + result = swap_memory_limit; + } + else + result = physical_memory_limit; + struct rlimit curr_rlimit; + size_t rlimit_soft_limit = RLIM_INFINITY; + if (getrlimit(RLIMIT_AS, &curr_rlimit) == 0) + { + rlimit_soft_limit = curr_rlimit.rlim_cur; + } + result = (result < rlimit_soft_limit) ? result : rlimit_soft_limit; + return result; +} + +bool GetWorkingSetSize(size_t* val) +{ + bool result = false; + size_t linelen; + char* line = NULL; + + FILE* file = fopen(PROC_STATM_FILENAME, "r"); + if (file != NULL && getline(&line, &linelen, file) != -1) + { + if (sscanf(line, "%*s %zu %*s %*s %*s %*s %*s", val) == 1) + result = true; + } + + if (file) + fclose(file); + if (line) + free(line); + return result; +} diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp index 34a45b3cc11a..2898cefa3cd5 100644 --- a/src/gc/unix/gcenv.unix.cpp +++ b/src/gc/unix/gcenv.unix.cpp @@ -78,6 +78,23 @@ static uint8_t g_helperPage[OS_PAGE_SIZE] __attribute__((aligned(OS_PAGE_SIZE))) // Mutex to make the FlushProcessWriteBuffersMutex thread safe static pthread_mutex_t g_flushProcessWriteBuffersMutex; +size_t GetRestrictedPhysicalMemoryLimit(); +bool GetWorkingSetSize(size_t* val); + +static size_t g_RestrictedPhysicalMemoryLimit = 0; + +static size_t GetRestrictedPhysicalMemoryLimit() +{ + // The limit was cached already + if (g_RestrictedPhysicalMemoryLimit != 0) + return g_RestrictedPhysicalMemoryLimit; + + size_t memory_limit = GetRestrictedPhysicalMemoryLimit(); + + VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit); + return g_RestrictedPhysicalMemoryLimit; +} + // Initialize the interface implementation // Return: // true if it has succeeded, false if it has failed @@ -442,6 +459,10 @@ size_t GCToOSInterface::GetVirtualMemoryLimit() // specified, it returns amount of actual physical memory. uint64_t GCToOSInterface::GetPhysicalMemoryLimit() { + size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); + if (restricted_limit != 0 && restricted_limit != SIZE_T_MAX) + return restricted_limit; + long pages = sysconf(_SC_PHYS_PAGES); if (pages == -1) { @@ -471,13 +492,13 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available 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) + if (total > 0 && GetWorkingSetSize(&used)) { - available = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); - uint64_t used = total - available; + available = total - used; load = (uint32_t)((used * 100) / total); } diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 8d099aff0f0e..1d74358aeeb9 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -538,7 +538,6 @@ PAL_ProbeMemory( BOOL fWriteAccess); /******************* winuser.h Entrypoints *******************************/ - PALIMPORT LPSTR PALAPI @@ -2533,6 +2532,16 @@ DWORD PALAPI PAL_GetLogicalCpuCountFromOS(VOID); +PALIMPORT +size_t +PALAPI +PAL_GetRestrictedPhysicalMemoryLimit(VOID); + +PALIMPORT +BOOL +PALAPI +PAL_GetWorkingSetSize(size_t* val); + PALIMPORT size_t PALAPI diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt index 5142dcc18bce..f21c9dbf8f81 100644 --- a/src/pal/src/CMakeLists.txt +++ b/src/pal/src/CMakeLists.txt @@ -168,6 +168,7 @@ set(SOURCES map/virtual.cpp memory/heap.cpp memory/local.cpp + misc/cgroup.cpp misc/dbgmsg.cpp misc/environ.cpp misc/error.cpp diff --git a/src/pal/src/misc/cgroup.cpp b/src/pal/src/misc/cgroup.cpp new file mode 100644 index 000000000000..0e420a73c954 --- /dev/null +++ b/src/pal/src/misc/cgroup.cpp @@ -0,0 +1,268 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + +Module Name: + + cgroup.cpp + +Abstract: + Read memory limits for the current process +--*/ + +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(MISC); +#include "pal/palinternal.h" +#include + +#define PROC_MOUNTINFO_FILENAME "/proc/self/mountinfo" +#define PROC_CGROUP_FILENAME "/proc/self/cgroup" +#define PROC_STATM_FILENAME "/proc/self/statm" +#define MEM_LIMIT_FILENAME "/memory.limit_in_bytes" +#define MEM_SWAP_LIMIT_FILENAME "/memory.memsw.limit_in_bytes" + +class CGroup +{ + char m_memory_cgroup_path[MAX_PATH]; +public: + CGroup() + { + if (!FindMemoryHierarchyMount(m_memory_cgroup_path, MAX_PATH)) + { + m_memory_cgroup_path[0] = '\0'; + return; + } + char cgroup_path_relative_to_mount[MAX_PATH]; + if (!FindCGroupPathForMemorySubsystem(cgroup_path_relative_to_mount, MAX_PATH)) + { + m_memory_cgroup_path[0] = '\0'; + return; + } + strcat_s(m_memory_cgroup_path, MAX_PATH, cgroup_path_relative_to_mount); + } + + bool GetPhysicalMemoryLimit(size_t *val) + { + char mem_limit_filename[MAX_PATH]; + + if (m_memory_cgroup_path[0] == 0) + return false; + + strcpy_s(mem_limit_filename, MAX_PATH, m_memory_cgroup_path); + strcat_s(mem_limit_filename, MAX_PATH, MEM_LIMIT_FILENAME); + return ReadMemoryValueFromFile(mem_limit_filename, val); + } + bool GetSwapMemoryLimit(size_t *val) + { + char memsw_limit_filename[MAX_PATH]; + + if (m_memory_cgroup_path[0] == 0) + return false; + + strcpy_s(memsw_limit_filename, MAX_PATH, m_memory_cgroup_path); + strcat_s(memsw_limit_filename, MAX_PATH, MEM_SWAP_LIMIT_FILENAME); + return ReadMemoryValueFromFile(memsw_limit_filename, val); + } +private: + bool FindMemoryHierarchyMount(char* mountpath, size_t mountpathsize) + { + bool retval = false; + char *line = nullptr; + size_t lineLen = 0; + FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r"); + if (mountinfofile == NULL) + goto done; + + while (!retval && getline(&line, &lineLen, mountinfofile) != -1) + { + char filesystemType[100]; + char options[MAX_PATH]; + + char* separatorChar = strchr(line, '-'); + + // See man page of proc to get format for /proc/self/mountinfo file + int sscanfRet = sscanf_s(separatorChar, + "- %s %*s %s", + filesystemType, (unsigned)_countof(filesystemType), + options, (unsigned)_countof(options)); + if (sscanfRet != 2) + { + _ASSERTE(!"Failed to parse mount info file contents with sscanf_s."); + goto done; + } + + if (strncmp(filesystemType, "cgroup", 6) == 0) + { + char* context = NULL; + char* strTok = strtok_s(options, ",", &context); + while (strTok != NULL) + { + if (strncmp("memory", strTok, 6) == 0) + { + sscanfRet = sscanf_s(line, + "%*d %*d %*s %*s %s ", + mountpath, mountpathsize); + if (sscanfRet != 1) + { + _ASSERTE(!"Failed to parse mount info file contents with sscanf_s."); + goto done; + } + retval = true; + break; + } + strTok = strtok_s(NULL, ",", &context); + } + } + } + done: + if (line) + free(line); + if (mountinfofile) + fclose(mountinfofile); + return retval; + } + + bool FindCGroupPathForMemorySubsystem(char* cgrouppath, size_t cgrouppathsize) + { + bool retval = false; + char *line = nullptr; + size_t lineLen = 0; + FILE *cgroupfile = fopen(PROC_CGROUP_FILENAME, "r"); + if (cgroupfile == NULL) + goto done; + + while (!retval && getline(&line, &lineLen, cgroupfile) != -1) + { + char subsystem_list[100]; + char cgroup_path[MAX_PATH]; + + // See man page of proc to get format for /proc/self/cgroup file + int sscanfRet = sscanf_s(line, + "%*[^:]:%[^:]:%s", + subsystem_list, (unsigned)_countof(subsystem_list), + cgroup_path, (unsigned)_countof(cgroup_path)); + if (sscanfRet != 2) + { + _ASSERTE(!"Failed to parse cgroup info file contents with sscanf_s."); + goto done; + } + + char* context = NULL; + char* strTok = strtok_s(subsystem_list, ",", &context); + while (strTok != NULL) + { + if (strncmp("memory", strTok, 6) == 0) + { + strcpy_s(cgrouppath, cgrouppathsize, cgroup_path); + retval = true; + break; + } + strTok = strtok_s(NULL, ",", &context); + } + } + done: + if (line) + free(line); + if (cgroupfile) + fclose(cgroupfile); + return retval; + } + + bool ReadMemoryValueFromFile(const char* filename, size_t* val) + { + bool result = false; + char *line = nullptr; + size_t lineLen = 0; + char* endptr = NULL; + size_t num = 0, l, multiplier; + + FILE* file = fopen(filename, "r"); + if (file == NULL) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + errno = 0; + num = strtoull(line, &endptr, 0); + 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; + } + + *val = num * multiplier; + result = true; + if (*val/multiplier != num) + result = false; + done: + if (file) + fclose(file); + if (line) + free(line); + return result; + } +}; + +size_t +PALAPI +PAL_GetRestrictedPhysicalMemoryLimit() +{ + CGroup cgroup; + size_t result; + size_t physical_memory_limit; + size_t swap_memory_limit; + + if (!cgroup.GetPhysicalMemoryLimit(&physical_memory_limit)) + physical_memory_limit = SIZE_T_MAX; + if (cgroup.GetSwapMemoryLimit(&swap_memory_limit)) + { + _ASSERTE(swap_memory_limit >= physical_memory_limit); + result = swap_memory_limit; + } + else + result = physical_memory_limit; + struct rlimit curr_rlimit; + size_t rlimit_soft_limit = (size_t)RLIM_INFINITY; + if (getrlimit(RLIMIT_AS, &curr_rlimit) == 0) + { + rlimit_soft_limit = curr_rlimit.rlim_cur; + } + result = min(result, rlimit_soft_limit); + + if(result == SIZE_T_MAX) + result = 0; + return result; +} + +BOOL +PALAPI +PAL_GetWorkingSetSize(size_t* val) +{ + BOOL result = false; + size_t linelen; + char* line = NULL; + + FILE* file = fopen(PROC_STATM_FILENAME, "r"); + if (file != NULL && getline(&line, &linelen, file) != -1) + { + if (sscanf_s(line, "%*s %zu %*s %*s %*s %*s %*s", val) == 1) + result = true; + } + + if (file) + fclose(file); + if (line) + free(line); + return result; +} diff --git a/src/pal/tests/palsuite/miscellaneous/CGroup/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/CGroup/CMakeLists.txt new file mode 100644 index 000000000000..f6aa0cb2d9da --- /dev/null +++ b/src/pal/tests/palsuite/miscellaneous/CGroup/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 2.8.12.2) + +add_subdirectory(test1) + diff --git a/src/pal/tests/palsuite/miscellaneous/CGroup/test1/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/CMakeLists.txt new file mode 100644 index 000000000000..cdd7fa99d0c9 --- /dev/null +++ b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8.12.2) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(SOURCES + test.cpp +) + +add_executable(paltest_cgroup_test1 + ${SOURCES} +) + +add_dependencies(paltest_cgroup_test1 coreclrpal) + +target_link_libraries(paltest_cgroup_test1 + ${COMMON_TEST_LIBRARIES} +) diff --git a/src/pal/tests/palsuite/miscellaneous/CGroup/test1/test.cpp b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/test.cpp new file mode 100644 index 000000000000..26d54f77b35d --- /dev/null +++ b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/test.cpp @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Source: test.c +** +** Purpose: Test for CGroup +** +** +** Steps to run this test on ubuntu: +** 1. sudo apt-get install cgroup-bin +** 2. sudo vi /etc/default/grub +** Add cgroup_enable=memory to GRUB_CMDLINE_LINUX_DEFAULT +** 3. sudo update-grub +** 4. reboot +** 5. sudo cgcreate -g cpu,memory:/mygroup -a : -t : +** 6. echo 4M > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes +** 7. cgexe -g memory:/mygroup --sticky +**=========================================================*/ + +#include + +int __cdecl main(int argc,char *argv[]) +{ + + /* + * Initialize the PAL and return FAILURE if this fails + */ + + if(0 != (PAL_Initialize(argc, argv))) + { + return FAIL; + } + + size_t mem_limit = PAL_GetRestrictedPhysicalMemoryLimit(); + + FILE* file = fopen("/sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes", "r"); + if(file != NULL) + { + if(mem_limit != 4194304) + Fail("Memory limit obtained from PAL_GetRestrictedPhysicalMemory is not 4MB\n"); + fclose(file); + } + + PAL_Terminate(); + return PASS; +} + + + diff --git a/src/pal/tests/palsuite/miscellaneous/CGroup/test1/testinfo.dat b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/testinfo.dat new file mode 100644 index 000000000000..86da2d151507 --- /dev/null +++ b/src/pal/tests/palsuite/miscellaneous/CGroup/test1/testinfo.dat @@ -0,0 +1,12 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the MIT license. +# See the LICENSE file in the project root for more information. + +Version = 1.0 +Section = Miscellaneous +Function = CGroup +Name = Positive Test for CGroup +TYPE = DEFAULT +EXE1 = test +Description += Test to see if Cgroup memory limit works properly diff --git a/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt index 0fd4df8ad54e..0b5fd37bbb56 100644 --- a/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt +++ b/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 2.8.12.2) +add_subdirectory(CGroup) add_subdirectory(CharNextA) add_subdirectory(CharNextExA) add_subdirectory(CloseHandle) diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp index 0cbe01644596..5ed987fe158a 100644 --- a/src/vm/gcenv.os.cpp +++ b/src/vm/gcenv.os.cpp @@ -360,13 +360,13 @@ size_t GCToOSInterface::GetVirtualMemoryLimit() } +static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR; + #ifndef FEATURE_PAL typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb); static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0; -static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR; - typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result); typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength); @@ -455,6 +455,21 @@ static size_t GetRestrictedPhysicalMemoryLimit() return g_RestrictedPhysicalMemoryLimit; } +#else + +static size_t GetRestrictedPhysicalMemoryLimit() +{ + LIMITED_METHOD_CONTRACT; + + // The limit was cached already + if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR) + return g_RestrictedPhysicalMemoryLimit; + + size_t memory_limit = PAL_GetRestrictedPhysicalMemoryLimit(); + + VolatileStore(&g_RestrictedPhysicalMemoryLimit, memory_limit); + return g_RestrictedPhysicalMemoryLimit; +} #endif // FEATURE_PAL @@ -465,11 +480,9 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit() { LIMITED_METHOD_CONTRACT; -#ifndef FEATURE_PAL size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) return restricted_limit; -#endif MEMORYSTATUSEX memStatus; ::GetProcessMemoryLoad(&memStatus); @@ -489,17 +502,24 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available { LIMITED_METHOD_CONTRACT; -#ifndef FEATURE_PAL uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) { + size_t workingSetSize; + BOOL status = FALSE; +#ifndef FEATURE_PAL PROCESS_MEMORY_COUNTERS pmc; - if (GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + workingSetSize = pmc.WorkingSetSize; +#else + status = PAL_GetWorkingSetSize(&workingSetSize); +#endif + if(status) { if (memory_load) - *memory_load = (uint32_t)((float)pmc.WorkingSetSize * 100.0 / (float)restricted_limit); + *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit); if (available_physical) - *available_physical = restricted_limit - pmc.WorkingSetSize; + *available_physical = restricted_limit - workingSetSize; // Available page file doesn't mean much when physical memory is restricted since // we don't know how much of it is available to this process so we are not going to // bother to make another OS call for it. @@ -509,7 +529,6 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available return; } } -#endif MEMORYSTATUSEX ms; ::GetProcessMemoryLoad(&ms);