OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/memory/memory_pressure_monitor_chromeos.h" | 5 #include "base/memory/memory_pressure_monitor.h" |
6 | |
7 #include <fcntl.h> | |
8 #include <sys/select.h> | |
9 | 6 |
10 #include "base/metrics/histogram_macros.h" | 7 #include "base/metrics/histogram_macros.h" |
11 #include "base/posix/eintr_wrapper.h" | |
12 #include "base/process/process_metrics.h" | 8 #include "base/process/process_metrics.h" |
13 #include "base/single_thread_task_runner.h" | 9 #include "base/single_thread_task_runner.h" |
14 #include "base/thread_task_runner_handle.h" | 10 #include "base/thread_task_runner_handle.h" |
15 #include "base/time/time.h" | 11 #include "base/time/time.h" |
16 | 12 |
17 namespace base { | 13 namespace base { |
18 namespace chromeos { | |
19 | 14 |
20 namespace { | 15 namespace { |
21 | 16 |
22 // The time between memory pressure checks. While under critical pressure, this | 17 // The time between memory pressure checks. While under critical pressure, this |
23 // is also the timer to repeat cleanup attempts. | 18 // is also the timer to repeat cleanup attempts. |
24 const int kMemoryPressureIntervalMs = 1000; | 19 const int kMemoryPressureIntervalMs = 1000; |
25 | 20 |
26 // The time which should pass between two moderate memory pressure calls. | 21 // The time which should pass between two moderate memory pressure calls. |
27 const int kModerateMemoryPressureCooldownMs = 10000; | 22 const int kModerateMemoryPressureCooldownMs = 10000; |
28 | 23 |
29 // Number of event polls before the next moderate pressure event can be sent. | 24 // Number of event polls before the next moderate pressure event can be sent. |
30 const int kModerateMemoryPressureCooldown = | 25 const int kModerateMemoryPressureCooldown = |
31 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; | 26 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; |
32 | 27 |
33 // Threshold constants to emit pressure events. | 28 // Threshold constants to emit pressure events. |
34 const int kNormalMemoryPressureModerateThresholdPercent = 60; | 29 const int kNormalMemoryPressureModerateThresholdPercent = 60; |
35 const int kNormalMemoryPressureCriticalThresholdPercent = 95; | 30 const int kNormalMemoryPressureCriticalThresholdPercent = 95; |
36 const int kAggressiveMemoryPressureModerateThresholdPercent = 35; | |
37 const int kAggressiveMemoryPressureCriticalThresholdPercent = 70; | |
38 | 31 |
39 // The possible state for memory pressure level. The values should be in line | 32 // The possible state for memory pressure level. The values should be in line |
40 // with values in MemoryPressureListener::MemoryPressureLevel and should be | 33 // with values in MemoryPressureListener::MemoryPressureLevel and should be |
41 // updated if more memory pressure levels are introduced. | 34 // updated if more memory pressure levels are introduced. |
42 enum MemoryPressureLevelUMA { | 35 enum MemoryPressureLevelUMA { |
43 MEMORY_PRESSURE_LEVEL_NONE = 0, | 36 UMA_MEMORY_PRESSURE_LEVEL_NONE = 0, |
44 MEMORY_PRESSURE_LEVEL_MODERATE, | 37 UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1, |
45 MEMORY_PRESSURE_LEVEL_CRITICAL, | 38 UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2, |
46 NUM_MEMORY_PRESSURE_LEVELS | 39 // This must be the last value in the enum. |
| 40 UMA_MEMORY_PRESSURE_LEVEL_COUNT, |
47 }; | 41 }; |
48 | 42 |
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 | |
51 // condition. | |
52 const char kLowMemFile[] = "/dev/chromeos-low-mem"; | |
53 | |
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; | |
63 } | |
64 | |
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; | |
74 } | |
75 | |
76 // Converts free percent of memory into a memory pressure value. | 43 // Converts free percent of memory into a memory pressure value. |
77 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel( | 44 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel( |
78 int actual_fill_level, | 45 int actual_fill_level) { |
79 int moderate_threshold, | 46 if (actual_fill_level < kNormalMemoryPressureModerateThresholdPercent) |
80 int critical_threshold) { | |
81 if (actual_fill_level < moderate_threshold) | |
82 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | 47 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
83 return actual_fill_level < critical_threshold | 48 return actual_fill_level < kNormalMemoryPressureCriticalThresholdPercent |
84 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE | 49 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE |
85 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; | 50 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; |
86 } | 51 } |
87 | |
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) { | |
91 fd_set fds; | |
92 struct timeval tv; | |
93 | |
94 FD_ZERO(&fds); | |
95 FD_SET(file_descriptor, &fds); | |
96 | |
97 tv.tv_sec = 0; | |
98 tv.tv_usec = 0; | |
99 | |
100 return HANDLE_EINTR(select(file_descriptor + 1, &fds, NULL, NULL, &tv)) > 0; | |
101 } | |
102 | |
103 } // namespace | 52 } // namespace |
104 | 53 |
105 MemoryPressureMonitor::MemoryPressureMonitor( | 54 MemoryPressureMonitor::MemoryPressureMonitor() |
106 MemoryPressureThresholds thresholds) | |
107 : current_memory_pressure_level_( | 55 : current_memory_pressure_level_( |
108 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), | 56 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), |
109 moderate_pressure_repeat_count_(0), | 57 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) { | 58 weak_ptr_factory_(this) { |
| 59 DCHECK(!g_monitor); |
| 60 g_monitor = this; |
| 61 |
116 StartObserving(); | 62 StartObserving(); |
117 LOG_IF(ERROR, !low_mem_file_.is_valid()) << "Cannot open kernel listener"; | |
118 } | 63 } |
119 | 64 |
120 MemoryPressureMonitor::~MemoryPressureMonitor() { | 65 MemoryPressureMonitor::~MemoryPressureMonitor() { |
| 66 DCHECK(g_monitor); |
| 67 g_monitor = nullptr; |
| 68 |
121 StopObserving(); | 69 StopObserving(); |
122 } | 70 } |
123 | 71 |
124 void MemoryPressureMonitor::ScheduleEarlyCheck() { | 72 void MemoryPressureMonitor::ScheduleEarlyCheck() { |
125 ThreadTaskRunnerHandle::Get()->PostTask( | 73 ThreadTaskRunnerHandle::Get()->PostTask( |
126 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, | 74 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, |
127 weak_ptr_factory_.GetWeakPtr())); | 75 weak_ptr_factory_.GetWeakPtr())); |
128 } | 76 } |
129 | 77 |
130 MemoryPressureListener::MemoryPressureLevel | 78 MemoryPressureListener::MemoryPressureLevel |
131 MemoryPressureMonitor::GetCurrentPressureLevel() const { | 79 MemoryPressureMonitor::GetCurrentPressureLevel() const { |
132 return current_memory_pressure_level_; | 80 return current_memory_pressure_level_; |
133 } | 81 } |
134 | 82 |
135 // static | |
136 MemoryPressureMonitor* MemoryPressureMonitor::Get() { | |
137 return static_cast<MemoryPressureMonitor*>( | |
138 base::MemoryPressureMonitor::Get()); | |
139 } | |
140 | |
141 void MemoryPressureMonitor::StartObserving() { | 83 void MemoryPressureMonitor::StartObserving() { |
142 timer_.Start(FROM_HERE, | 84 timer_.Start( |
143 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), | 85 FROM_HERE, TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), |
144 Bind(&MemoryPressureMonitor:: | 86 Bind(&MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics, |
145 CheckMemoryPressureAndRecordStatistics, | 87 weak_ptr_factory_.GetWeakPtr())); |
146 weak_ptr_factory_.GetWeakPtr())); | |
147 } | 88 } |
148 | 89 |
149 void MemoryPressureMonitor::StopObserving() { | 90 void MemoryPressureMonitor::StopObserving() { |
150 // If StartObserving failed, StopObserving will still get called. | 91 // If StartObserving failed, StopObserving will still get called. |
151 timer_.Stop(); | 92 timer_.Stop(); |
152 } | 93 } |
153 | 94 |
154 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { | 95 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { |
155 CheckMemoryPressure(); | 96 CheckMemoryPressure(); |
156 | 97 |
157 // Record UMA histogram statistics for the current memory pressure level. | 98 // Record UMA histogram statistics for the current memory pressure level. |
158 MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE); | 99 MemoryPressureLevelUMA memory_pressure_level_uma( |
| 100 UMA_MEMORY_PRESSURE_LEVEL_NONE); |
159 switch (current_memory_pressure_level_) { | 101 switch (current_memory_pressure_level_) { |
160 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | 102 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: |
161 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE; | 103 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_NONE; |
162 break; | 104 break; |
163 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | 105 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: |
164 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE; | 106 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_MODERATE; |
165 break; | 107 break; |
166 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | 108 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: |
167 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL; | 109 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_CRITICAL; |
168 break; | 110 break; |
169 } | 111 } |
170 | 112 |
171 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", | 113 UMA_HISTOGRAM_ENUMERATION("Memory.PressureLevel", memory_pressure_level_uma, |
172 memory_pressure_level_uma, | 114 UMA_MEMORY_PRESSURE_LEVEL_COUNT); |
173 NUM_MEMORY_PRESSURE_LEVELS); | |
174 } | 115 } |
175 | 116 |
176 void MemoryPressureMonitor::CheckMemoryPressure() { | 117 void MemoryPressureMonitor::CheckMemoryPressure() { |
177 MemoryPressureListener::MemoryPressureLevel old_pressure = | 118 MemoryPressureListener::MemoryPressureLevel old_pressure = |
178 current_memory_pressure_level_; | 119 current_memory_pressure_level_; |
179 | 120 current_memory_pressure_level_ = |
180 // If we have the kernel low memory observer, we use it's flag instead of our | 121 GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent()); |
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; | |
189 } else { | |
190 current_memory_pressure_level_ = GetMemoryPressureLevelFromFillLevel( | |
191 GetUsedMemoryInPercent(), | |
192 moderate_pressure_threshold_percent_, | |
193 critical_pressure_threshold_percent_); | |
194 | |
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; | |
202 } | |
203 } | |
204 | 122 |
205 // In case there is no memory pressure we do not notify. | 123 // In case there is no memory pressure we do not notify. |
206 if (current_memory_pressure_level_ == | 124 if (current_memory_pressure_level_ == |
207 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { | 125 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { |
208 return; | 126 return; |
209 } | 127 } |
| 128 |
210 if (old_pressure == current_memory_pressure_level_) { | 129 if (old_pressure == current_memory_pressure_level_) { |
211 // If the memory pressure is still at the same level, we notify again for a | 130 // 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 | 131 // critical level. In case of a moderate level repeat however, we only send |
213 // a notification after a certain time has passed. | 132 // a notification after a certain time has passed. |
214 if (current_memory_pressure_level_ == | 133 if (current_memory_pressure_level_ == |
215 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && | 134 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && |
216 ++moderate_pressure_repeat_count_ < | 135 ++moderate_pressure_repeat_count_ < kModerateMemoryPressureCooldown) { |
217 kModerateMemoryPressureCooldown) { | |
218 return; | 136 return; |
219 } | 137 } |
220 } else if (current_memory_pressure_level_ == | 138 } else if (current_memory_pressure_level_ == |
221 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && | 139 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && |
222 old_pressure == | 140 old_pressure == |
223 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 141 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
224 // When we reducing the pressure level from critical to moderate, we | 142 // When we reducing the pressure level from critical to moderate, we |
225 // restart the timeout and do not send another notification. | 143 // restart the timeout and do not send another notification. |
226 moderate_pressure_repeat_count_ = 0; | 144 moderate_pressure_repeat_count_ = 0; |
227 return; | 145 return; |
228 } | 146 } |
229 moderate_pressure_repeat_count_ = 0; | 147 moderate_pressure_repeat_count_ = 0; |
230 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); | 148 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); |
231 } | 149 } |
232 | 150 |
233 // Gets the used ChromeOS memory in percent. | 151 // Gets the used memory in percent. |
234 int MemoryPressureMonitor::GetUsedMemoryInPercent() { | 152 int MemoryPressureMonitor::GetUsedMemoryInPercent() { |
235 base::SystemMemoryInfoKB info; | 153 base::SystemMemoryInfoKB info; |
236 if (!base::GetSystemMemoryInfo(&info)) { | 154 if (!base::GetSystemMemoryInfo(&info)) { |
237 VLOG(1) << "Cannot determine the free memory of the system."; | 155 VLOG(1) << "Cannot determine the free memory of the system."; |
238 return 0; | 156 return 0; |
239 } | 157 } |
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. | |
244 | 158 |
245 // The available memory consists of "real" and virtual (z)ram memory. | 159 // Cast to int64 to avoid integer overflows when calculating the percentage. |
246 // Since swappable memory uses a non pre-deterministic compression and | 160 int64 total_memory = info.total; |
247 // the compression creates its own "dynamic" in the system, it gets | 161 int64 available_memory = info.free; |
248 // de-emphasized by the |kSwapWeight| factor. | |
249 const int kSwapWeight = 4; | |
250 | |
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; | |
253 | |
254 // The kernel internally uses 50MB. | |
255 const int kMinFileMemory = 50 * 1024; | |
256 | |
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; | |
261 | |
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; | |
265 | 162 |
266 DCHECK(available_memory < total_memory); | 163 DCHECK(available_memory < total_memory); |
267 int percentage = ((total_memory - available_memory) * 100) / total_memory; | 164 int percentage = static_cast<int>(((total_memory - available_memory) * 100) / |
| 165 total_memory); |
268 return percentage; | 166 return percentage; |
269 } | 167 } |
270 | 168 |
271 } // namespace chromeos | |
272 } // namespace base | 169 } // namespace base |
OLD | NEW |