Unreviewed. Build fix after r244244.
[WebKit-https.git] / Source / bmalloc / bmalloc / AvailableMemory.cpp
1 /*
2  * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "AvailableMemory.h"
27
28 #include "Environment.h"
29 #if BPLATFORM(IOS_FAMILY)
30 #include "MemoryStatusSPI.h"
31 #endif
32 #include "PerProcess.h"
33 #include "Scavenger.h"
34 #include "Sizes.h"
35 #include <mutex>
36 #if BOS(DARWIN)
37 #if BPLATFORM(IOS_FAMILY)
38 #import <algorithm>
39 #endif
40 #import <dispatch/dispatch.h>
41 #import <mach/host_info.h>
42 #import <mach/mach.h>
43 #import <mach/mach_error.h>
44 #import <math.h>
45 #elif BOS(UNIX)
46 #if BOS(LINUX)
47 #include <algorithm>
48 #include <fcntl.h>
49 #endif
50 #include <unistd.h>
51 #endif
52
53 namespace bmalloc {
54
55 static const size_t availableMemoryGuess = 512 * bmalloc::MB;
56
57 #if BOS(DARWIN)
58 static size_t memorySizeAccordingToKernel()
59 {
60 #if BPLATFORM(IOS_FAMILY_SIMULATOR)
61     BUNUSED_PARAM(availableMemoryGuess);
62     // Pretend we have 1024MB of memory to make cache sizes behave like on device.
63     return 1024 * bmalloc::MB;
64 #else
65     host_basic_info_data_t hostInfo;
66
67     mach_port_t host = mach_host_self();
68     mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
69     kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count);
70     mach_port_deallocate(mach_task_self(), host);
71     if (r != KERN_SUCCESS)
72         return availableMemoryGuess;
73
74     if (hostInfo.max_mem > std::numeric_limits<size_t>::max())
75         return std::numeric_limits<size_t>::max();
76
77     return static_cast<size_t>(hostInfo.max_mem);
78 #endif
79 }
80 #endif
81
82 #if BPLATFORM(IOS_FAMILY)
83 static size_t jetsamLimit()
84 {
85     memorystatus_memlimit_properties_t properties;
86     pid_t pid = getpid();
87     if (memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &properties, sizeof(properties)))
88         return 840 * bmalloc::MB;
89     if (properties.memlimit_active < 0)
90         return std::numeric_limits<size_t>::max();
91     return static_cast<size_t>(properties.memlimit_active) * bmalloc::MB;
92 }
93 #endif
94
95 #if BOS(LINUX)
96 struct LinuxMemory {
97     static const LinuxMemory& singleton()
98     {
99         static LinuxMemory s_singleton;
100         static std::once_flag s_onceFlag;
101         std::call_once(s_onceFlag,
102             [] {
103                 long numPages = sysconf(_SC_PHYS_PAGES);
104                 s_singleton.pageSize = sysconf(_SC_PAGE_SIZE);
105                 if (numPages == -1 || s_singleton.pageSize == -1)
106                     s_singleton.availableMemory = availableMemoryGuess;
107                 else
108                     s_singleton.availableMemory = numPages * s_singleton.pageSize;
109
110                 s_singleton.statmFd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
111             });
112         return s_singleton;
113     }
114
115     size_t footprint() const
116     {
117         if (statmFd == -1)
118             return 0;
119
120         std::array<char, 256> statmBuffer;
121         ssize_t numBytes = pread(statmFd, statmBuffer.data(), statmBuffer.size(), 0);
122         if (numBytes <= 0)
123             return 0;
124
125         std::array<char, 32> rssBuffer;
126         {
127             auto begin = std::find(statmBuffer.begin(), statmBuffer.end(), ' ');
128             if (begin == statmBuffer.end())
129                 return 0;
130
131             std::advance(begin, 1);
132             auto end = std::find(begin, statmBuffer.end(), ' ');
133             if (end == statmBuffer.end())
134                 return 0;
135
136             auto last = std::copy_n(begin, std::min<size_t>(31, std::distance(begin, end)), rssBuffer.begin());
137             *last = '\0';
138         }
139
140         unsigned long dirtyPages = strtoul(rssBuffer.data(), nullptr, 10);
141         return dirtyPages * pageSize;
142     }
143
144     long pageSize { 0 };
145     size_t availableMemory { 0 };
146
147     int statmFd { -1 };
148 };
149 #endif
150
151 static size_t computeAvailableMemory()
152 {
153 #if BOS(DARWIN)
154     size_t sizeAccordingToKernel = memorySizeAccordingToKernel();
155 #if BPLATFORM(IOS_FAMILY)
156     sizeAccordingToKernel = std::min(sizeAccordingToKernel, jetsamLimit());
157 #endif
158     size_t multiple = 128 * bmalloc::MB;
159
160     // Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
161     // (for example) and we have code that depends on those boundaries.
162     return ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
163 #elif BOS(LINUX)
164     return LinuxMemory::singleton().availableMemory;
165 #elif BOS(UNIX)
166     long pages = sysconf(_SC_PHYS_PAGES);
167     long pageSize = sysconf(_SC_PAGE_SIZE);
168     if (pages == -1 || pageSize == -1)
169         return availableMemoryGuess;
170     return pages * pageSize;
171 #else
172     return availableMemoryGuess;
173 #endif
174 }
175
176 size_t availableMemory()
177 {
178     static size_t availableMemory;
179     static std::once_flag onceFlag;
180     std::call_once(onceFlag, [] {
181         availableMemory = computeAvailableMemory();
182     });
183     return availableMemory;
184 }
185
186 #if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
187 MemoryStatus memoryStatus()
188 {
189 #if BPLATFORM(IOS_FAMILY)
190     task_vm_info_data_t vmInfo;
191     mach_msg_type_number_t vmSize = TASK_VM_INFO_COUNT;
192     
193     size_t memoryFootprint = 0;
194     if (KERN_SUCCESS == task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)(&vmInfo), &vmSize))
195         memoryFootprint = static_cast<size_t>(vmInfo.phys_footprint);
196
197     double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(availableMemory());
198 #elif BOS(LINUX)
199     auto& memory = LinuxMemory::singleton();
200     size_t memoryFootprint = memory.footprint();
201     double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(memory.availableMemory);
202 #endif
203
204     double percentAvailableMemoryInUse = std::min(percentInUse, 1.0);
205     return MemoryStatus(memoryFootprint, percentAvailableMemoryInUse);
206 }
207 #endif
208
209 } // namespace bmalloc