0f4fd09f059061fdf184185a1282aaf7cad2a9d7
[WebKit-https.git] / Source / WTF / wtf / MemoryPressureHandler.cpp
1 /*
2  * Copyright (C) 2011-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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "MemoryPressureHandler.h"
28
29 #include <wtf/MemoryFootprint.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/RAMSize.h>
32
33 #define LOG_CHANNEL_PREFIX Log
34
35 namespace WTF {
36
37 #if RELEASE_LOG_DISABLED
38 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", WTFLogLevelError };
39 #else
40 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", WTFLogLevelError, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
41 #endif
42
43 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
44
45 MemoryPressureHandler& MemoryPressureHandler::singleton()
46 {
47     static NeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
48     return memoryPressureHandler;
49 }
50
51 MemoryPressureHandler::MemoryPressureHandler()
52 #if OS(LINUX)
53     : m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
54 #elif OS(WINDOWS)
55     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
56 #endif
57 {
58 }
59
60 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
61 {
62     if (!isFastMallocEnabled()) {
63         // If we're running with FastMalloc disabled, some kind of testing or debugging is probably happening.
64         // Let's be nice and not enable the memory kill mechanism.
65         return;
66     }
67
68     if (use) {
69         m_measurementTimer = std::make_unique<RunLoop::Timer<MemoryPressureHandler>>(RunLoop::main(), this, &MemoryPressureHandler::measurementTimerFired);
70         m_measurementTimer->startRepeating(30_s);
71     } else
72         m_measurementTimer = nullptr;
73 }
74
75 #if !RELEASE_LOG_DISABLED
76 static const char* toString(MemoryUsagePolicy policy)
77 {
78     switch (policy) {
79     case MemoryUsagePolicy::Unrestricted: return "Unrestricted";
80     case MemoryUsagePolicy::Conservative: return "Conservative";
81     case MemoryUsagePolicy::Strict: return "Strict";
82     }
83 }
84 #endif
85
86 static size_t thresholdForMemoryKillWithProcessState(WebsamProcessState processState, unsigned tabCount)
87 {
88     size_t baseThreshold = 2 * GB;
89 #if CPU(X86_64) || CPU(ARM64)
90     if (processState == WebsamProcessState::Active)
91         baseThreshold = 4 * GB;
92     if (tabCount > 1)
93         baseThreshold += std::min(tabCount - 1, 4u) * 1 * GB;
94 #else
95     if ((tabCount > 1) || (processState == WebsamProcessState::Active))
96         baseThreshold = 3 * GB;
97 #endif
98     return std::min(baseThreshold, static_cast<size_t>(ramSize() * 0.9));
99 }
100
101 void MemoryPressureHandler::setPageCount(unsigned pageCount)
102 {
103     if (singleton().m_pageCount == pageCount)
104         return;
105     singleton().m_pageCount = pageCount;
106 }
107
108 size_t MemoryPressureHandler::thresholdForMemoryKill()
109 {
110     return thresholdForMemoryKillWithProcessState(m_processState, m_pageCount);
111 }
112
113 static size_t thresholdForPolicy(MemoryUsagePolicy policy)
114 {
115     const size_t baseThresholdForPolicy = std::min(3 * GB, ramSize());
116
117 #if PLATFORM(IOS)
118     const double conservativeThresholdFraction = 0.5;
119     const double strictThresholdFraction = 0.65;
120 #else
121     const double conservativeThresholdFraction = 0.33;
122     const double strictThresholdFraction = 0.5;
123 #endif
124
125     switch (policy) {
126     case MemoryUsagePolicy::Unrestricted:
127         return 0;
128     case MemoryUsagePolicy::Conservative:
129         return baseThresholdForPolicy * conservativeThresholdFraction;
130     case MemoryUsagePolicy::Strict:
131         return baseThresholdForPolicy * strictThresholdFraction;
132     default:
133         ASSERT_NOT_REACHED();
134         return 0;
135     }
136 }
137
138 static MemoryUsagePolicy policyForFootprint(size_t footprint)
139 {
140     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
141         return MemoryUsagePolicy::Strict;
142     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
143         return MemoryUsagePolicy::Conservative;
144     return MemoryUsagePolicy::Unrestricted;
145 }
146
147 MemoryUsagePolicy MemoryPressureHandler::currentMemoryUsagePolicy()
148 {
149     return policyForFootprint(memoryFootprint().value_or(0));
150 }
151
152 void MemoryPressureHandler::shrinkOrDie()
153 {
154     RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
155     releaseMemory(Critical::Yes, Synchronous::Yes);
156
157     auto footprint = memoryFootprint();
158     RELEASE_ASSERT(footprint);
159     RELEASE_LOG(MemoryPressure, "New memory footprint: %zu MB", footprint.value() / MB);
160
161     if (footprint.value() < thresholdForMemoryKill()) {
162         RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
163         setMemoryUsagePolicyBasedOnFootprint(footprint.value());
164         return;
165     }
166
167     WTFLogAlways("Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprint.value() / MB, thresholdForMemoryKill() / MB);
168     RELEASE_ASSERT(m_memoryKillCallback);
169     m_memoryKillCallback();
170 }
171
172 void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
173 {
174     auto newPolicy = policyForFootprint(footprint);
175     if (newPolicy == m_memoryUsagePolicy)
176         return;
177
178     RELEASE_LOG(MemoryPressure, "Memory usage policy changed: %s -> %s", toString(m_memoryUsagePolicy), toString(newPolicy));
179     m_memoryUsagePolicy = newPolicy;
180     memoryPressureStatusChanged();
181 }
182
183 void MemoryPressureHandler::measurementTimerFired()
184 {
185     auto footprint = memoryFootprint();
186     if (!footprint)
187         return;
188
189     RELEASE_LOG(MemoryPressure, "Current memory footprint: %zu MB", footprint.value() / MB);
190     if (footprint.value() >= thresholdForMemoryKill()) {
191         shrinkOrDie();
192         return;
193     }
194
195     setMemoryUsagePolicyBasedOnFootprint(footprint.value());
196
197     switch (m_memoryUsagePolicy) {
198     case MemoryUsagePolicy::Unrestricted:
199         break;
200     case MemoryUsagePolicy::Conservative:
201         releaseMemory(Critical::No, Synchronous::No);
202         break;
203     case MemoryUsagePolicy::Strict:
204         releaseMemory(Critical::Yes, Synchronous::No);
205         break;
206     }
207
208     if (processState() == WebsamProcessState::Active && footprint.value() > thresholdForMemoryKillWithProcessState(WebsamProcessState::Inactive, m_pageCount))
209         doesExceedInactiveLimitWhileActive();
210     else
211         doesNotExceedInactiveLimitWhileActive();
212 }
213
214 void MemoryPressureHandler::doesExceedInactiveLimitWhileActive()
215 {
216     if (m_hasInvokedDidExceedInactiveLimitWhileActiveCallback)
217         return;
218     if (m_didExceedInactiveLimitWhileActiveCallback)
219         m_didExceedInactiveLimitWhileActiveCallback();
220     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = true;
221 }
222
223 void MemoryPressureHandler::doesNotExceedInactiveLimitWhileActive()
224 {
225     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = false;
226 }
227
228 void MemoryPressureHandler::setProcessState(WebsamProcessState state)
229 {
230     if (m_processState == state)
231         return;
232     m_processState = state;
233 }
234
235 void MemoryPressureHandler::beginSimulatedMemoryPressure()
236 {
237     if (m_isSimulatingMemoryPressure)
238         return;
239     m_isSimulatingMemoryPressure = true;
240     memoryPressureStatusChanged();
241     respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
242 }
243
244 void MemoryPressureHandler::endSimulatedMemoryPressure()
245 {
246     if (!m_isSimulatingMemoryPressure)
247         return;
248     m_isSimulatingMemoryPressure = false;
249     memoryPressureStatusChanged();
250 }
251
252 void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
253 {
254     if (!m_lowMemoryHandler)
255         return;
256
257     ReliefLogger log("Total");
258     m_lowMemoryHandler(critical, synchronous);
259     platformReleaseMemory(critical);
260 }
261
262 void MemoryPressureHandler::setUnderMemoryPressure(bool underMemoryPressure)
263 {
264     if (m_underMemoryPressure == underMemoryPressure)
265         return;
266     m_underMemoryPressure = underMemoryPressure;
267     memoryPressureStatusChanged();
268 }
269
270 void MemoryPressureHandler::memoryPressureStatusChanged()
271 {
272     if (m_memoryPressureStatusChangedCallback)
273         m_memoryPressureStatusChangedCallback(isUnderMemoryPressure());
274 }
275
276 void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
277 {
278 #if !RELEASE_LOG_DISABLED
279 #define STRING_SPECIFICATION "%{public}s"
280 #define MEMORYPRESSURE_LOG(...) RELEASE_LOG(MemoryPressure, __VA_ARGS__)
281 #else
282 #define STRING_SPECIFICATION "%s"
283 #define MEMORYPRESSURE_LOG(...) WTFLogAlways(__VA_ARGS__)
284 #endif
285
286     auto currentMemory = platformMemoryUsage();
287     if (!currentMemory || !m_initialMemory) {
288         MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": (Unable to get dirty memory information for process)", m_logString);
289         return;
290     }
291
292     long residentDiff = currentMemory->resident - m_initialMemory->resident;
293     long physicalDiff = currentMemory->physical - m_initialMemory->physical;
294
295     MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
296         m_logString,
297         m_initialMemory->resident, currentMemory->resident, residentDiff,
298         m_initialMemory->physical, currentMemory->physical, physicalDiff);
299 }
300
301 #if !PLATFORM(COCOA) && !OS(LINUX) && !OS(WINDOWS)
302 void MemoryPressureHandler::install() { }
303 void MemoryPressureHandler::uninstall() { }
304 void MemoryPressureHandler::respondToMemoryPressure(Critical, Synchronous) { }
305 void MemoryPressureHandler::platformReleaseMemory(Critical) { }
306 std::optional<MemoryPressureHandler::ReliefLogger::MemoryUsage> MemoryPressureHandler::ReliefLogger::platformMemoryUsage() { return std::nullopt; }
307 #endif
308
309 #if !OS(WINDOWS)
310 void MemoryPressureHandler::platformInitialize() { }
311 #endif
312
313 } // namespace WebCore