Use sysconf(_SC_NPROCESSORS_CONF) instead of sysconf(_SC_NPROCESSORS_ONLN) in PAL...
[platform/upstream/coreclr.git] / src / pal / src / misc / sysinfo.cpp
index 3ccb35a..495cc8b 100644 (file)
@@ -32,12 +32,20 @@ Revision History:
 #error Either sysctl or sysconf is required for GetSystemInfo.
 #endif
 
+#if HAVE_SYSINFO
+#include <sys/sysinfo.h>
+#endif
+
 #include <sys/param.h>
 
 #if HAVE_SYS_VMPARAM_H
 #include <sys/vmparam.h>
 #endif  // HAVE_SYS_VMPARAM_H
 
+#if HAVE_XSWDEV
+#include <vm/vm_param.h>
+#endif // HAVE_XSWDEV
+
 #if HAVE_MACH_VM_TYPES_H
 #include <mach/vm_types.h>
 #endif // HAVE_MACH_VM_TYPES_H
@@ -73,6 +81,7 @@ Revision History:
 
 #include "pal/dbgmsg.h"
 
+#include <algorithm>
 
 SET_DEFAULT_DEBUG_CHANNEL(MISC);
 
@@ -87,6 +96,44 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC);
 #endif // __APPLE__
 
 
+DWORD
+PALAPI
+PAL_GetLogicalCpuCountFromOS()
+{
+    int nrcpus = 0;
+
+#if HAVE_SYSCONF
+
+#if defined(_ARM_) || defined(_ARM64_)
+#define SYSCONF_GET_NUMPROCS       _SC_NPROCESSORS_CONF
+#define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_CONF"
+#else
+#define SYSCONF_GET_NUMPROCS       _SC_NPROCESSORS_ONLN
+#define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_ONLN"
+#endif
+    nrcpus = sysconf(SYSCONF_GET_NUMPROCS);
+    if (nrcpus < 1)
+    {
+        ASSERT("sysconf failed for %s (%d)\n", SYSCONF_GET_NUMPROCS_NAME, errno);
+    }
+#elif HAVE_SYSCTL
+    int rc;
+    size_t sz;
+    int mib[2];
+
+    sz = sizeof(nrcpus);
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+    rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
+    if (rc != 0)
+    {
+        ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
+    }
+#endif // HAVE_SYSCONF
+
+    return nrcpus;
+}
+
 /*++
 Function:
   GetSystemInfo
@@ -129,27 +176,7 @@ GetSystemInfo(
     lpSystemInfo->dwPageSize = pagesize;
     lpSystemInfo->dwActiveProcessorMask_PAL_Undefined = 0;
 
-#if HAVE_SYSCONF
-    nrcpus = sysconf(_SC_NPROCESSORS_ONLN);
-    if (nrcpus < 1)
-    {
-        ASSERT("sysconf failed for _SC_NPROCESSORS_ONLN (%d)\n", errno);
-    }
-#elif HAVE_SYSCTL
-    int rc;
-    size_t sz;
-    int mib[2];
-
-    sz = sizeof(nrcpus);
-    mib[0] = CTL_HW;
-    mib[1] = HW_NCPU;
-    rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
-    if (rc != 0)
-    {
-        ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
-    }
-#endif // HAVE_SYSCONF
-
+    nrcpus = PAL_GetLogicalCpuCountFromOS();
     TRACE("dwNumberOfProcessors=%d\n", nrcpus);
     lpSystemInfo->dwNumberOfProcessors = nrcpus;
 
@@ -216,6 +243,8 @@ GlobalMemoryStatusEx(
     lpBuffer->ullAvailExtendedVirtual = 0;
 
     BOOL fRetVal = FALSE;
+    int mib[3];
+    int rc;
 
     // Get the physical memory size
 #if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
@@ -226,7 +255,6 @@ GlobalMemoryStatusEx(
     lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
     fRetVal = TRUE;
 #elif HAVE_SYSCTL
-    int mib[2];
     int64_t physical_memory;
     size_t length;
 
@@ -234,7 +262,7 @@ GlobalMemoryStatusEx(
     mib[0] = CTL_HW;
     mib[1] = HW_MEMSIZE;
     length = sizeof(INT64);
-    int rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
+    rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
     if (rc != 0)
     {
         ASSERT("sysctl failed for HW_MEMSIZE (%d)\n", errno);
@@ -244,11 +272,65 @@ GlobalMemoryStatusEx(
         lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
         fRetVal = TRUE;
     }
-#elif // HAVE_SYSINFO
-    // TODO: implement getting memory details via sysinfo. On Linux, it provides swap file details that
-    // we can use to fill in the xxxPageFile members.
 
-#endif // HAVE_SYSCONF
+#endif // HAVE_SYSCTL
+
+    // Get swap file size, consider the ability to get the values optional
+    // (don't return FALSE from the GlobalMemoryStatusEx)
+#if HAVE_XSW_USAGE
+    // This is available on OSX
+    struct xsw_usage xsu;
+    mib[0] = CTL_VM;
+    mib[1] = VM_SWAPUSAGE;
+    size_t length = sizeof(xsu);
+    rc = sysctl(mib, 2, &xsu, &length, NULL, 0);
+    if (rc == 0)
+    {
+        lpBuffer->ullTotalPageFile = xsu.xsu_total;
+        lpBuffer->ullAvailPageFile = xsu.xsu_avail;
+    }
+#elif HAVE_XSWDEV
+    // E.g. FreeBSD
+    struct xswdev xsw;
+
+    size_t length = 2;
+    rc = sysctlnametomib("vm.swap_info", mib, &length);
+    if (rc == 0)
+    {
+        int pagesize = getpagesize();
+        // Aggregate the information for all swap files on the system
+        for (mib[2] = 0; ; mib[2]++)
+        {
+            length = sizeof(xsw);
+            rc = sysctl(mib, 3, &xsw, &length, NULL, 0);
+            if ((rc < 0) || (xsw.xsw_version != XSWDEV_VERSION))
+            {
+                // All the swap files were processed or coreclr was built against
+                // a version of headers not compatible with the current XSWDEV_VERSION.
+                break;
+            }
+
+            DWORDLONG avail = xsw.xsw_nblks - xsw.xsw_used;
+            lpBuffer->ullTotalPageFile += (DWORDLONG)xsw.xsw_nblks * pagesize;
+            lpBuffer->ullAvailPageFile += (DWORDLONG)avail * pagesize;
+        }
+    }
+#elif HAVE_SYSINFO
+    // Linux
+    struct sysinfo info;
+    rc = sysinfo(&info);
+    if (rc == 0)
+    {
+        lpBuffer->ullTotalPageFile = info.totalswap;
+        lpBuffer->ullAvailPageFile = info.freeswap;
+#if HAVE_SYSINFO_WITH_MEM_UNIT
+        // A newer version of the sysinfo structure represents all the sizes
+        // in mem_unit instead of bytes
+        lpBuffer->ullTotalPageFile *= info.mem_unit;
+        lpBuffer->ullAvailPageFile *= info.mem_unit;
+#endif // HAVE_SYSINFO_WITH_MEM_UNIT
+    }
+#endif // HAVE_SYSINFO
 
     // 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.
@@ -311,17 +393,50 @@ PAL_HasGetCurrentProcessorNumber()
     return HAVE_SCHED_GETCPU;
 }
 
-DWORD
-PALAPI
-PAL_GetLogicalCpuCountFromOS()
+bool
+ReadMemoryValueFromFile(const char* filename, size_t* val)
 {
-    DWORD numLogicalCores = 0;
+    bool result = false;
+    char *line = nullptr;
+    size_t lineLen = 0;
+    char* endptr = nullptr;
+    size_t num = 0, l, multiplier;
 
-#if HAVE_SYSCONF
-    numLogicalCores = sysconf(_SC_NPROCESSORS_ONLN);
-#endif
+    if (val == nullptr)
+        return false;
+
+    FILE* file = fopen(filename, "r");
+    if (file == nullptr)
+        goto done;
+
+    if (getline(&line, &lineLen, file) == -1)
+        goto done;
+
+    errno = 0;
+    num = strtoull(line, &endptr, 0);
+    if (errno != 0)
+        goto done;
 
-    return numLogicalCores;
+    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);
+    free(line);
+    return result;
 }
 
 size_t
@@ -331,16 +446,59 @@ PAL_GetLogicalProcessorCacheSizeFromOS()
     size_t cacheSize = 0;
 
 #ifdef _SC_LEVEL1_DCACHE_SIZE
-    cacheSize = max(cacheSize, sysconf(_SC_LEVEL1_DCACHE_SIZE));
+    cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL1_DCACHE_SIZE));
 #endif
 #ifdef _SC_LEVEL2_CACHE_SIZE
-    cacheSize = max(cacheSize, sysconf(_SC_LEVEL2_CACHE_SIZE));
+    cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL2_CACHE_SIZE));
 #endif
 #ifdef _SC_LEVEL3_CACHE_SIZE
-    cacheSize = max(cacheSize, sysconf(_SC_LEVEL3_CACHE_SIZE));
+    cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL3_CACHE_SIZE));
 #endif
 #ifdef _SC_LEVEL4_CACHE_SIZE
-    cacheSize = max(cacheSize, sysconf(_SC_LEVEL4_CACHE_SIZE));
+    cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL4_CACHE_SIZE));
+#endif
+
+#if defined(_ARM64_)
+    if(cacheSize == 0)
+    {
+        size_t size;
+
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index0/size", &size))
+            cacheSize = std::max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index1/size", &size))
+            cacheSize = std::max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index2/size", &size))
+            cacheSize = std::max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index3/size", &size))
+            cacheSize = std::max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index4/size", &size))
+            cacheSize = std::max(cacheSize, size);
+    }
+
+    if(cacheSize == 0)
+    {
+        // It is currently expected to be missing cache size info
+        //
+        // _SC_LEVEL*_*CACHE_SIZE is not yet present.  Work is in progress to enable this for arm64
+        //
+        // /sys/devices/system/cpu/cpu*/cache/index*/ is also not yet present in most systems.
+        // Arm64 patch is in Linux kernel tip.
+        //
+        // midr_el1 is available in "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1",
+        // but without an exhaustive list of ARM64 processors any decode of midr_el1
+        // Would likely be incomplete
+
+        // Published information on ARM64 architectures is limited.
+        // If we use recent high core count chips as a guide for state of the art, we find
+        // total L3 cache to be 1-2MB/core.  As always, there are exceptions.
+
+        // Estimate cache size based on CPU count
+        // Assume lower core count are lighter weight parts which are likely to have smaller caches
+        // Assume L3$/CPU grows linearly from 256K to 1.5M/CPU as logicalCPUs grows from 2 to 12 CPUs
+        DWORD logicalCPUs = PAL_GetLogicalCpuCountFromOS();
+
+        cacheSize = logicalCPUs*std::min(1536, std::max(256, (int)logicalCPUs*128))*1024;
+    }
 #endif
 
     return cacheSize;