RELEASE_LOG should not be Cocoa specific
[WebKit.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 <wtf/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 = { WTFLogChannelState::On, "MemoryPressure", WTFLogLevel::Error };
39 #endif
40 #if USE(OS_LOG) && !RELEASE_LOG_DISABLED
41 WTFLogChannel LogMemoryPressure = { WTFLogChannelState::On, "MemoryPressure", WTFLogLevel::Error, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
42 #endif
43 #if USE(JOURNALD) && !RELEASE_LOG_DISABLED
44 WTFLogChannel LogMemoryPressure = { WTFLogChannelState::On, "MemoryPressure", WTFLogLevel::Error, LOG_CHANNEL_WEBKIT_SUBSYSTEM };
45 #endif
46
47 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
48
49 MemoryPressureHandler& MemoryPressureHandler::singleton()
50 {
51     static NeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
52     return memoryPressureHandler;
53 }
54
55 MemoryPressureHandler::MemoryPressureHandler()
56 #if OS(LINUX)
57     : m_holdOffTimer(RunLoop::main(), this, &MemoryPressureHandler::holdOffTimerFired)
58 #elif OS(WINDOWS)
59     : m_windowsMeasurementTimer(RunLoop::main(), this, &MemoryPressureHandler::windowsMeasurementTimerFired)
60 #endif
61 {
62 #if PLATFORM(COCOA)
63     setDispatchQueue(dispatch_get_main_queue());
64 #endif
65 }
66
67 void MemoryPressureHandler::setShouldUsePeriodicMemoryMonitor(bool use)
68 {
69     if (!isFastMallocEnabled()) {
70         // If we're running with FastMalloc disabled, some kind of testing or debugging is probably happening.
71         // Let's be nice and not enable the memory kill mechanism.
72         return;
73     }
74
75     if (use) {
76         m_measurementTimer = makeUnique<RunLoop::Timer<MemoryPressureHandler>>(RunLoop::main(), this, &MemoryPressureHandler::measurementTimerFired);
77         m_measurementTimer->startRepeating(30_s);
78     } else
79         m_measurementTimer = nullptr;
80 }
81
82 #if !RELEASE_LOG_DISABLED
83 static const char* toString(MemoryUsagePolicy policy)
84 {
85     switch (policy) {
86     case MemoryUsagePolicy::Unrestricted: return "Unrestricted";
87     case MemoryUsagePolicy::Conservative: return "Conservative";
88     case MemoryUsagePolicy::Strict: return "Strict";
89     }
90     ASSERT_NOT_REACHED();
91     return "";
92 }
93 #endif
94
95 static size_t thresholdForMemoryKillWithProcessState(WebsamProcessState processState, unsigned tabCount)
96 {
97     size_t baseThreshold = 2 * GB;
98 #if CPU(X86_64) || CPU(ARM64)
99     if (processState == WebsamProcessState::Active)
100         baseThreshold = 4 * GB;
101     if (tabCount > 1)
102         baseThreshold += std::min(tabCount - 1, 4u) * 1 * GB;
103 #else
104     if ((tabCount > 1) || (processState == WebsamProcessState::Active))
105         baseThreshold = 3 * GB;
106 #endif
107     return std::min(baseThreshold, static_cast<size_t>(ramSize() * 0.9));
108 }
109
110 void MemoryPressureHandler::setPageCount(unsigned pageCount)
111 {
112     if (singleton().m_pageCount == pageCount)
113         return;
114     singleton().m_pageCount = pageCount;
115 }
116
117 size_t MemoryPressureHandler::thresholdForMemoryKill()
118 {
119     return thresholdForMemoryKillWithProcessState(m_processState, m_pageCount);
120 }
121
122 static size_t thresholdForPolicy(MemoryUsagePolicy policy)
123 {
124     const size_t baseThresholdForPolicy = std::min(3 * GB, ramSize());
125
126 #if PLATFORM(IOS_FAMILY)
127     const double conservativeThresholdFraction = 0.5;
128     const double strictThresholdFraction = 0.65;
129 #else
130     const double conservativeThresholdFraction = 0.33;
131     const double strictThresholdFraction = 0.5;
132 #endif
133
134     switch (policy) {
135     case MemoryUsagePolicy::Unrestricted:
136         return 0;
137     case MemoryUsagePolicy::Conservative:
138         return baseThresholdForPolicy * conservativeThresholdFraction;
139     case MemoryUsagePolicy::Strict:
140         return baseThresholdForPolicy * strictThresholdFraction;
141     default:
142         ASSERT_NOT_REACHED();
143         return 0;
144     }
145 }
146
147 static MemoryUsagePolicy policyForFootprint(size_t footprint)
148 {
149     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
150         return MemoryUsagePolicy::Strict;
151     if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
152         return MemoryUsagePolicy::Conservative;
153     return MemoryUsagePolicy::Unrestricted;
154 }
155
156 MemoryUsagePolicy MemoryPressureHandler::currentMemoryUsagePolicy()
157 {
158     return policyForFootprint(memoryFootprint());
159 }
160
161 void MemoryPressureHandler::shrinkOrDie()
162 {
163     RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
164     releaseMemory(Critical::Yes, Synchronous::Yes);
165
166     size_t footprint = memoryFootprint();
167     RELEASE_LOG(MemoryPressure, "New memory footprint: %zu MB", footprint / MB);
168
169     if (footprint < thresholdForMemoryKill()) {
170         RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
171         setMemoryUsagePolicyBasedOnFootprint(footprint);
172         return;
173     }
174
175     WTFLogAlways("Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprint / MB, thresholdForMemoryKill() / MB);
176     RELEASE_ASSERT(m_memoryKillCallback);
177     m_memoryKillCallback();
178 }
179
180 void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
181 {
182     auto newPolicy = policyForFootprint(footprint);
183     if (newPolicy == m_memoryUsagePolicy)
184         return;
185
186     RELEASE_LOG(MemoryPressure, "Memory usage policy changed: %s -> %s", toString(m_memoryUsagePolicy), toString(newPolicy));
187     m_memoryUsagePolicy = newPolicy;
188     memoryPressureStatusChanged();
189 }
190
191 void MemoryPressureHandler::measurementTimerFired()
192 {
193     size_t footprint = memoryFootprint();
194     RELEASE_LOG(MemoryPressure, "Current memory footprint: %zu MB", footprint / MB);
195     if (footprint >= thresholdForMemoryKill()) {
196         shrinkOrDie();
197         return;
198     }
199
200     setMemoryUsagePolicyBasedOnFootprint(footprint);
201
202     switch (m_memoryUsagePolicy) {
203     case MemoryUsagePolicy::Unrestricted:
204         break;
205     case MemoryUsagePolicy::Conservative:
206         releaseMemory(Critical::No, Synchronous::No);
207         break;
208     case MemoryUsagePolicy::Strict:
209         releaseMemory(Critical::Yes, Synchronous::No);
210         break;
211     }
212
213     if (processState() == WebsamProcessState::Active && footprint > thresholdForMemoryKillWithProcessState(WebsamProcessState::Inactive, m_pageCount))
214         doesExceedInactiveLimitWhileActive();
215     else
216         doesNotExceedInactiveLimitWhileActive();
217 }
218
219 void MemoryPressureHandler::doesExceedInactiveLimitWhileActive()
220 {
221     if (m_hasInvokedDidExceedInactiveLimitWhileActiveCallback)
222         return;
223     if (m_didExceedInactiveLimitWhileActiveCallback)
224         m_didExceedInactiveLimitWhileActiveCallback();
225     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = true;
226 }
227
228 void MemoryPressureHandler::doesNotExceedInactiveLimitWhileActive()
229 {
230     m_hasInvokedDidExceedInactiveLimitWhileActiveCallback = false;
231 }
232
233 void MemoryPressureHandler::setProcessState(WebsamProcessState state)
234 {
235     if (m_processState == state)
236         return;
237     m_processState = state;
238 }
239
240 void MemoryPressureHandler::beginSimulatedMemoryPressure()
241 {
242     if (m_isSimulatingMemoryPressure)
243         return;
244     m_isSimulatingMemoryPressure = true;
245     memoryPressureStatusChanged();
246     respondToMemoryPressure(Critical::Yes, Synchronous::Yes);
247 }
248
249 void MemoryPressureHandler::endSimulatedMemoryPressure()
250 {
251     if (!m_isSimulatingMemoryPressure)
252         return;
253     m_isSimulatingMemoryPressure = false;
254     memoryPressureStatusChanged();
255 }
256
257 void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
258 {
259     if (!m_lowMemoryHandler)
260         return;
261
262     ReliefLogger log("Total");
263     m_lowMemoryHandler(critical, synchronous);
264     platformReleaseMemory(critical);
265 }
266
267 void MemoryPressureHandler::setUnderMemoryPressure(bool underMemoryPressure)
268 {
269     if (m_underMemoryPressure == underMemoryPressure)
270         return;
271     m_underMemoryPressure = underMemoryPressure;
272     memoryPressureStatusChanged();
273 }
274
275 void MemoryPressureHandler::memoryPressureStatusChanged()
276 {
277     if (m_memoryPressureStatusChangedCallback)
278         m_memoryPressureStatusChangedCallback(isUnderMemoryPressure());
279 }
280
281 void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
282 {
283 #if !RELEASE_LOG_DISABLED
284 #define MEMORYPRESSURE_LOG(...) RELEASE_LOG(MemoryPressure, __VA_ARGS__)
285 #else
286 #define MEMORYPRESSURE_LOG(...) WTFLogAlways(__VA_ARGS__)
287 #endif
288
289     auto currentMemory = platformMemoryUsage();
290     if (!currentMemory || !m_initialMemory) {
291         MEMORYPRESSURE_LOG("Memory pressure relief: %" PUBLIC_LOG_STRING ": (Unable to get dirty memory information for process)", m_logString);
292         return;
293     }
294
295     long residentDiff = currentMemory->resident - m_initialMemory->resident;
296     long physicalDiff = currentMemory->physical - m_initialMemory->physical;
297
298     MEMORYPRESSURE_LOG("Memory pressure relief: %" PUBLIC_LOG_STRING ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
299         m_logString,
300         m_initialMemory->resident, currentMemory->resident, residentDiff,
301         m_initialMemory->physical, currentMemory->physical, physicalDiff);
302 }
303
304 #if !OS(WINDOWS)
305 void MemoryPressureHandler::platformInitialize() { }
306 #endif
307
308 #if PLATFORM(COCOA)
309 void MemoryPressureHandler::setDispatchQueue(dispatch_queue_t queue)
310 {
311     RELEASE_ASSERT(!m_installed);
312     dispatch_retain(queue);
313     if (m_dispatchQueue)
314         dispatch_release(m_dispatchQueue);
315     m_dispatchQueue = queue;
316 }
317 #endif
318
319 } // namespace WebCore