Use constexpr instead of const in symbol definitions that are obviously constexpr.
[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 <array>
36 #include <mutex>
37 #if BOS(DARWIN)
38 #if BPLATFORM(IOS_FAMILY)
39 #import <algorithm>
40 #endif
41 #import <dispatch/dispatch.h>
42 #import <mach/host_info.h>
43 #import <mach/mach.h>
44 #import <mach/mach_error.h>
45 #import <math.h>
46 #elif BOS(UNIX)
47 #if BOS(LINUX)
48 #include <algorithm>
49 #include <fcntl.h>
50 #endif
51 #include <unistd.h>
52 #endif
53
54 namespace bmalloc {
55
56 static constexpr size_t availableMemoryGuess = 512 * bmalloc::MB;
57
58 #if BOS(DARWIN)
59 static size_t memorySizeAccordingToKernel()
60 {
61 #if BPLATFORM(IOS_FAMILY_SIMULATOR)
62     BUNUSED_PARAM(availableMemoryGuess);
63     // Pretend we have 1024MB of memory to make cache sizes behave like on device.
64     return 1024 * bmalloc::MB;
65 #else
66     host_basic_info_data_t hostInfo;
67
68     mach_port_t host = mach_host_self();
69     mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
70     kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count);
71     mach_port_deallocate(mach_task_self(), host);
72     if (r != KERN_SUCCESS)
73         return availableMemoryGuess;
74
75     if (hostInfo.max_mem > std::numeric_limits<size_t>::max())
76         return std::numeric_limits<size_t>::max();
77
78     return static_cast<size_t>(hostInfo.max_mem);
79 #endif
80 }
81 #endif
82
83 #if BPLATFORM(IOS_FAMILY)
84 static size_t jetsamLimit()
85 {
86     memorystatus_memlimit_properties_t properties;
87     pid_t pid = getpid();
88     if (memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &properties, sizeof(properties)))
89         return 840 * bmalloc::MB;
90     if (properties.memlimit_active < 0)
91         return std::numeric_limits<size_t>::max();
92     return static_cast<size_t>(properties.memlimit_active) * bmalloc::MB;
93 }
94 #endif
95
96 #if BOS(LINUX)
97 struct LinuxMemory {
98     static const LinuxMemory& singleton()
99     {
100         static LinuxMemory s_singleton;
101         static std::once_flag s_onceFlag;
102         std::call_once(s_onceFlag,
103             [] {
104                 long numPages = sysconf(_SC_PHYS_PAGES);
105                 s_singleton.pageSize = sysconf(_SC_PAGE_SIZE);
106                 if (numPages == -1 || s_singleton.pageSize == -1)
107                     s_singleton.availableMemory = availableMemoryGuess;
108                 else
109                     s_singleton.availableMemory = numPages * s_singleton.pageSize;
110
111                 s_singleton.statmFd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
112             });
113         return s_singleton;
114     }
115
116     size_t footprint() const
117     {
118         if (statmFd == -1)
119             return 0;
120
121         std::array<char, 256> statmBuffer;
122         ssize_t numBytes = pread(statmFd, statmBuffer.data(), statmBuffer.size(), 0);
123         if (numBytes <= 0)
124             return 0;
125
126         std::array<char, 32> rssBuffer;
127         {
128             auto begin = std::find(statmBuffer.begin(), statmBuffer.end(), ' ');
129             if (begin == statmBuffer.end())
130                 return 0;
131
132             std::advance(begin, 1);
133             auto end = std::find(begin, statmBuffer.end(), ' ');
134             if (end == statmBuffer.end())
135                 return 0;
136
137             auto last = std::copy_n(begin, std::min<size_t>(31, std::distance(begin, end)), rssBuffer.begin());
138             *last = '\0';
139         }
140
141         unsigned long dirtyPages = strtoul(rssBuffer.data(), nullptr, 10);
142         return dirtyPages * pageSize;
143     }
144
145     long pageSize { 0 };
146     size_t availableMemory { 0 };
147
148     int statmFd { -1 };
149 };
150 #endif
151
152 static size_t computeAvailableMemory()
153 {
154 #if BOS(DARWIN)
155     size_t sizeAccordingToKernel = memorySizeAccordingToKernel();
156 #if BPLATFORM(IOS_FAMILY)
157     sizeAccordingToKernel = std::min(sizeAccordingToKernel, jetsamLimit());
158 #endif
159     size_t multiple = 128 * bmalloc::MB;
160
161     // Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
162     // (for example) and we have code that depends on those boundaries.
163     return ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
164 #elif BOS(LINUX)
165     return LinuxMemory::singleton().availableMemory;
166 #elif BOS(UNIX)
167     long pages = sysconf(_SC_PHYS_PAGES);
168     long pageSize = sysconf(_SC_PAGE_SIZE);
169     if (pages == -1 || pageSize == -1)
170         return availableMemoryGuess;
171     return pages * pageSize;
172 #else
173     return availableMemoryGuess;
174 #endif
175 }
176
177 size_t availableMemory()
178 {
179     static size_t availableMemory;
180     static std::once_flag onceFlag;
181     std::call_once(onceFlag, [] {
182         availableMemory = computeAvailableMemory();
183     });
184     return availableMemory;
185 }
186
187 #if BPLATFORM(IOS_FAMILY) || BOS(LINUX)
188 MemoryStatus memoryStatus()
189 {
190 #if BPLATFORM(IOS_FAMILY)
191     task_vm_info_data_t vmInfo;
192     mach_msg_type_number_t vmSize = TASK_VM_INFO_COUNT;
193     
194     size_t memoryFootprint = 0;
195     if (KERN_SUCCESS == task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)(&vmInfo), &vmSize))
196         memoryFootprint = static_cast<size_t>(vmInfo.phys_footprint);
197
198     double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(availableMemory());
199 #elif BOS(LINUX)
200     auto& memory = LinuxMemory::singleton();
201     size_t memoryFootprint = memory.footprint();
202     double percentInUse = static_cast<double>(memoryFootprint) / static_cast<double>(memory.availableMemory);
203 #endif
204
205     double percentAvailableMemoryInUse = std::min(percentInUse, 1.0);
206     return MemoryStatus(memoryFootprint, percentAvailableMemoryInUse);
207 }
208 #endif
209
210 } // namespace bmalloc