Skip to content

Commit eb8da83

Browse files
committed
MemoryPressure: Handle video memory usage besides normal memory
1 parent cada9f9 commit eb8da83

5 files changed

Lines changed: 181 additions & 30 deletions

File tree

Source/WTF/wtf/MemoryPressureHandler.cpp

Lines changed: 123 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@
2828

2929
#include <algorithm>
3030
#include <atomic>
31+
#include <fnmatch.h>
3132
#include <functional>
3233
#include <ranges>
34+
#include <unistd.h>
3335
#include <wtf/Logging.h>
3436
#include <wtf/MemoryFootprint.h>
3537
#include <wtf/NeverDestroyed.h>
3638
#include <wtf/RAMSize.h>
39+
#include <wtf/text/StringToIntegerConversion.h>
3740

3841
namespace WTF {
3942

@@ -51,6 +54,56 @@ static const Seconds s_pollInterval = 30_s;
5154

5255
static std::atomic<bool> s_hasCreatedMemoryPressureHandler;
5356

57+
// This file contains the amount of video memory used, and will be filled by some other
58+
// platform component. It's a text file containing an unsigned integer value.
59+
static String s_GPUMemoryFile;
60+
static ssize_t s_envBaseThresholdVideo = 0;
61+
62+
static bool isWebProcess()
63+
{
64+
static bool result = false;
65+
static bool initialized = false;
66+
67+
if (!initialized) {
68+
initialized = true;
69+
70+
FILE* file = fopen("/proc/self/cmdline", "r");
71+
if (!file)
72+
return result;
73+
74+
char* buffer = nullptr;
75+
size_t size = 0;
76+
if (getline(&buffer, &size, file) != -1)
77+
result = !fnmatch("*WPEWebProcess*", buffer, 0);
78+
79+
free(buffer);
80+
fclose(file);
81+
}
82+
83+
return result;
84+
}
85+
86+
static size_t memoryFootprintVideo()
87+
{
88+
if (!isWebProcess() || s_GPUMemoryFile.isEmpty())
89+
return 0;
90+
91+
FILE* file = fopen(s_GPUMemoryFile.utf8().data(), "r");
92+
if (!file)
93+
return 0;
94+
95+
char* buffer = nullptr;
96+
size_t size = 0;
97+
size_t footprint = 0;
98+
if (getline(&buffer, &size, file) != -1)
99+
sscanf(buffer, "%u", &footprint);
100+
101+
free(buffer);
102+
fclose(file);
103+
104+
return footprint;
105+
}
106+
54107
MemoryPressureHandler& MemoryPressureHandler::singleton()
55108
{
56109
static LazyNeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
@@ -77,6 +130,27 @@ MemoryPressureHandler::MemoryPressureHandler()
77130
#if PLATFORM(COCOA)
78131
setDispatchQueue(dispatch_get_main_queue());
79132
#endif
133+
134+
// If this is the WebProcess, Check whether the env var WPE_POLL_MAX_MEMORY_GPU_FILE exists, containing the file
135+
// that we need to poll to get the video memory used, and whether WPE_POLL_MAX_MEMORY_GPU exists, overriding the
136+
// limit for video memory set by the API.
137+
if (isWebProcess()) {
138+
s_GPUMemoryFile = String::fromLatin1(getenv("WPE_POLL_MAX_MEMORY_GPU_FILE"));
139+
String s = String::fromLatin1(getenv("WPE_POLL_MAX_MEMORY_GPU"));
140+
if (!s.isEmpty()) {
141+
String value = s.convertToLowercaseWithoutLocale();
142+
size_t units = 1;
143+
if (value.endsWith('k'))
144+
units = KB;
145+
else if (value.endsWith('m'))
146+
units = MB;
147+
if (units != 1)
148+
value = value.substring(0, value.length() - 1);
149+
s_envBaseThresholdVideo = parseInteger<size_t>(value).value_or(0) * units;
150+
if (s_envBaseThresholdVideo)
151+
m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
152+
}
153+
}
80154
}
81155

82156
void MemoryPressureHandler::setMemoryFootprintPollIntervalForTesting(Seconds pollInterval)
@@ -134,10 +208,14 @@ void MemoryPressureHandler::setPageCount(unsigned pageCount)
134208
singleton().m_pageCount = pageCount;
135209
}
136210

137-
std::optional<size_t> MemoryPressureHandler::thresholdForMemoryKill()
211+
std::optional<size_t> MemoryPressureHandler::thresholdForMemoryKill(MemoryType type)
138212
{
139213
if (m_configuration.killThresholdFraction)
140-
return m_configuration.baseThreshold * (*m_configuration.killThresholdFraction);
214+
return (*m_configuration.killThresholdFraction) * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration.baseThresholdVideo);
215+
else {
216+
// Don't kill the process if no killThreshold was set.
217+
return std::nullopt;
218+
}
141219

142220
switch (m_processState) {
143221
case WebsamProcessState::Inactive:
@@ -148,26 +226,26 @@ std::optional<size_t> MemoryPressureHandler::thresholdForMemoryKill()
148226
return std::nullopt;
149227
}
150228

151-
size_t MemoryPressureHandler::thresholdForPolicy(MemoryUsagePolicy policy)
229+
size_t MemoryPressureHandler::thresholdForPolicy(MemoryUsagePolicy policy, MemoryType type)
152230
{
153231
switch (policy) {
154232
case MemoryUsagePolicy::Unrestricted:
155233
return 0;
156234
case MemoryUsagePolicy::Conservative:
157-
return m_configuration.baseThreshold * m_configuration.conservativeThresholdFraction;
235+
return m_configuration.conservativeThresholdFraction * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration.baseThresholdVideo);
158236
case MemoryUsagePolicy::Strict:
159-
return m_configuration.baseThreshold * m_configuration.strictThresholdFraction;
237+
return m_configuration.strictThresholdFraction * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration.baseThresholdVideo);
160238
default:
161239
ASSERT_NOT_REACHED();
162240
return 0;
163241
}
164242
}
165243

166-
MemoryUsagePolicy MemoryPressureHandler::policyForFootprint(size_t footprint)
244+
MemoryUsagePolicy MemoryPressureHandler::policyForFootprints(size_t footprint, size_t footprintVideo)
167245
{
168-
if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict))
246+
if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Strict, MemoryType::Normal) || footprintVideo >= thresholdForPolicy(MemoryUsagePolicy::Strict, MemoryType::Video))
169247
return MemoryUsagePolicy::Strict;
170-
if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative))
248+
if (footprint >= thresholdForPolicy(MemoryUsagePolicy::Conservative, MemoryType::Normal) || footprintVideo >= thresholdForPolicy(MemoryUsagePolicy::Conservative, MemoryType::Video))
171249
return MemoryUsagePolicy::Conservative;
172250
return MemoryUsagePolicy::Unrestricted;
173251
}
@@ -178,31 +256,35 @@ MemoryUsagePolicy MemoryPressureHandler::currentMemoryUsagePolicy()
178256
return MemoryUsagePolicy::Conservative;
179257
if (m_isSimulatingMemoryPressure)
180258
return MemoryUsagePolicy::Strict;
181-
return policyForFootprint(memoryFootprint());
259+
return policyForFootprints(memoryFootprint(), memoryFootprintVideo());
182260
}
183261

184-
void MemoryPressureHandler::shrinkOrDie(size_t killThreshold)
262+
void MemoryPressureHandler::shrinkOrDie(size_t killThreshold, size_t killThresholdVideo)
185263
{
186264
RELEASE_LOG(MemoryPressure, "Process is above the memory kill threshold. Trying to shrink down.");
187265
releaseMemory(Critical::Yes, Synchronous::Yes);
188266

189267
size_t footprint = memoryFootprint();
268+
size_t footprintVideo = memoryFootprintVideo();
190269
RELEASE_LOG(MemoryPressure, "New memory footprint: %zu MB", footprint / MB);
191270

192-
if (footprint < killThreshold) {
271+
if ((footprint < killThreshold) && (footprintVideo < killThresholdVideo)) {
193272
RELEASE_LOG(MemoryPressure, "Shrank below memory kill threshold. Process gets to live.");
194-
setMemoryUsagePolicyBasedOnFootprint(footprint);
273+
setMemoryUsagePolicyBasedOnFootprints(footprint, footprintVideo);
195274
return;
196275
}
197276

198-
WTFLogAlways("Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprint / MB, killThreshold / MB);
277+
if (footprint >= killThreshold)
278+
WTFLogAlways("Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprint / MB, killThreshold / MB);
279+
else
280+
WTFLogAlways("Unable to shrink video memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n", footprintVideo / MB, killThresholdVideo / MB);
199281
RELEASE_ASSERT(m_memoryKillCallback);
200282
m_memoryKillCallback();
201283
}
202284

203-
void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint(size_t footprint)
285+
void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprints(size_t footprint, size_t footprintVideo)
204286
{
205-
auto newPolicy = policyForFootprint(footprint);
287+
auto newPolicy = policyForFootprints(footprint, footprintVideo);
206288
if (newPolicy == m_memoryUsagePolicy)
207289
return;
208290

@@ -225,6 +307,7 @@ void MemoryPressureHandler::setMemoryFootprintNotificationThresholds(Vector<size
225307
void MemoryPressureHandler::measurementTimerFired()
226308
{
227309
size_t footprint = memoryFootprint();
310+
size_t footprintVideo = memoryFootprintVideo();
228311
#if PLATFORM(COCOA)
229312
RELEASE_LOG(MemoryPressure, "Current memory footprint: %zu MB", footprint / MB);
230313
#endif
@@ -234,13 +317,14 @@ void MemoryPressureHandler::measurementTimerFired()
234317
m_memoryFootprintNotificationHandler(notificationThreshold);
235318
}
236319

237-
auto killThreshold = thresholdForMemoryKill();
238-
if (killThreshold && footprint >= *killThreshold) {
239-
shrinkOrDie(*killThreshold);
320+
auto killThreshold = thresholdForMemoryKill(MemoryType::Normal);
321+
auto killThresholdVideo = thresholdForMemoryKill(MemoryType::Video);
322+
if ((killThreshold && footprint >= *killThreshold) || (killThresholdVideo && footprintVideo >= *killThresholdVideo)) {
323+
shrinkOrDie(*killThreshold, *killThresholdVideo);
240324
return;
241325
}
242326

243-
setMemoryUsagePolicyBasedOnFootprint(footprint);
327+
setMemoryUsagePolicyBasedOnFootprints(footprint, footprintVideo);
244328

245329
switch (m_memoryUsagePolicy) {
246330
case MemoryUsagePolicy::Unrestricted:
@@ -308,6 +392,20 @@ void MemoryPressureHandler::endSimulatedMemoryPressure()
308392
memoryPressureStatusChanged();
309393
}
310394

395+
void MemoryPressureHandler::setConfiguration(Configuration&& configuration)
396+
{
397+
m_configuration = WTFMove(configuration);
398+
if (s_envBaseThresholdVideo)
399+
m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
400+
}
401+
402+
void MemoryPressureHandler::setConfiguration(const Configuration& configuration)
403+
{
404+
m_configuration = configuration;
405+
if (s_envBaseThresholdVideo)
406+
m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
407+
}
408+
311409
void MemoryPressureHandler::releaseMemory(Critical critical, Synchronous synchronous)
312410
{
313411
if (!m_lowMemoryHandler)
@@ -349,14 +447,15 @@ void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
349447

350448
auto currentMemory = platformMemoryUsage();
351449
if (!currentMemory || !m_initialMemory) {
352-
MEMORYPRESSURE_LOG("Memory pressure relief: %" PUBLIC_LOG_STRING ": (Unable to get dirty memory information for process)", m_logString);
450+
MEMORYPRESSURE_LOG("Memory pressure relief: pid = %d, %" PUBLIC_LOG_STRING ": (Unable to get dirty memory information for process)", getpid(), m_logString);
353451
return;
354452
}
355453

356454
long residentDiff = currentMemory->resident - m_initialMemory->resident;
357455
long physicalDiff = currentMemory->physical - m_initialMemory->physical;
358456

359-
MEMORYPRESSURE_LOG("Memory pressure relief: %" PUBLIC_LOG_STRING ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
457+
MEMORYPRESSURE_LOG("Memory pressure relief: pid = %d, %" PUBLIC_LOG_STRING ": res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld",
458+
getpid(),
360459
m_logString,
361460
m_initialMemory->resident, currentMemory->resident, residentDiff,
362461
m_initialMemory->physical, currentMemory->physical, physicalDiff);
@@ -368,15 +467,17 @@ void MemoryPressureHandler::platformInitialize() { }
368467

369468
MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration()
370469
: baseThreshold(std::min(3 * GB, ramSize()))
470+
, baseThresholdVideo(1 * GB)
371471
, conservativeThresholdFraction(s_conservativeThresholdFraction)
372472
, strictThresholdFraction(s_strictThresholdFraction)
373473
, killThresholdFraction(s_killThresholdFraction)
374474
, pollInterval(s_pollInterval)
375475
{
376476
}
377477

378-
MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration(size_t base, double conservative, double strict, std::optional<double> kill, Seconds interval)
478+
MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration(size_t base, size_t baseVideo, double conservative, double strict, std::optional<double> kill, Seconds interval)
379479
: baseThreshold(base)
480+
, baseThresholdVideo(baseVideo)
380481
, conservativeThresholdFraction(conservative)
381482
, strictThresholdFraction(strict)
382483
, killThresholdFraction(kill)

Source/WTF/wtf/MemoryPressureHandler.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ enum class MemoryUsagePolicy : uint8_t {
6060
Strict, // Time to start pinching pennies for real
6161
};
6262

63+
enum class MemoryType : uint8_t {
64+
Normal,
65+
Video
66+
};
67+
6368
enum class WebsamProcessState : uint8_t {
6469
Active,
6570
Inactive,
@@ -73,9 +78,10 @@ typedef WTF::Function<void(Critical, Synchronous)> LowMemoryHandler;
7378
struct MemoryPressureHandlerConfiguration {
7479
WTF_MAKE_STRUCT_FAST_ALLOCATED;
7580
WTF_EXPORT_PRIVATE MemoryPressureHandlerConfiguration();
76-
WTF_EXPORT_PRIVATE MemoryPressureHandlerConfiguration(size_t, double, double, std::optional<double>, Seconds);
81+
WTF_EXPORT_PRIVATE MemoryPressureHandlerConfiguration(size_t, size_t, double, double, std::optional<double>, Seconds);
7782

7883
size_t baseThreshold;
84+
size_t baseThresholdVideo;
7985
double conservativeThresholdFraction;
8086
double strictThresholdFraction;
8187
std::optional<double> killThresholdFraction;
@@ -194,8 +200,8 @@ class MemoryPressureHandler {
194200

195201
using Configuration = MemoryPressureHandlerConfiguration;
196202

197-
void setConfiguration(Configuration&& configuration) { m_configuration = WTFMove(configuration); }
198-
void setConfiguration(const Configuration& configuration) { m_configuration = configuration; }
203+
void setConfiguration(Configuration&&);
204+
void setConfiguration(const Configuration&);
199205

200206
WTF_EXPORT_PRIVATE void releaseMemory(Critical, Synchronous = Synchronous::No);
201207

@@ -218,9 +224,9 @@ class MemoryPressureHandler {
218224
WTF_EXPORT_PRIVATE void setMemoryFootprintNotificationThresholds(Vector<size_t>&& thresholds, WTF::Function<void(size_t)>&&);
219225

220226
private:
221-
std::optional<size_t> thresholdForMemoryKill();
222-
size_t thresholdForPolicy(MemoryUsagePolicy);
223-
MemoryUsagePolicy policyForFootprint(size_t);
227+
std::optional<size_t> thresholdForMemoryKill(MemoryType);
228+
size_t thresholdForPolicy(MemoryUsagePolicy, MemoryType);
229+
MemoryUsagePolicy policyForFootprints(size_t, size_t);
224230

225231
void memoryPressureStatusChanged();
226232

@@ -237,8 +243,8 @@ class MemoryPressureHandler {
237243
void platformInitialize();
238244

239245
void measurementTimerFired();
240-
void shrinkOrDie(size_t killThreshold);
241-
void setMemoryUsagePolicyBasedOnFootprint(size_t);
246+
void shrinkOrDie(size_t killThreshold, size_t killThresholdVideo);
247+
void setMemoryUsagePolicyBasedOnFootprints(size_t, size_t);
242248

243249
unsigned m_pageCount { 0 };
244250

Source/WebKit/Shared/WTFArgumentCoders.serialization.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ using WTF::ProcessID = pid_t;
231231
header: <wtf/MemoryPressureHandler.h>
232232
[CustomHeader] struct WTF::MemoryPressureHandlerConfiguration {
233233
size_t baseThreshold;
234+
size_t baseThresholdVideo;
234235
double conservativeThresholdFraction;
235236
double strictThresholdFraction;
236237
std::optional<double> killThresholdFraction;

Source/WebKit/UIProcess/API/glib/WebKitMemoryPressureSettings.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,42 @@ guint webkit_memory_pressure_settings_get_memory_limit(WebKitMemoryPressureSetti
144144
return settings->configuration.baseThreshold / MB;
145145
}
146146

147+
/**
148+
* webkit_memory_pressure_settings_set_video_memory_limit:
149+
* @settings: a #WebKitMemoryPressureSettings
150+
* @memory_limit: amount of video memory (in MB) that the process is allowed to use.
151+
*
152+
* Sets @memory_limit the video memory limit value to @settings.
153+
*
154+
* The default value is 1GB.
155+
*
156+
* Since: 2.34
157+
*/
158+
void webkit_memory_pressure_settings_set_video_memory_limit(WebKitMemoryPressureSettings* settings, guint memoryLimit)
159+
{
160+
g_return_if_fail(settings);
161+
g_return_if_fail(memoryLimit);
162+
163+
settings->configuration.baseThresholdVideo = memoryLimit * MB;
164+
}
165+
166+
/**
167+
* webkit_memory_pressure_settings_get_video_memory_limit:
168+
* @settings: a #WebKitMemoryPressureSettings
169+
*
170+
* Gets the video memory usage limit.
171+
*
172+
* Returns: current value, in megabytes.
173+
*
174+
* Since: 2.34
175+
*/
176+
guint webkit_memory_pressure_settings_get_video_memory_limit(WebKitMemoryPressureSettings* settings)
177+
{
178+
g_return_val_if_fail(settings, 0);
179+
180+
return settings->configuration.baseThresholdVideo / MB;
181+
}
182+
147183
/**
148184
* webkit_memory_pressure_settings_set_conservative_threshold:
149185
* @settings: a #WebKitMemoryPressureSettings

0 commit comments

Comments
 (0)