Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / base / memory / memory_pressure_monitor_chromeos.cc
blob0c3d979b357fb5a795fa61f4416d97cad916e425
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"
7 #include <fcntl.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/sys_info.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/time/time.h"
18 namespace base {
19 namespace chromeos {
21 namespace {
23 // The time between memory pressure checks. While under critical pressure, this
24 // is also the timer to repeat cleanup attempts.
25 const int kMemoryPressureIntervalMs = 1000;
27 // The time which should pass between two moderate memory pressure calls.
28 const int kModerateMemoryPressureCooldownMs = 10000;
30 // Number of event polls before the next moderate pressure event can be sent.
31 const int kModerateMemoryPressureCooldown =
32 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
34 // Threshold constants to emit pressure events.
35 const int kNormalMemoryPressureModerateThresholdPercent = 60;
36 const int kNormalMemoryPressureCriticalThresholdPercent = 95;
37 const int kAggressiveMemoryPressureModerateThresholdPercent = 35;
38 const int kAggressiveMemoryPressureCriticalThresholdPercent = 70;
40 // The possible state for memory pressure level. The values should be in line
41 // with values in MemoryPressureListener::MemoryPressureLevel and should be
42 // updated if more memory pressure levels are introduced.
43 enum MemoryPressureLevelUMA {
44 MEMORY_PRESSURE_LEVEL_NONE = 0,
45 MEMORY_PRESSURE_LEVEL_MODERATE,
46 MEMORY_PRESSURE_LEVEL_CRITICAL,
47 NUM_MEMORY_PRESSURE_LEVELS
50 // This is the file that will exist if low memory notification is available
51 // on the device. Whenever it becomes readable, it signals a low memory
52 // condition.
53 const char kLowMemFile[] = "/dev/chromeos-low-mem";
55 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
56 // the moderate pressure event.
57 int GetModerateMemoryThresholdInPercent(
58 MemoryPressureMonitor::MemoryPressureThresholds thresholds) {
59 return thresholds == MemoryPressureMonitor::
60 THRESHOLD_AGGRESSIVE_CACHE_DISCARD ||
61 thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
62 ? kAggressiveMemoryPressureModerateThresholdPercent
63 : kNormalMemoryPressureModerateThresholdPercent;
66 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
67 // the critical pressure event.
68 int GetCriticalMemoryThresholdInPercent(
69 MemoryPressureMonitor::MemoryPressureThresholds thresholds) {
70 return thresholds == MemoryPressureMonitor::
71 THRESHOLD_AGGRESSIVE_TAB_DISCARD ||
72 thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
73 ? kAggressiveMemoryPressureCriticalThresholdPercent
74 : kNormalMemoryPressureCriticalThresholdPercent;
77 // Converts free percent of memory into a memory pressure value.
78 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
79 int actual_fill_level,
80 int moderate_threshold,
81 int critical_threshold) {
82 if (actual_fill_level < moderate_threshold)
83 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
84 return actual_fill_level < critical_threshold
85 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
86 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
89 // This function will be called less then once a second. It will check if
90 // the kernel has detected a low memory situation.
91 bool IsLowMemoryCondition(int file_descriptor) {
92 fd_set fds;
93 struct timeval tv;
95 FD_ZERO(&fds);
96 FD_SET(file_descriptor, &fds);
98 tv.tv_sec = 0;
99 tv.tv_usec = 0;
101 return HANDLE_EINTR(select(file_descriptor + 1, &fds, NULL, NULL, &tv)) > 0;
104 } // namespace
106 MemoryPressureMonitor::MemoryPressureMonitor(
107 MemoryPressureThresholds thresholds)
108 : current_memory_pressure_level_(
109 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
110 moderate_pressure_repeat_count_(0),
111 moderate_pressure_threshold_percent_(
112 GetModerateMemoryThresholdInPercent(thresholds)),
113 critical_pressure_threshold_percent_(
114 GetCriticalMemoryThresholdInPercent(thresholds)),
115 low_mem_file_(HANDLE_EINTR(::open(kLowMemFile, O_RDONLY))),
116 weak_ptr_factory_(this) {
117 StartObserving();
118 LOG_IF(ERROR,
119 base::SysInfo::IsRunningOnChromeOS() && !low_mem_file_.is_valid())
120 << "Cannot open kernel listener";
123 MemoryPressureMonitor::~MemoryPressureMonitor() {
124 StopObserving();
127 void MemoryPressureMonitor::ScheduleEarlyCheck() {
128 ThreadTaskRunnerHandle::Get()->PostTask(
129 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure,
130 weak_ptr_factory_.GetWeakPtr()));
133 MemoryPressureListener::MemoryPressureLevel
134 MemoryPressureMonitor::GetCurrentPressureLevel() const {
135 return current_memory_pressure_level_;
138 // static
139 MemoryPressureMonitor* MemoryPressureMonitor::Get() {
140 return static_cast<MemoryPressureMonitor*>(
141 base::MemoryPressureMonitor::Get());
144 void MemoryPressureMonitor::StartObserving() {
145 timer_.Start(FROM_HERE,
146 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs),
147 Bind(&MemoryPressureMonitor::
148 CheckMemoryPressureAndRecordStatistics,
149 weak_ptr_factory_.GetWeakPtr()));
152 void MemoryPressureMonitor::StopObserving() {
153 // If StartObserving failed, StopObserving will still get called.
154 timer_.Stop();
157 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
158 CheckMemoryPressure();
160 // Record UMA histogram statistics for the current memory pressure level.
161 MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE);
162 switch (current_memory_pressure_level_) {
163 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
164 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE;
165 break;
166 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
167 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE;
168 break;
169 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
170 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL;
171 break;
174 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel",
175 memory_pressure_level_uma,
176 NUM_MEMORY_PRESSURE_LEVELS);
179 void MemoryPressureMonitor::CheckMemoryPressure() {
180 MemoryPressureListener::MemoryPressureLevel old_pressure =
181 current_memory_pressure_level_;
183 // If we have the kernel low memory observer, we use it's flag instead of our
184 // own computation (for now). Note that in "simulation mode" it can be null.
185 // TODO(skuhne): We need to add code which makes sure that the kernel and this
186 // computation come to similar results and then remove this override again.
187 // TODO(skuhne): Add some testing framework here to see how close the kernel
188 // and the internal functions are.
189 if (low_mem_file_.is_valid() && IsLowMemoryCondition(low_mem_file_.get())) {
190 current_memory_pressure_level_ =
191 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
192 } else {
193 current_memory_pressure_level_ = GetMemoryPressureLevelFromFillLevel(
194 GetUsedMemoryInPercent(),
195 moderate_pressure_threshold_percent_,
196 critical_pressure_threshold_percent_);
198 // When listening to the kernel, we ignore the reported memory pressure
199 // level from our own computation and reduce critical to moderate.
200 if (low_mem_file_.is_valid() &&
201 current_memory_pressure_level_ ==
202 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
203 current_memory_pressure_level_ =
204 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
208 // In case there is no memory pressure we do not notify.
209 if (current_memory_pressure_level_ ==
210 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
211 return;
213 if (old_pressure == current_memory_pressure_level_) {
214 // If the memory pressure is still at the same level, we notify again for a
215 // critical level. In case of a moderate level repeat however, we only send
216 // a notification after a certain time has passed.
217 if (current_memory_pressure_level_ ==
218 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
219 ++moderate_pressure_repeat_count_ <
220 kModerateMemoryPressureCooldown) {
221 return;
223 } else if (current_memory_pressure_level_ ==
224 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
225 old_pressure ==
226 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
227 // When we reducing the pressure level from critical to moderate, we
228 // restart the timeout and do not send another notification.
229 moderate_pressure_repeat_count_ = 0;
230 return;
232 moderate_pressure_repeat_count_ = 0;
233 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_);
236 // Gets the used ChromeOS memory in percent.
237 int MemoryPressureMonitor::GetUsedMemoryInPercent() {
238 base::SystemMemoryInfoKB info;
239 if (!base::GetSystemMemoryInfo(&info)) {
240 VLOG(1) << "Cannot determine the free memory of the system.";
241 return 0;
243 // TODO(skuhne): Instead of adding the kernel memory pressure calculation
244 // logic here, we should have a kernel mechanism similar to the low memory
245 // notifier in ChromeOS which offers multiple pressure states.
246 // To track this, we have crbug.com/381196.
248 // The available memory consists of "real" and virtual (z)ram memory.
249 // Since swappable memory uses a non pre-deterministic compression and
250 // the compression creates its own "dynamic" in the system, it gets
251 // de-emphasized by the |kSwapWeight| factor.
252 const int kSwapWeight = 4;
254 // The total memory we have is the "real memory" plus the virtual (z)ram.
255 int total_memory = info.total + info.swap_total / kSwapWeight;
257 // The kernel internally uses 50MB.
258 const int kMinFileMemory = 50 * 1024;
260 // Most file memory can be easily reclaimed.
261 int file_memory = info.active_file + info.inactive_file;
262 // unless it is dirty or it's a minimal portion which is required.
263 file_memory -= info.dirty + kMinFileMemory;
265 // Available memory is the sum of free, swap and easy reclaimable memory.
266 int available_memory =
267 info.free + info.swap_free / kSwapWeight + file_memory;
269 DCHECK(available_memory < total_memory);
270 int percentage = ((total_memory - available_memory) * 100) / total_memory;
271 return percentage;
274 } // namespace chromeos
275 } // namespace base