diff options
Diffstat (limited to 'src/vm/gcenv.os.cpp')
-rw-r--r-- | src/vm/gcenv.os.cpp | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp index 051508eed3..95550728de 100644 --- a/src/vm/gcenv.os.cpp +++ b/src/vm/gcenv.os.cpp @@ -14,6 +14,12 @@ #include "common.h" #include "gcenv.h" +#ifndef FEATURE_PAL +#include <Psapi.h> +#endif + +#define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0)) + // Initialize the interface implementation // Return: // true if it has succeeded, false if it has failed @@ -334,6 +340,174 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount() return ::GetCurrentProcessCpuCount(); } +#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; + +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); +#endif + +#if defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) +// For coresys we need to look for an API in some apiset dll on win8 if we can't find it +// in the traditional dll. +HINSTANCE LoadDllForAPI(WCHAR* dllTraditional, WCHAR* dllApiSet) +{ + HINSTANCE hinst = WszLoadLibrary(dllTraditional); + + if (!hinst) + { + if(RunningOnWin8()) + hinst = WszLoadLibrary(dllApiSet); + } + + return hinst; +} +#endif + +// If the process's memory is restricted (ie, beyond what's available on the machine), return that limit. +// Return: +// 0 if it has failed for some reason, the real value if it has succeeded +// Remarks: +// If a process runs with a restricted memory limit, and we are successful at getting +// that limit, it returns the limit. If there's no limit specified, or there's an error +// at getting that limit, it returns 0. +uint64_t GCToOSInterface::GetRestrictedPhysicalMemoryLimit() +{ +#ifdef FEATURE_PAL + return 0; +#else + size_t job_physical_memory_limit = (size_t)MAX_PTR; + BOOL in_job_p = FALSE; +#ifdef FEATURE_CORECLR + HINSTANCE hinstApiSetPsapiOrKernel32 = 0; + // these 2 modules will need to be freed no matter what as we only use them locally in this method. + HINSTANCE hinstApiSetJob1OrKernel32 = 0; + HINSTANCE hinstApiSetJob2OrKernel32 = 0; +#else + HINSTANCE hinstPsapi = 0; +#endif + + PIS_PROCESS_IN_JOB GCIsProcessInJob = 0; + PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0; + +#ifdef FEATURE_CORECLR + hinstApiSetJob1OrKernel32 = LoadDllForAPI(L"kernel32.dll", L"api-ms-win-core-job-l1-1-0.dll"); + if (!hinstApiSetJob1OrKernel32) + goto exit; + + GCIsProcessInJob = (PIS_PROCESS_IN_JOB)GetProcAddress(hinstApiSetJob1OrKernel32, "IsProcessInJob"); + if (!GCIsProcessInJob) + goto exit; +#else + GCIsProcessInJob = &(::IsProcessInJob); +#endif + + if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) + goto exit; + + if (in_job_p) + { +#ifdef FEATURE_CORECLR + hinstApiSetPsapiOrKernel32 = LoadDllForAPI(L"kernel32.dll", L"api-ms-win-core-psapi-l1-1-0"); + if (!hinstApiSetPsapiOrKernel32) + goto exit; + + GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstApiSetPsapiOrKernel32, "K32GetProcessMemoryInfo"); +#else + // We need a way to get the working set in a job object and GetProcessMemoryInfo + // is the way to get that. According to MSDN, we should use GetProcessMemoryInfo In order to + // compensate for the incompatibility that psapi.dll introduced we are getting this dynamically. + hinstPsapi = WszLoadLibrary(L"psapi.dll"); + if (!hinstPsapi) + return 0; + GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstPsapi, "GetProcessMemoryInfo"); +#endif + + if (!GCGetProcessMemoryInfo) + goto exit; + +#ifdef FEATURE_CORECLR + hinstApiSetJob2OrKernel32 = LoadDllForAPI(L"kernel32.dll", L"api-ms-win-core-job-l2-1-0"); + if (!hinstApiSetJob2OrKernel32) + goto exit; + + GCQueryInformationJobObject = (PQUERY_INFORMATION_JOB_OBJECT)GetProcAddress(hinstApiSetJob2OrKernel32, "QueryInformationJobObject"); +#else + GCQueryInformationJobObject = &(::QueryInformationJobObject); +#endif + + if (!GCQueryInformationJobObject) + goto exit; + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; + if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, + sizeof(limit_info), NULL)) + { + size_t job_memory_limit = (size_t)MAX_PTR; + size_t job_process_memory_limit = (size_t)MAX_PTR; + size_t job_workingset_limit = (size_t)MAX_PTR; + + // Notes on the NT job object: + // + // You can specific a bigger process commit or working set limit than + // job limit which is pointless so we use the smallest of all 3 as + // to calculate our "physical memory load" or "available physical memory" + // when running inside a job object, ie, we treat this as the amount of physical memory + // our process is allowed to use. + // + // The commit limit is already reflected by default when you run in a + // job but the physical memory load is not. + // + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0) + job_memory_limit = limit_info.JobMemoryLimit; + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0) + job_process_memory_limit = limit_info.ProcessMemoryLimit; + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0) + job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize; + + job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit); + job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit); + } + } + +exit: +#ifdef FEATURE_CORECLR + if (hinstApiSetJob1OrKernel32) + FreeLibrary(hinstApiSetJob1OrKernel32); + if (hinstApiSetJob2OrKernel32) + FreeLibrary(hinstApiSetJob2OrKernel32); +#endif + + if (job_physical_memory_limit == (size_t)MAX_PTR) + { + job_physical_memory_limit = 0; + +#ifdef FEATURE_CORECLR + FreeLibrary(hinstApiSetPsapiOrKernel32); +#else + FreeLibrary(hinstPsapi); +#endif + } + + return job_physical_memory_limit; +#endif +} + +// Get the current physical memory this process is using. +// Return: +// 0 if it has failed, the real value if it has succeeded +size_t GCToOSInterface::GetCurrentPhysicalMemory() +{ +#ifndef FEATURE_PAL + PROCESS_MEMORY_COUNTERS pmc; + if (GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + return pmc.WorkingSetSize; +#endif + + return 0; +} + // Get global memory status // Parameters: // ms - pointer to the structure that will be filled in with the memory status |