Chromium Code Reviews| 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_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 |
| OLD | NEW |