2828
2929#include < algorithm>
3030#include < atomic>
31+ #include < fnmatch.h>
3132#include < functional>
33+ #include < unistd.h>
3234#include < wtf/Logging.h>
3335#include < wtf/MemoryFootprint.h>
3436#include < wtf/NeverDestroyed.h>
3537#include < wtf/RAMSize.h>
38+ #include < wtf/text/StringToIntegerConversion.h>
3639
3740namespace WTF {
3841
@@ -42,14 +45,64 @@ WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled =
4245static const double s_conservativeThresholdFraction = 0.5 ;
4346static const double s_strictThresholdFraction = 0.65 ;
4447#else
45- static const double s_conservativeThresholdFraction = 0.33 ;
46- static const double s_strictThresholdFraction = 0.5 ;
48+ static const double s_conservativeThresholdFraction = 0.8 ;
49+ static const double s_strictThresholdFraction = 0.9 ;
4750#endif
4851static const std::optional<double > s_killThresholdFraction;
4952static const Seconds s_pollInterval = 30_s;
5053
5154static std::atomic<bool > s_hasCreatedMemoryPressureHandler;
5255
56+ // This file contains the amount of video memory used, and will be filled by some other
57+ // platform component. It's a text file containing an unsigned integer value.
58+ static String s_GPUMemoryFile;
59+ static ssize_t s_envBaseThresholdVideo = 0 ;
60+
61+ static bool isWebProcess ()
62+ {
63+ static bool result = false ;
64+ static bool initialized = false ;
65+
66+ if (!initialized) {
67+ initialized = true ;
68+
69+ FILE* file = fopen (" /proc/self/cmdline" , " r" );
70+ if (!file)
71+ return result;
72+
73+ char * buffer = nullptr ;
74+ size_t size = 0 ;
75+ if (getline (&buffer, &size, file) != -1 )
76+ result = !fnmatch (" *WPEWebProcess*" , buffer, 0 );
77+
78+ free (buffer);
79+ fclose (file);
80+ }
81+
82+ return result;
83+ }
84+
85+ static size_t memoryFootprintVideo ()
86+ {
87+ if (!isWebProcess () || s_GPUMemoryFile.isEmpty ())
88+ return 0 ;
89+
90+ FILE* file = fopen (s_GPUMemoryFile.utf8 ().data (), " r" );
91+ if (!file)
92+ return 0 ;
93+
94+ char * buffer = nullptr ;
95+ size_t size = 0 ;
96+ size_t footprint = 0 ;
97+ if (getline (&buffer, &size, file) != -1 )
98+ sscanf (buffer, " %lu" , &footprint);
99+
100+ free (buffer);
101+ fclose (file);
102+
103+ return footprint;
104+ }
105+
53106MemoryPressureHandler& MemoryPressureHandler::singleton ()
54107{
55108 static LazyNeverDestroyed<MemoryPressureHandler> memoryPressureHandler;
@@ -76,6 +129,27 @@ MemoryPressureHandler::MemoryPressureHandler()
76129#if PLATFORM(COCOA)
77130 setDispatchQueue (dispatch_get_main_queue ());
78131#endif
132+
133+ // If this is the WebProcess, Check whether the env var WPE_POLL_MAX_MEMORY_GPU_FILE exists, containing the file
134+ // that we need to poll to get the video memory used, and whether WPE_POLL_MAX_MEMORY_GPU exists, overriding the
135+ // limit for video memory set by the API.
136+ if (isWebProcess ()) {
137+ s_GPUMemoryFile = String::fromLatin1 (getenv (" WPE_POLL_MAX_MEMORY_GPU_FILE" ));
138+ String s = String::fromLatin1 (getenv (" WPE_POLL_MAX_MEMORY_GPU" ));
139+ if (!s.isEmpty ()) {
140+ String value = s.convertToLowercaseWithoutLocale ();
141+ size_t units = 1 ;
142+ if (value.endsWith (' k' ))
143+ units = KB;
144+ else if (value.endsWith (' m' ))
145+ units = MB;
146+ if (units != 1 )
147+ value = value.substring (0 , value.length () - 1 );
148+ s_envBaseThresholdVideo = parseInteger<size_t >(value).value_or (0 ) * units;
149+ if (s_envBaseThresholdVideo)
150+ m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
151+ }
152+ }
79153}
80154
81155void MemoryPressureHandler::setMemoryFootprintPollIntervalForTesting (Seconds pollInterval)
@@ -139,10 +213,14 @@ void MemoryPressureHandler::setPageCount(unsigned pageCount)
139213 singleton ().m_pageCount = pageCount;
140214}
141215
142- std::optional<size_t > MemoryPressureHandler::thresholdForMemoryKill ()
216+ std::optional<size_t > MemoryPressureHandler::thresholdForMemoryKill (MemoryType type )
143217{
144218 if (m_configuration.killThresholdFraction )
145- return m_configuration.baseThreshold * (*m_configuration.killThresholdFraction );
219+ return (*m_configuration.killThresholdFraction ) * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration.baseThresholdVideo );
220+ else {
221+ // Don't kill the process if no killThreshold was set.
222+ return std::nullopt ;
223+ }
146224
147225 switch (m_processState) {
148226 case WebsamProcessState::Inactive:
@@ -153,26 +231,26 @@ std::optional<size_t> MemoryPressureHandler::thresholdForMemoryKill()
153231 return std::nullopt ;
154232}
155233
156- size_t MemoryPressureHandler::thresholdForPolicy (MemoryUsagePolicy policy)
234+ size_t MemoryPressureHandler::thresholdForPolicy (MemoryUsagePolicy policy, MemoryType type )
157235{
158236 switch (policy) {
159237 case MemoryUsagePolicy::Unrestricted:
160238 return 0 ;
161239 case MemoryUsagePolicy::Conservative:
162- return m_configuration.baseThreshold * m_configuration.conservativeThresholdFraction ;
240+ return m_configuration.conservativeThresholdFraction * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration. baseThresholdVideo ) ;
163241 case MemoryUsagePolicy::Strict:
164- return m_configuration.baseThreshold * m_configuration.strictThresholdFraction ;
242+ return m_configuration.strictThresholdFraction * (type == MemoryType::Normal ? m_configuration.baseThreshold : m_configuration. baseThresholdVideo ) ;
165243 default :
166244 ASSERT_NOT_REACHED ();
167245 return 0 ;
168246 }
169247}
170248
171- MemoryUsagePolicy MemoryPressureHandler::policyForFootprint (size_t footprint)
249+ MemoryUsagePolicy MemoryPressureHandler::policyForFootprints (size_t footprint, size_t footprintVideo )
172250{
173- if (footprint >= thresholdForPolicy (MemoryUsagePolicy::Strict))
251+ if (footprint >= thresholdForPolicy (MemoryUsagePolicy::Strict, MemoryType::Normal) || footprintVideo >= thresholdForPolicy (MemoryUsagePolicy::Strict, MemoryType::Video ))
174252 return MemoryUsagePolicy::Strict;
175- if (footprint >= thresholdForPolicy (MemoryUsagePolicy::Conservative))
253+ if (footprint >= thresholdForPolicy (MemoryUsagePolicy::Conservative, MemoryType::Normal) || footprintVideo >= thresholdForPolicy (MemoryUsagePolicy::Conservative, MemoryType::Video ))
176254 return MemoryUsagePolicy::Conservative;
177255 return MemoryUsagePolicy::Unrestricted;
178256}
@@ -183,31 +261,35 @@ MemoryUsagePolicy MemoryPressureHandler::currentMemoryUsagePolicy()
183261 return MemoryUsagePolicy::Conservative;
184262 if (m_isSimulatingMemoryPressure)
185263 return MemoryUsagePolicy::Strict;
186- return policyForFootprint (memoryFootprint ());
264+ return policyForFootprints (memoryFootprint (), memoryFootprintVideo ());
187265}
188266
189- void MemoryPressureHandler::shrinkOrDie (size_t killThreshold)
267+ void MemoryPressureHandler::shrinkOrDie (size_t killThreshold, size_t killThresholdVideo )
190268{
191269 RELEASE_LOG (MemoryPressure, " Process is above the memory kill threshold. Trying to shrink down." );
192270 releaseMemory (Critical::Yes, Synchronous::Yes);
193271
194272 size_t footprint = memoryFootprint ();
273+ size_t footprintVideo = memoryFootprintVideo ();
195274 RELEASE_LOG (MemoryPressure, " New memory footprint: %zu MB" , footprint / MB);
196275
197- if (footprint < killThreshold) {
276+ if (( footprint < killThreshold) && (footprintVideo < killThresholdVideo) ) {
198277 RELEASE_LOG (MemoryPressure, " Shrank below memory kill threshold. Process gets to live." );
199- setMemoryUsagePolicyBasedOnFootprint (footprint);
278+ setMemoryUsagePolicyBasedOnFootprints (footprint, footprintVideo );
200279 return ;
201280 }
202281
203- WTFLogAlways (" Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n " , footprint / MB, killThreshold / MB);
282+ if (footprint >= killThreshold)
283+ WTFLogAlways (" Unable to shrink memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n " , footprint / MB, killThreshold / MB);
284+ else
285+ WTFLogAlways (" Unable to shrink video memory footprint of process (%zu MB) below the kill thresold (%zu MB). Killed\n " , footprintVideo / MB, killThresholdVideo / MB);
204286 RELEASE_ASSERT (m_memoryKillCallback);
205287 m_memoryKillCallback ();
206288}
207289
208- void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprint (size_t footprint)
290+ void MemoryPressureHandler::setMemoryUsagePolicyBasedOnFootprints (size_t footprint, size_t footprintVideo )
209291{
210- auto newPolicy = policyForFootprint (footprint);
292+ auto newPolicy = policyForFootprints (footprint, footprintVideo );
211293 if (newPolicy == m_memoryUsagePolicy)
212294 return ;
213295
@@ -230,6 +312,7 @@ void MemoryPressureHandler::setMemoryFootprintNotificationThresholds(Vector<size
230312void MemoryPressureHandler::measurementTimerFired ()
231313{
232314 size_t footprint = memoryFootprint ();
315+ size_t footprintVideo = memoryFootprintVideo ();
233316#if PLATFORM(COCOA)
234317 RELEASE_LOG (MemoryPressure, " Current memory footprint: %zu MB" , footprint / MB);
235318#endif
@@ -239,13 +322,14 @@ void MemoryPressureHandler::measurementTimerFired()
239322 m_memoryFootprintNotificationHandler (notificationThreshold);
240323 }
241324
242- auto killThreshold = thresholdForMemoryKill ();
243- if (killThreshold && footprint >= *killThreshold) {
244- shrinkOrDie (*killThreshold);
325+ auto killThreshold = thresholdForMemoryKill (MemoryType::Normal);
326+ auto killThresholdVideo = thresholdForMemoryKill (MemoryType::Video);
327+ if ((killThreshold && footprint >= *killThreshold) || (killThresholdVideo && footprintVideo >= *killThresholdVideo)) {
328+ shrinkOrDie (*killThreshold, *killThresholdVideo);
245329 return ;
246330 }
247331
248- setMemoryUsagePolicyBasedOnFootprint (footprint);
332+ setMemoryUsagePolicyBasedOnFootprints (footprint, footprintVideo );
249333
250334 switch (m_memoryUsagePolicy) {
251335 case MemoryUsagePolicy::Unrestricted:
@@ -254,6 +338,13 @@ void MemoryPressureHandler::measurementTimerFired()
254338 releaseMemory (Critical::No, Synchronous::No);
255339 break ;
256340 case MemoryUsagePolicy::Strict:
341+ if (footprint > m_configuration.baseThreshold || footprintVideo > m_configuration.baseThresholdVideo ) {
342+ WTFLogAlways (" MemoryPressure: Critical memory usage (PID=%d) [MB]: %zu (of %zu), video: %zu (of %zu)\n " ,
343+ getpid (), footprint / MB, m_configuration.baseThreshold / MB,
344+ footprintVideo / MB, m_configuration.baseThresholdVideo / MB);
345+ releaseMemory (Critical::Yes, Synchronous::Yes);
346+ break ;
347+ }
257348 releaseMemory (Critical::Yes, Synchronous::No);
258349 break ;
259350 }
@@ -313,6 +404,20 @@ void MemoryPressureHandler::endSimulatedMemoryPressure()
313404 memoryPressureStatusChanged ();
314405}
315406
407+ void MemoryPressureHandler::setConfiguration (Configuration&& configuration)
408+ {
409+ m_configuration = WTFMove (configuration);
410+ if (s_envBaseThresholdVideo)
411+ m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
412+ }
413+
414+ void MemoryPressureHandler::setConfiguration (const Configuration& configuration)
415+ {
416+ m_configuration = configuration;
417+ if (s_envBaseThresholdVideo)
418+ m_configuration.baseThresholdVideo = s_envBaseThresholdVideo;
419+ }
420+
316421void MemoryPressureHandler::releaseMemory (Critical critical, Synchronous synchronous)
317422{
318423 if (!m_lowMemoryHandler)
@@ -354,14 +459,15 @@ void MemoryPressureHandler::ReliefLogger::logMemoryUsageChange()
354459
355460 auto currentMemory = platformMemoryUsage ();
356461 if (!currentMemory || !m_initialMemory) {
357- MEMORYPRESSURE_LOG (" Memory pressure relief: % " PUBLIC_LOG_STRING " : (Unable to get dirty memory information for process)" , m_logString);
462+ MEMORYPRESSURE_LOG (" Memory pressure relief: pid = %d, % " PUBLIC_LOG_STRING " : (Unable to get dirty memory information for process)" , getpid () , m_logString);
358463 return ;
359464 }
360465
361466 long residentDiff = currentMemory->resident - m_initialMemory->resident ;
362467 long physicalDiff = currentMemory->physical - m_initialMemory->physical ;
363468
364- MEMORYPRESSURE_LOG (" Memory pressure relief: %" PUBLIC_LOG_STRING " : res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld" ,
469+ MEMORYPRESSURE_LOG (" Memory pressure relief: pid = %d, %" PUBLIC_LOG_STRING " : res = %zu/%zu/%ld, res+swap = %zu/%zu/%ld" ,
470+ getpid (),
365471 m_logString,
366472 m_initialMemory->resident , currentMemory->resident , residentDiff,
367473 m_initialMemory->physical , currentMemory->physical , physicalDiff);
@@ -373,15 +479,17 @@ void MemoryPressureHandler::platformInitialize() { }
373479
374480MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration ()
375481 : baseThreshold(std::min(3 * GB, ramSize()))
482+ , baseThresholdVideo(1 * GB)
376483 , conservativeThresholdFraction(s_conservativeThresholdFraction)
377484 , strictThresholdFraction(s_strictThresholdFraction)
378485 , killThresholdFraction(s_killThresholdFraction)
379486 , pollInterval(s_pollInterval)
380487{
381488}
382489
383- MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration (size_t base, double conservative, double strict, std::optional<double > kill, Seconds interval)
490+ MemoryPressureHandlerConfiguration::MemoryPressureHandlerConfiguration (size_t base, size_t baseVideo, double conservative, double strict, std::optional<double > kill, Seconds interval)
384491 : baseThreshold(base)
492+ , baseThresholdVideo(baseVideo)
385493 , conservativeThresholdFraction(conservative)
386494 , strictThresholdFraction(strict)
387495 , killThresholdFraction(kill)
0 commit comments