1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/memory/memory_pressure_monitor_chromeos.h"
8 #include <sys/select.h>
10 #include "base/metrics/histogram_macros.h"
11 #include "base/posix/eintr_wrapper.h"
12 #include "base/process/process_metrics.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/time/time.h"
22 // The time between memory pressure checks. While under critical pressure, this
23 // is also the timer to repeat cleanup attempts.
24 const int kMemoryPressureIntervalMs
= 1000;
26 // The time which should pass between two moderate memory pressure calls.
27 const int kModerateMemoryPressureCooldownMs
= 10000;
29 // Number of event polls before the next moderate pressure event can be sent.
30 const int kModerateMemoryPressureCooldown
=
31 kModerateMemoryPressureCooldownMs
/ kMemoryPressureIntervalMs
;
33 // Threshold constants to emit pressure events.
34 const int kNormalMemoryPressureModerateThresholdPercent
= 60;
35 const int kNormalMemoryPressureCriticalThresholdPercent
= 95;
36 const int kAggressiveMemoryPressureModerateThresholdPercent
= 35;
37 const int kAggressiveMemoryPressureCriticalThresholdPercent
= 70;
39 // The possible state for memory pressure level. The values should be in line
40 // with values in MemoryPressureListener::MemoryPressureLevel and should be
41 // updated if more memory pressure levels are introduced.
42 enum MemoryPressureLevelUMA
{
43 MEMORY_PRESSURE_LEVEL_NONE
= 0,
44 MEMORY_PRESSURE_LEVEL_MODERATE
,
45 MEMORY_PRESSURE_LEVEL_CRITICAL
,
46 NUM_MEMORY_PRESSURE_LEVELS
49 // This is the file that will exist if low memory notification is available
50 // on the device. Whenever it becomes readable, it signals a low memory
52 const char kLowMemFile
[] = "/dev/chromeos-low-mem";
54 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
55 // the moderate pressure event.
56 int GetModerateMemoryThresholdInPercent(
57 MemoryPressureMonitor::MemoryPressureThresholds thresholds
) {
58 return thresholds
== MemoryPressureMonitor::
59 THRESHOLD_AGGRESSIVE_CACHE_DISCARD
||
60 thresholds
== MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
61 ? kAggressiveMemoryPressureModerateThresholdPercent
62 : kNormalMemoryPressureModerateThresholdPercent
;
65 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
66 // the critical pressure event.
67 int GetCriticalMemoryThresholdInPercent(
68 MemoryPressureMonitor::MemoryPressureThresholds thresholds
) {
69 return thresholds
== MemoryPressureMonitor::
70 THRESHOLD_AGGRESSIVE_TAB_DISCARD
||
71 thresholds
== MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
72 ? kAggressiveMemoryPressureCriticalThresholdPercent
73 : kNormalMemoryPressureCriticalThresholdPercent
;
76 // Converts free percent of memory into a memory pressure value.
77 MemoryPressureListener::MemoryPressureLevel
GetMemoryPressureLevelFromFillLevel(
78 int actual_fill_level
,
79 int moderate_threshold
,
80 int critical_threshold
) {
81 if (actual_fill_level
< moderate_threshold
)
82 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
;
83 return actual_fill_level
< critical_threshold
84 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
85 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
;
88 // This function will be called less then once a second. It will check if
89 // the kernel has detected a low memory situation.
90 bool IsLowMemoryCondition(int file_descriptor
) {
95 FD_SET(file_descriptor
, &fds
);
100 return HANDLE_EINTR(select(file_descriptor
+ 1, &fds
, NULL
, NULL
, &tv
)) > 0;
105 MemoryPressureMonitor::MemoryPressureMonitor(
106 MemoryPressureThresholds thresholds
)
107 : current_memory_pressure_level_(
108 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
),
109 moderate_pressure_repeat_count_(0),
110 moderate_pressure_threshold_percent_(
111 GetModerateMemoryThresholdInPercent(thresholds
)),
112 critical_pressure_threshold_percent_(
113 GetCriticalMemoryThresholdInPercent(thresholds
)),
114 low_mem_file_(HANDLE_EINTR(::open(kLowMemFile
, O_RDONLY
))),
115 weak_ptr_factory_(this) {
117 LOG_IF(ERROR
, !low_mem_file_
.is_valid()) << "Cannot open kernel listener";
120 MemoryPressureMonitor::~MemoryPressureMonitor() {
124 void MemoryPressureMonitor::ScheduleEarlyCheck() {
125 ThreadTaskRunnerHandle::Get()->PostTask(
126 FROM_HERE
, Bind(&MemoryPressureMonitor::CheckMemoryPressure
,
127 weak_ptr_factory_
.GetWeakPtr()));
130 MemoryPressureListener::MemoryPressureLevel
131 MemoryPressureMonitor::GetCurrentPressureLevel() const {
132 return current_memory_pressure_level_
;
136 MemoryPressureMonitor
* MemoryPressureMonitor::Get() {
137 return static_cast<MemoryPressureMonitor
*>(
138 base::MemoryPressureMonitor::Get());
141 void MemoryPressureMonitor::StartObserving() {
142 timer_
.Start(FROM_HERE
,
143 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs
),
144 Bind(&MemoryPressureMonitor::
145 CheckMemoryPressureAndRecordStatistics
,
146 weak_ptr_factory_
.GetWeakPtr()));
149 void MemoryPressureMonitor::StopObserving() {
150 // If StartObserving failed, StopObserving will still get called.
154 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
155 CheckMemoryPressure();
157 // Record UMA histogram statistics for the current memory pressure level.
158 MemoryPressureLevelUMA
memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE
);
159 switch (current_memory_pressure_level_
) {
160 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
:
161 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_NONE
;
163 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
:
164 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_MODERATE
;
166 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
:
167 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_CRITICAL
;
171 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel",
172 memory_pressure_level_uma
,
173 NUM_MEMORY_PRESSURE_LEVELS
);
176 void MemoryPressureMonitor::CheckMemoryPressure() {
177 MemoryPressureListener::MemoryPressureLevel old_pressure
=
178 current_memory_pressure_level_
;
180 // If we have the kernel low memory observer, we use it's flag instead of our
181 // own computation (for now). Note that in "simulation mode" it can be null.
182 // TODO(skuhne): We need to add code which makes sure that the kernel and this
183 // computation come to similar results and then remove this override again.
184 // TODO(skuhne): Add some testing framework here to see how close the kernel
185 // and the internal functions are.
186 if (low_mem_file_
.is_valid() && IsLowMemoryCondition(low_mem_file_
.get())) {
187 current_memory_pressure_level_
=
188 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
;
190 current_memory_pressure_level_
= GetMemoryPressureLevelFromFillLevel(
191 GetUsedMemoryInPercent(),
192 moderate_pressure_threshold_percent_
,
193 critical_pressure_threshold_percent_
);
195 // When listening to the kernel, we ignore the reported memory pressure
196 // level from our own computation and reduce critical to moderate.
197 if (low_mem_file_
.is_valid() &&
198 current_memory_pressure_level_
==
199 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
) {
200 current_memory_pressure_level_
=
201 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
;
205 // In case there is no memory pressure we do not notify.
206 if (current_memory_pressure_level_
==
207 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
) {
210 if (old_pressure
== current_memory_pressure_level_
) {
211 // If the memory pressure is still at the same level, we notify again for a
212 // critical level. In case of a moderate level repeat however, we only send
213 // a notification after a certain time has passed.
214 if (current_memory_pressure_level_
==
215 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
&&
216 ++moderate_pressure_repeat_count_
<
217 kModerateMemoryPressureCooldown
) {
220 } else if (current_memory_pressure_level_
==
221 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
&&
223 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
) {
224 // When we reducing the pressure level from critical to moderate, we
225 // restart the timeout and do not send another notification.
226 moderate_pressure_repeat_count_
= 0;
229 moderate_pressure_repeat_count_
= 0;
230 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_
);
233 // Gets the used ChromeOS memory in percent.
234 int MemoryPressureMonitor::GetUsedMemoryInPercent() {
235 base::SystemMemoryInfoKB info
;
236 if (!base::GetSystemMemoryInfo(&info
)) {
237 VLOG(1) << "Cannot determine the free memory of the system.";
240 // TODO(skuhne): Instead of adding the kernel memory pressure calculation
241 // logic here, we should have a kernel mechanism similar to the low memory
242 // notifier in ChromeOS which offers multiple pressure states.
243 // To track this, we have crbug.com/381196.
245 // The available memory consists of "real" and virtual (z)ram memory.
246 // Since swappable memory uses a non pre-deterministic compression and
247 // the compression creates its own "dynamic" in the system, it gets
248 // de-emphasized by the |kSwapWeight| factor.
249 const int kSwapWeight
= 4;
251 // The total memory we have is the "real memory" plus the virtual (z)ram.
252 int total_memory
= info
.total
+ info
.swap_total
/ kSwapWeight
;
254 // The kernel internally uses 50MB.
255 const int kMinFileMemory
= 50 * 1024;
257 // Most file memory can be easily reclaimed.
258 int file_memory
= info
.active_file
+ info
.inactive_file
;
259 // unless it is dirty or it's a minimal portion which is required.
260 file_memory
-= info
.dirty
+ kMinFileMemory
;
262 // Available memory is the sum of free, swap and easy reclaimable memory.
263 int available_memory
=
264 info
.free
+ info
.swap_free
/ kSwapWeight
+ file_memory
;
266 DCHECK(available_memory
< total_memory
);
267 int percentage
= ((total_memory
- available_memory
) * 100) / total_memory
;
271 } // namespace chromeos