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

Side by Side Diff: base/memory/memory_pressure_monitor_linux.cc

Issue 1250093006: Added memory pressure monitor for linux. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Re-upload with less restrictive copy detection. Created 5 years, 5 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
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_linux.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 { 14 namespace nix {
19 15
20 namespace { 16 namespace {
21 17
22 // The time between memory pressure checks. While under critical pressure, this 18 // The time between memory pressure checks. While under critical pressure, this
23 // is also the timer to repeat cleanup attempts. 19 // is also the timer to repeat cleanup attempts.
24 const int kMemoryPressureIntervalMs = 1000; 20 const int kMemoryPressureIntervalMs = 1000;
25 21
26 // The time which should pass between two moderate memory pressure calls. 22 // The time which should pass between two moderate memory pressure calls.
27 const int kModerateMemoryPressureCooldownMs = 10000; 23 const int kModerateMemoryPressureCooldownMs = 10000;
28 24
29 // Number of event polls before the next moderate pressure event can be sent. 25 // Number of event polls before the next moderate pressure event can be sent.
30 const int kModerateMemoryPressureCooldown = 26 const int kModerateMemoryPressureCooldown =
31 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; 27 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
32 28
33 // Threshold constants to emit pressure events. 29 // Threshold constants to emit pressure events.
34 const int kNormalMemoryPressureModerateThresholdPercent = 60; 30 const int kNormalMemoryPressureModerateThresholdPercent = 60;
35 const int kNormalMemoryPressureCriticalThresholdPercent = 95; 31 const int kNormalMemoryPressureCriticalThresholdPercent = 95;
36 const int kAggressiveMemoryPressureModerateThresholdPercent = 35;
37 const int kAggressiveMemoryPressureCriticalThresholdPercent = 70;
38 32
39 // The possible state for memory pressure level. The values should be in line 33 // The possible state for memory pressure level. The values should be in line
40 // with values in MemoryPressureListener::MemoryPressureLevel and should be 34 // with values in MemoryPressureListener::MemoryPressureLevel and should be
41 // updated if more memory pressure levels are introduced. 35 // updated if more memory pressure levels are introduced.
42 enum MemoryPressureLevelUMA { 36 enum MemoryPressureLevelUMA {
43 MEMORY_PRESSURE_LEVEL_NONE = 0, 37 UMA_MEMORY_PRESSURE_LEVEL_NONE = 0,
chrisha 2015/07/24 13:25:45 If we're going to change the name here, I'd make i
proberge 2015/07/24 14:37:37 It's to be consistent with the enum in memory_pres
44 MEMORY_PRESSURE_LEVEL_MODERATE, 38 UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1,
45 MEMORY_PRESSURE_LEVEL_CRITICAL, 39 UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
46 NUM_MEMORY_PRESSURE_LEVELS 40 // This must be the last value in the enum.
41 UMA_MEMORY_PRESSURE_LEVEL_COUNT,
47 }; 42 };
48 43
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. 44 // Converts free percent of memory into a memory pressure value.
77 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel( 45 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
78 int actual_fill_level, 46 int actual_fill_level) {
79 int moderate_threshold, 47 if (actual_fill_level < kNormalMemoryPressureModerateThresholdPercent)
80 int critical_threshold) {
81 if (actual_fill_level < moderate_threshold)
82 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; 48 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
83 return actual_fill_level < critical_threshold 49 return actual_fill_level < kNormalMemoryPressureCriticalThresholdPercent
84 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE 50 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
85 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; 51 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
86 } 52 }
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 53 } // namespace
104 54
105 MemoryPressureMonitor::MemoryPressureMonitor( 55 MemoryPressureMonitor::MemoryPressureMonitor()
106 MemoryPressureThresholds thresholds)
107 : current_memory_pressure_level_( 56 : current_memory_pressure_level_(
108 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), 57 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
109 moderate_pressure_repeat_count_(0), 58 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) { 59 weak_ptr_factory_(this) {
116 StartObserving(); 60 StartObserving();
117 LOG_IF(ERROR, !low_mem_file_.is_valid()) << "Cannot open kernel listener";
118 } 61 }
119 62
120 MemoryPressureMonitor::~MemoryPressureMonitor() { 63 MemoryPressureMonitor::~MemoryPressureMonitor() {
121 StopObserving(); 64 StopObserving();
122 } 65 }
123 66
124 void MemoryPressureMonitor::ScheduleEarlyCheck() { 67 void MemoryPressureMonitor::ScheduleEarlyCheck() {
125 ThreadTaskRunnerHandle::Get()->PostTask( 68 ThreadTaskRunnerHandle::Get()->PostTask(
126 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, 69 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure,
127 weak_ptr_factory_.GetWeakPtr())); 70 weak_ptr_factory_.GetWeakPtr()));
128 } 71 }
129 72
130 MemoryPressureListener::MemoryPressureLevel 73 MemoryPressureListener::MemoryPressureLevel
131 MemoryPressureMonitor::GetCurrentPressureLevel() const { 74 MemoryPressureMonitor::GetCurrentPressureLevel() const {
132 return current_memory_pressure_level_; 75 return current_memory_pressure_level_;
133 } 76 }
134 77
135 // static 78 // static
136 MemoryPressureMonitor* MemoryPressureMonitor::Get() { 79 MemoryPressureMonitor* MemoryPressureMonitor::Get() {
137 return static_cast<MemoryPressureMonitor*>( 80 return static_cast<MemoryPressureMonitor*>(
138 base::MemoryPressureMonitor::Get()); 81 base::MemoryPressureMonitor::Get());
139 } 82 }
140 83
141 void MemoryPressureMonitor::StartObserving() { 84 void MemoryPressureMonitor::StartObserving() {
142 timer_.Start(FROM_HERE, 85 timer_.Start(
143 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), 86 FROM_HERE, TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs),
144 Bind(&MemoryPressureMonitor:: 87 Bind(&MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics,
145 CheckMemoryPressureAndRecordStatistics, 88 weak_ptr_factory_.GetWeakPtr()));
146 weak_ptr_factory_.GetWeakPtr()));
147 } 89 }
148 90
149 void MemoryPressureMonitor::StopObserving() { 91 void MemoryPressureMonitor::StopObserving() {
150 // If StartObserving failed, StopObserving will still get called. 92 // If StartObserving failed, StopObserving will still get called.
151 timer_.Stop(); 93 timer_.Stop();
152 } 94 }
153 95
154 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { 96 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
155 CheckMemoryPressure(); 97 CheckMemoryPressure();
156 98
157 // Record UMA histogram statistics for the current memory pressure level. 99 // Record UMA histogram statistics for the current memory pressure level.
158 MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE); 100 MemoryPressureLevelUMA memory_pressure_level_uma(
101 UMA_MEMORY_PRESSURE_LEVEL_NONE);
159 switch (current_memory_pressure_level_) { 102 switch (current_memory_pressure_level_) {
160 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: 103 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
161 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE; 104 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_NONE;
162 break; 105 break;
163 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: 106 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
164 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE; 107 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
165 break; 108 break;
166 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: 109 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
167 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL; 110 memory_pressure_level_uma = UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
168 break; 111 break;
169 } 112 }
170 113
171 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", 114 UMA_HISTOGRAM_ENUMERATION("Memory.PressureLevel", memory_pressure_level_uma,
172 memory_pressure_level_uma, 115 UMA_MEMORY_PRESSURE_LEVEL_COUNT);
173 NUM_MEMORY_PRESSURE_LEVELS);
174 } 116 }
175 117
176 void MemoryPressureMonitor::CheckMemoryPressure() { 118 void MemoryPressureMonitor::CheckMemoryPressure() {
177 MemoryPressureListener::MemoryPressureLevel old_pressure = 119 MemoryPressureListener::MemoryPressureLevel old_pressure =
178 current_memory_pressure_level_; 120 current_memory_pressure_level_;
179 121
180 // If we have the kernel low memory observer, we use it's flag instead of our 122 // 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. 123 // 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 124 // 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. 125 // 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 126 // TODO(skuhne): Add some testing framework here to see how close the kernel
185 // and the internal functions are. 127 // and the internal functions are.
chrisha 2015/07/24 13:25:45 These comments probably don't apply to the Linux o
proberge 2015/07/24 14:37:37 Done.
186 if (low_mem_file_.is_valid() && IsLowMemoryCondition(low_mem_file_.get())) { 128 current_memory_pressure_level_ =
187 current_memory_pressure_level_ = 129 GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent());
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 130
205 // In case there is no memory pressure we do not notify. 131 // In case there is no memory pressure we do not notify.
206 if (current_memory_pressure_level_ == 132 if (current_memory_pressure_level_ ==
207 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { 133 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
208 return; 134 return;
209 } 135 }
136
210 if (old_pressure == current_memory_pressure_level_) { 137 if (old_pressure == current_memory_pressure_level_) {
211 // If the memory pressure is still at the same level, we notify again for a 138 // 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 139 // critical level. In case of a moderate level repeat however, we only send
213 // a notification after a certain time has passed. 140 // a notification after a certain time has passed.
214 if (current_memory_pressure_level_ == 141 if (current_memory_pressure_level_ ==
215 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && 142 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
216 ++moderate_pressure_repeat_count_ < 143 ++moderate_pressure_repeat_count_ < kModerateMemoryPressureCooldown) {
217 kModerateMemoryPressureCooldown) {
218 return; 144 return;
219 } 145 }
220 } else if (current_memory_pressure_level_ == 146 } else if (current_memory_pressure_level_ ==
221 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && 147 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
222 old_pressure == 148 old_pressure ==
223 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { 149 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
224 // When we reducing the pressure level from critical to moderate, we 150 // When we reducing the pressure level from critical to moderate, we
225 // restart the timeout and do not send another notification. 151 // restart the timeout and do not send another notification.
226 moderate_pressure_repeat_count_ = 0; 152 moderate_pressure_repeat_count_ = 0;
227 return; 153 return;
228 } 154 }
229 moderate_pressure_repeat_count_ = 0; 155 moderate_pressure_repeat_count_ = 0;
230 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); 156 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_);
231 } 157 }
232 158
233 // Gets the used ChromeOS memory in percent. 159 // Gets the used memory in percent.
234 int MemoryPressureMonitor::GetUsedMemoryInPercent() { 160 int MemoryPressureMonitor::GetUsedMemoryInPercent() {
235 base::SystemMemoryInfoKB info; 161 base::SystemMemoryInfoKB info;
236 if (!base::GetSystemMemoryInfo(&info)) { 162 if (!base::GetSystemMemoryInfo(&info)) {
237 VLOG(1) << "Cannot determine the free memory of the system."; 163 VLOG(1) << "Cannot determine the free memory of the system.";
238 return 0; 164 return 0;
239 } 165 }
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 166
245 // The available memory consists of "real" and virtual (z)ram memory. 167 // Cast to int64 to avoid integer overflows when calculating the percentage.
246 // Since swappable memory uses a non pre-deterministic compression and 168 int64 total_memory = info.total;
247 // the compression creates its own "dynamic" in the system, it gets 169 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;
chrisha 2015/07/24 13:25:45 Is this not also true on Linux? ChromeOS is linux
proberge 2015/07/24 14:37:37 My methodology could be wrong (writing large files
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 170
266 DCHECK(available_memory < total_memory); 171 DCHECK(available_memory < total_memory);
267 int percentage = ((total_memory - available_memory) * 100) / total_memory; 172 int percentage = static_cast<int>(((total_memory - available_memory) * 100) /
173 total_memory);
174
268 return percentage; 175 return percentage;
269 } 176 }
270 177
271 } // namespace chromeos 178 } // namespace nix
272 } // namespace base 179 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698