Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: base/chromeos/memory_pressure_monitor_chromeos.cc

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

Powered by Google App Engine
This is Rietveld 408576698