Add reporting swap file size to GlobalMemoryStatusEx on Unix (#10700)
authorJan Vorlicek <janvorli@microsoft.com>
Fri, 7 Apr 2017 13:35:34 +0000 (15:35 +0200)
committerGitHub <noreply@github.com>
Fri, 7 Apr 2017 13:35:34 +0000 (15:35 +0200)
The swap file size reporting is added using the Linux, OSX and FreeBSD
means. Also a new PAL test was added to exercise the function.

src/pal/src/config.h.in
src/pal/src/configure.cmake
src/pal/src/misc/sysinfo.cpp
src/pal/tests/palsuite/miscellaneous/CMakeLists.txt
src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/test.cpp [new file with mode: 0644]
src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/testinfo.dat [new file with mode: 0644]
src/pal/tests/palsuite/paltestlist.txt
src/pal/tests/palsuite/palverify.dat

index 77d7bfa..ab5fa03 100644 (file)
@@ -40,6 +40,7 @@
 #cmakedefine01 HAVE_UTIMES
 #cmakedefine01 HAVE_SYSCTL
 #cmakedefine01 HAVE_SYSCONF
+#cmakedefine01 HAVE_SYSINFO
 #cmakedefine01 HAVE_LOCALTIME_R
 #cmakedefine01 HAVE_GMTIME_R
 #cmakedefine01 HAVE_TIMEGM
@@ -57,6 +58,8 @@
 #cmakedefine01 HAVE_TTRACE
 #cmakedefine HAVE_UNW_GET_SAVE_LOC
 #cmakedefine HAVE_UNW_GET_ACCESSORS
+#cmakedefine01 HAVE_XSWDEV
+#cmakedefine01 HAVE_XSW_USAGE
 
 #cmakedefine01 HAVE_STAT_TIMESPEC
 #cmakedefine01 HAVE_STAT_NSEC
index 4ca1fe9..4d78f54 100644 (file)
@@ -78,6 +78,7 @@ check_function_exists(fsync HAVE_FSYNC)
 check_function_exists(futimes HAVE_FUTIMES)
 check_function_exists(utimes HAVE_UTIMES)
 check_function_exists(sysctl HAVE_SYSCTL)
+check_function_exists(sysinfo HAVE_SYSINFO)
 check_function_exists(sysconf HAVE_SYSCONF)
 check_function_exists(localtime_r HAVE_LOCALTIME_R)
 check_function_exists(gmtime_r HAVE_GMTIME_R)
@@ -122,6 +123,7 @@ check_struct_has_member ("struct stat" st_atimensec "sys/types.h;sys/stat.h" HAV
 check_struct_has_member ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
 check_struct_has_member ("ucontext_t" uc_mcontext.gregs[0] ucontext.h HAVE_GREGSET_T)
 check_struct_has_member ("ucontext_t" uc_mcontext.__gregs[0] ucontext.h HAVE___GREGSET_T)
+check_struct_has_member ("struct sysinfo" mem_unit "sys/sysinfo.h" HAVE_SYSINFO_WITH_MEM_UNIT)
 
 set(CMAKE_EXTRA_INCLUDE_FILES machine/reg.h)
 check_type_size("struct reg" BSD_REGS_T)
@@ -982,6 +984,29 @@ int main(int argc, char **argv)
         return 0;
 }" UNWIND_CONTEXT_IS_UCONTEXT_T)
 
+check_cxx_source_compiles("
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <vm/vm_param.h>
+
+int main(int argc, char **argv)
+{
+    struct xswdev xsw;
+
+    return 0;
+}" HAVE_XSWDEV)
+
+check_cxx_source_compiles("
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+int main(int argc, char **argv)
+{
+    struct xsw_usage xsu;
+
+    return 0;
+}" HAVE_XSW_USAGE)
+
 set(CMAKE_REQUIRED_LIBRARIES pthread)
 check_cxx_source_compiles("
 #include <errno.h>
index 3ccb35a..fff0518 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
@@ -216,6 +224,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 +236,6 @@ GlobalMemoryStatusEx(
     lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
     fRetVal = TRUE;
 #elif HAVE_SYSCTL
-    int mib[2];
     int64_t physical_memory;
     size_t length;
 
@@ -234,7 +243,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 +253,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.
index 0b5fd37..9352ede 100644 (file)
@@ -15,6 +15,7 @@ add_subdirectory(GetEnvironmentVariableA)
 add_subdirectory(GetEnvironmentVariableW)
 add_subdirectory(GetLastError)
 add_subdirectory(GetSystemInfo)
+add_subdirectory(GlobalMemoryStatusEx)
 add_subdirectory(GetTickCount)
 add_subdirectory(InterlockedBit)
 add_subdirectory(InterlockedCompareExchange)
diff --git a/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f6aa0cb
--- /dev/null
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+
diff --git a/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e74f22
--- /dev/null
@@ -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_globalmemorystatusex_test1
+  ${SOURCES}
+)
+
+add_dependencies(paltest_globalmemorystatusex_test1 coreclrpal)
+
+target_link_libraries(paltest_globalmemorystatusex_test1
+  ${COMMON_TEST_LIBRARIES}
+)
diff --git a/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/test.cpp b/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/test.cpp
new file mode 100644 (file)
index 0000000..460edad
--- /dev/null
@@ -0,0 +1,55 @@
+// 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 GlobalMemoryStatusEx() function
+**
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+int __cdecl main(int argc, char *argv[]) {
+  
+    MEMORYSTATUSEX memoryStatus;
+
+    /*
+     * Initialize the PAL and return FAILURE if this fails
+     */
+
+    if(0 != (PAL_Initialize(argc, argv)))
+    {
+        return FAIL;
+    }
+
+    if (!GlobalMemoryStatusEx(&memoryStatus))
+    {
+        Fail("ERROR: GlobalMemoryStatusEx failed.");      
+    }
+
+    printf("GlobalMemoryStatusEx:\n");
+    printf("    ullTotalPhys: %llu\n", memoryStatus.ullTotalPhys);
+    printf("    ullAvailPhys: %llu\n", memoryStatus.ullAvailPhys);
+    printf("    ullTotalVirtual: %llu\n", memoryStatus.ullTotalVirtual);
+    printf("    ullAvailVirtual: %llu\n", memoryStatus.ullAvailVirtual);
+    printf("    ullTotalPageFile: %llu\n", memoryStatus.ullTotalPageFile);
+    printf("    ullAvailPageFile: %llu\n", memoryStatus.ullAvailPageFile);
+    printf("    ullAvailExtendedVirtual: %llu\n", memoryStatus.ullAvailExtendedVirtual);
+    printf("    dwMemoryLoad: %u\n", memoryStatus.dwMemoryLoad);
+
+    if (memoryStatus.ullTotalPhys == 0 ||
+        memoryStatus.ullAvailPhys == 0 ||
+        memoryStatus.ullTotalVirtual == 0 ||
+        memoryStatus.ullAvailVirtual == 0
+        )
+    {
+        Fail("ERROR: GlobalMemoryStatusEx succeeded, but returned zero physical of virtual memory sizes.");      
+    }
+
+    PAL_Terminate();
+    return PASS;
+}
diff --git a/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/testinfo.dat b/src/pal/tests/palsuite/miscellaneous/GlobalMemoryStatusEx/test1/testinfo.dat
new file mode 100644 (file)
index 0000000..7ae3b51
--- /dev/null
@@ -0,0 +1,14 @@
+# 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 = GlobalMemoryStatusEx
+Name = Positive Test for GlobalMemoryStatusEx
+TYPE = DEFAULT
+EXE1 = test
+Description
+= Ensures that invocation of GlobalMemoryStatusEx succeeds and 
+= that it returns nonzero virtual and physical memory sizes
+
index 9f0be50..f0dfe3f 100644 (file)
@@ -668,6 +668,7 @@ miscellaneous/GetEnvironmentVariableW/test5/paltest_getenvironmentvariablew_test
 miscellaneous/GetEnvironmentVariableW/test6/paltest_getenvironmentvariablew_test6
 miscellaneous/GetLastError/test1/paltest_getlasterror_test1
 miscellaneous/GetSystemInfo/test1/paltest_getsysteminfo_test1
+miscellaneous/GlobalMemoryStatusEx/test1/paltest_globalmemorystatusex_test1
 miscellaneous/GetTickCount/test1/paltest_gettickcount_test1
 miscellaneous/InterlockedCompareExchange/test1/paltest_interlockedcompareexchange_test1
 miscellaneous/InterlockedCompareExchange/test2/paltest_interlockedcompareexchange_test2
index 1872313..d4cb311 100644 (file)
@@ -753,6 +753,7 @@ miscellaneous/getenvironmentvariablew/test3,1
 miscellaneous/getenvironmentvariablew/test4,1
 miscellaneous/getlasterror/test1,1
 miscellaneous/getsysteminfo/test1,1
+miscellaneous/getglobalmemorystatusex/test1,1
 miscellaneous/gettickcount/test1,1
 miscellaneous/getversionexa/test1,1
 miscellaneous/getversionexw/test1,1