7fb88f4ccd8ff6df784404563e9d0ebfe3557374
[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
31 #define LOG_CHANNEL_PREFIX Log
32
33 namespace WTF {
34
35 #if RELEASE_LOG_DISABLED
36 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure" };
37 #else
38 WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
39 #endif
40
41 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
42
43 MemoryPressureHandler& MemoryPressureHandler::singleton()
44 {
45     static NeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
46     return memoryPressureHandler;
47 }
48
49 MemoryPressureHandler::MemoryPressureHandler()
50 #if OS(LINUX)
51     : m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
52 #elif OS(WINDOWS)
53     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
54 #endif
55 {
56 }
57
58 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
59 {
60     if (use) {
61         m_measurementTimer = std::make_unique<RunLoop::Timer<MemoryPressureHandler>>(RunLoop::main(), this, &MemoryPressureHandler::measurementTimerFired);
62         m_measurementTimer->startRepeating(30);
63     } else
64         m_measurementTimer = nullptr;
65 }
66
67 #if !RELEASE_LOG_DISABLED
68 static const char* toString(MemoryUsagePolicy policy)
69 {
70     switch (policy) {
71     case MemoryUsagePolicy::Unrestricted: return "Unrestricted";
72     case MemoryUsagePolicy::Conservative: return "Conservative";
73     case MemoryUsagePolicy::Strict: return "Strict";
74     }
75 }
76 #endif
77
78 size_t MemoryPressureHandler::thresholdForMemoryKill()
79 {
80 #if CPU(X86_64) || CPU(ARM64)
81     if (m_processState == WebsamProcessState::Active)
82         return 4 * GB;
83     return 2 * GB;
84 #else
85     return 3 * GB;
86 #endif
87 }
88
89 static size_t thresholdForPolicy(MemoryUsagePolicy policy)
90 {
91     switch (policy) {
92     case MemoryUsagePolicy::Conservative:
93         return 1 * GB;
94     case MemoryUsagePolicy::Strict:
95         return 1.5 * GB;
96     case MemoryUsagePolicy::Unrestricted:
97     default:
98         ASSERT_NOT_REACHED();
99         return 0;
100     }
101 }
102
103 static MemoryUsagePolicy policyForFootprint(size_t footprint)
104 {
105     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
106         return MemoryUsagePolicy::Strict;
107     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
108         return MemoryUsagePolicy::Conservative;
109     return MemoryUsagePolicy::Unrestricted;
110 }
111
112 void MemoryPressureHandler::shrinkOrDie()
113 {
114     RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
115     releaseMemory(Critical::Yes, Synchronous::Yes);
116
117     auto footprint = memoryFootprint();
118     RELEASE_ASSERT(footprint);
119     RELEASE_LOG(MemoryPressure, "New memory footprint: %lu MB", footprint.value() / MB);
120
121     if (footprint.value() < thresholdForMemoryKill()) {
122         RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
123         setMemoryUsagePolicyBasedOnFootprint(footprint.value());
124         return;
125     }
126
127     RELEASE_ASSERT(m_memoryKillCallback);
128     m_memoryKillCallback();
129 }
130
131 void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
132 {
133     auto newPolicy = policyForFootprint(footprint);
134     if (newPolicy == m_memoryUsagePolicy)
135         return;
136
137     RELEASE_LOG(MemoryPressure, "Memory usage policy changed: %s -> %s", toString(m_memoryUsagePolicy), toString(newPolicy));
138     m_memoryUsagePolicy = newPolicy;
139     memoryPressureStatusChanged();
140 }
141
142 void MemoryPressureHandler::measurementTimerFired()
143 {
144     auto footprint = memoryFootprint();
145     if (!footprint)
146         return;
147
148     RELEASE_LOG(MemoryPressure, "Current memory footprint: %lu MB", footprint.value() / MB);
149     if (footprint.value() >= thresholdForMemoryKill()) {
150         shrinkOrDie();
151         return;
152     }
153
154     setMemoryUsagePolicyBasedOnFootprint(footprint.value());
155
156     switch (m_memoryUsagePolicy) {
157     case MemoryUsagePolicy::Unrestricted:
158         break;
159     case MemoryUsagePolicy::Conservative:
160         releaseMemory(Critical::No, Synchronous::No);
161         break;
162     case MemoryUsagePolicy::Strict:
163         releaseMemory(Critical::Yes, Synchronous::No);
164         break;
165     }
166 }
167
168 void MemoryPressureHandler::setProcessState(WebsamProcessState state)
169 {
170     if (m_processState == state)
171         return;
172     m_processState = state;
173     memoryPressureStatusChanged();
174     if (m_processState == WebsamProcessState::Inactive)
175         respondToMemoryPressure(Critical::Yes, Synchronous::No);
176 }
177
178 void MemoryPressureHandler::beginSimulatedMemoryPressure()
179 {
180     if (m_isSimulatingMemoryPressure)
181         return;
182     m_isSimulatingMemoryPressure = true;
183     memoryPressureStatusChanged();
184     respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
185 }
186
187 void MemoryPressureHandler::endSimulatedMemoryPressure()
188 {
189     if (!m_isSimulatingMemoryPressure)
190         return;
191     m_isSimulatingMemoryPressure = false;
192     memoryPressureStatusChanged();
193 }
194
195 bool MemoryPressureHandler::isUnderMemoryPressure()
196 {
197     auto& memoryPressureHandler = singleton();
198     return memoryPressureHandler.m_underMemoryPressure
199 #if PLATFORM(MAC)
200         || memoryPressureHandler.m_memoryUsagePolicy >= MemoryUsagePolicy::Strict
201         || memoryPressureHandler.m_processState == WebsamProcessState::Inactive
202 #endif
203         || memoryPressureHandler.m_isSimulatingMemoryPressure;
204 }
205
206 void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
207 {
208     if (!m_lowMemoryHandler)
209         return;
210
211     ReliefLogger log("Total");
212     m_lowMemoryHandler(critical, synchronous);
213     platformReleaseMemory(critical);
214 }
215
216 void MemoryPressureHandler::setUnderMemoryPressure(bool underMemoryPressure)
217 {
218     if (m_underMemoryPressure == underMemoryPressure)
219         return;
220     m_underMemoryPressure = underMemoryPressure;
221     memoryPressureStatusChanged();
222 }
223
224 void MemoryPressureHandler::memoryPressureStatusChanged()
225 {
226     if (m_memoryPressureStatusChangedCallback)
227         m_memoryPressureStatusChangedCallback(isUnderMemoryPressure());
228 }
229
230 void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
231 {
232 #if !RELEASE_LOG_DISABLED
233 #define STRING_SPECIFICATION "%{public}s"
234 #define MEMORYPRESSURE_LOG(...) RELEASE_LOG(MemoryPressure, __VA_ARGS__)
235 #else
236 #define STRING_SPECIFICATION "%s"
237 #define MEMORYPRESSURE_LOG(...) WTFLogAlways(__VA_ARGS__)
238 #endif
239
240     auto currentMemory = platformMemoryUsage();
241     if (!currentMemory || !m_initialMemory) {
242         MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": (Unable to get dirty memory information for process)", m_logString);
243         return;
244     }
245
246     long residentDiff = currentMemory->resident - m_initialMemory->resident;
247     long physicalDiff = currentMemory->physical - m_initialMemory->physical;
248
249     MEMORYPRESSURE_LOG("Memory pressure relief: " STRING_SPECIFICATION ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
250         m_logString,
251         m_initialMemory->resident, currentMemory->resident, residentDiff,
252         m_initialMemory->physical, currentMemory->physical, physicalDiff);
253 }
254
255 #if !PLATFORM(COCOA) && !OS(LINUX) && !PLATFORM(WIN)
256 void MemoryPressureHandler::install() { }
257 void MemoryPressureHandler::uninstall() { }
258 void MemoryPressureHandler::holdOff(unsigned) { }
259 void MemoryPressureHandler::respondToMemoryPressure(Critical, Synchronous) { }
260 void MemoryPressureHandler::platformReleaseMemory(Critical) { }
261 std::optional<MemoryPressureHandler::ReliefLogger::MemoryUsage> MemoryPressureHandler::ReliefLogger::platformMemoryUsage() { return std::nullopt; }
262 #endif
263
264 #if !PLATFORM(WIN)
265 void MemoryPressureHandler::platformInitialize() { }
266 #endif
267
268 } // namespace WebCore