[bmalloc][Linux] Add support for memory status calculation
authorzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 14 Apr 2019 06:46:17 +0000 (06:46 +0000)
committerzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 14 Apr 2019 06:46:17 +0000 (06:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195938

Reviewed by Carlos Garcia Campos.

Memory status and under-memory-pressure capabilities in bmalloc can be
implemented on Linux by reading and parsing the statm file under the
proc filesystem.

We retrieve the resident set size from the statm file and multiply it
with the page size. This gives an upper-bound estimate of the memory
that's being consumed by the process.

The statm-based estimate seems preferable to other alternatives. One
such alternative would be reading and parsing more-detailed smaps file,
also exposed under the proc filesystem. This is at the moment being done
in WTF's MemoryFootprint implementation for Linux systems, but on Linux
ports this operation is being throttled to only execute once per second
because of the big computing expense required to read and parse out the
data. A future MemoryFootprint implementation could simply retrieve the
memory footprint value from bmalloc.

Another alternative is the Linux taskstats interface. This one would
require utilizing a netlink socket to retrieve the necessary statistics,
but it requires the process to have elevated privileges, which is a
blocker.

* bmalloc/AvailableMemory.cpp:
(bmalloc::LinuxMemory::singleton):
(bmalloc::LinuxMemory::footprint const):
(bmalloc::computeAvailableMemory):
(bmalloc::memoryStatus):
* bmalloc/AvailableMemory.h:
(bmalloc::isUnderMemoryPressure):
* bmalloc/bmalloc.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244244 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc/AvailableMemory.cpp
Source/bmalloc/bmalloc/AvailableMemory.h
Source/bmalloc/bmalloc/bmalloc.h

index 45248c7..3e0edb2 100644 (file)
@@ -1,3 +1,41 @@
+2019-04-13  Zan Dobersek  <zdobersek@igalia.com>
+
+        [bmalloc][Linux] Add support for memory status calculation
+        https://bugs.webkit.org/show_bug.cgi?id=195938
+
+        Reviewed by Carlos Garcia Campos.
+
+        Memory status and under-memory-pressure capabilities in bmalloc can be
+        implemented on Linux by reading and parsing the statm file under the
+        proc filesystem.
+
+        We retrieve the resident set size from the statm file and multiply it
+        with the page size. This gives an upper-bound estimate of the memory
+        that's being consumed by the process.
+
+        The statm-based estimate seems preferable to other alternatives. One
+        such alternative would be reading and parsing more-detailed smaps file,
+        also exposed under the proc filesystem. This is at the moment being done
+        in WTF's MemoryFootprint implementation for Linux systems, but on Linux
+        ports this operation is being throttled to only execute once per second
+        because of the big computing expense required to read and parse out the
+        data. A future MemoryFootprint implementation could simply retrieve the
+        memory footprint value from bmalloc.
+
+        Another alternative is the Linux taskstats interface. This one would
+        require utilizing a netlink socket to retrieve the necessary statistics,
+        but it requires the process to have elevated privileges, which is a
+        blocker.
+
+        * bmalloc/AvailableMemory.cpp:
+        (bmalloc::LinuxMemory::singleton):
+        (bmalloc::LinuxMemory::footprint const):
+        (bmalloc::computeAvailableMemory):
+        (bmalloc::memoryStatus):
+        * bmalloc/AvailableMemory.h:
+        (bmalloc::isUnderMemoryPressure):
+        * bmalloc/bmalloc.h:
+
 2019-04-04  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [WebCore] Put most of derived classes of ScriptWrappable into IsoHeap
index 9ee1594..a392087 100644 (file)
 #import <mach/mach_error.h>
 #import <math.h>
 #elif BOS(UNIX)
+#if BOS(LINUX)
+#include <algorithm>
+#include <fcntl.h>
+#endif
 #include <unistd.h>
 #endif
 
@@ -88,6 +92,62 @@ static size_t jetsamLimit()
 }
 #endif
 
+#if BOS(UNIX)
+struct LinuxMemory {
+    static const LinuxMemory& singleton()
+    {
+        static LinuxMemory s_singleton;
+        static std::once_flag s_onceFlag;
+        std::call_once(s_onceFlag,
+            [] {
+                long numPages = sysconf(_SC_PHYS_PAGES);
+                s_singleton.pageSize = sysconf(_SC_PAGE_SIZE);
+                if (numPages == -1 || s_singleton.pageSize == -1)
+                    s_singleton.availableMemory = availableMemoryGuess;
+                else
+                    s_singleton.availableMemory = numPages * s_singleton.pageSize;
+
+                s_singleton.statmFd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
+            });
+        return s_singleton;
+    }
+
+    size_t footprint() const
+    {
+        if (statmFd == -1)
+            return 0;
+
+        std::array<char, 256> statmBuffer;
+        ssize_t numBytes = pread(statmFd, statmBuffer.data(), statmBuffer.size(), 0);
+        if (numBytes <= 0)
+            return 0;
+
+        std::array<char, 32> rssBuffer;
+        {
+            auto begin = std::find(statmBuffer.begin(), statmBuffer.end(), ' ');
+            if (begin == statmBuffer.end())
+                return 0;
+
+            std::advance(begin, 1);
+            auto end = std::find(begin, statmBuffer.end(), ' ');
+            if (end == statmBuffer.end())
+                return 0;
+
+            auto last = std::copy_n(begin, std::min<size_t>(31, std::distance(begin, end)), rssBuffer.begin());
+            *last = '\0';
+        }
+
+        unsigned long dirtyPages = strtoul(rssBuffer.data(), nullptr, 10);
+        return dirtyPages * pageSize;
+    }
+
+    long pageSize { 0 };
+    size_t availableMemory { 0 };
+
+    int statmFd { -1 };
+};
+#endif
+
 static size_t computeAvailableMemory()
 {
 #if BOS(DARWIN)
@@ -100,6 +160,8 @@ static size_t computeAvailableMemory()
     // Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
     // (for example) and we have code that depends on those boundaries.
     return ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
+#elif BOS(LINUX)
+    return LinuxMemory::singleton().availableMemory;
 #elif BOS(UNIX)
     long pages = sysconf(_SC_PHYS_PAGES);
     long pageSize = sysconf(_SC_PAGE_SIZE);
@@ -121,9 +183,10 @@ size_t availableMemory()
     return availableMemory;
 }
 
-#if BPLATFORM(IOS_FAMILY)
+#if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
 MemoryStatus memoryStatus()
 {
+#if BPLATFORM(IOS_FAMILY)
     task_vm_info_data_t vmInfo;
     mach_msg_type_number_t vmSize = TASK_VM_INFO_COUNT;
     
@@ -132,8 +195,13 @@ MemoryStatus memoryStatus()
         memoryFootprint = static_cast<size_t>(vmInfo.phys_footprint);
 
     double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(availableMemory());
+#elif BOS(LINUX)
+    auto& memory = LinuxMemory::singleton();
+    size_t memoryFootprint = memory.footprint();
+    double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(memory.availableMemory);
+#endif
+
     double percentAvailableMemoryInUse = std::min(percentInUse, 1.0);
-    
     return MemoryStatus(memoryFootprint, percentAvailableMemoryInUse);
 }
 #endif
index 809d72e..959af18 100644 (file)
@@ -32,7 +32,7 @@ namespace bmalloc {
 
 size_t availableMemory();
 
-#if BPLATFORM(IOS_FAMILY)
+#if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
 struct MemoryStatus {
     MemoryStatus(size_t memoryFootprint, double percentAvailableMemoryInUse)
         : memoryFootprint(memoryFootprint)
@@ -61,7 +61,7 @@ inline double percentAvailableMemoryInUse()
 
 inline bool isUnderMemoryPressure()
 {
-#if BPLATFORM(IOS_FAMILY)
+#if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
     return percentAvailableMemoryInUse() > memoryPressureThreshold;
 #else
     return false;
index 775ac0a..6fb96c2 100644 (file)
@@ -111,7 +111,7 @@ inline size_t availableMemory()
     return bmalloc::availableMemory();
 }
     
-#if BPLATFORM(IOS_FAMILY)
+#if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
 inline size_t memoryFootprint()
 {
     return bmalloc::memoryFootprint();