| OLD | NEW |
| 1 // Copyright 2015 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_mac.h" | 5 #include "base/memory/memory_pressure_monitor_mac.h" |
| 6 | 6 |
| 7 #include <CoreFoundation/CoreFoundation.h> |
| 8 |
| 7 #include <dlfcn.h> | 9 #include <dlfcn.h> |
| 8 #include <stddef.h> | 10 #include <stddef.h> |
| 9 #include <sys/sysctl.h> | 11 #include <sys/sysctl.h> |
| 10 | 12 |
| 11 #include <cmath> | 13 #include <cmath> |
| 12 | 14 |
| 13 #include "base/bind.h" | 15 #include "base/bind.h" |
| 14 #include "base/logging.h" | 16 #include "base/logging.h" |
| 15 #include "base/mac/mac_util.h" | 17 #include "base/mac/mac_util.h" |
| 16 | 18 |
| 17 // Redeclare for partial 10.9 availability. | 19 // Redeclare for partial 10.9 availability. |
| 18 DISPATCH_EXPORT const struct dispatch_source_type_s | 20 DISPATCH_EXPORT const struct dispatch_source_type_s |
| 19 _dispatch_source_type_memorypressure; | 21 _dispatch_source_type_memorypressure; |
| 20 | 22 |
| 21 namespace { | 23 namespace { |
| 22 static const int kUMATickSize = 5; | 24 static const int kUMATickSize = 5; |
| 23 } // namespace | 25 } // namespace |
| 24 | 26 |
| 25 namespace base { | 27 namespace base { |
| 26 namespace mac { | 28 namespace mac { |
| 27 | 29 |
| 28 MemoryPressureListener::MemoryPressureLevel | 30 MemoryPressureListener::MemoryPressureLevel |
| 29 MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure( | 31 MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( |
| 30 int mac_memory_pressure) { | 32 int mac_memory_pressure_level) { |
| 31 switch (mac_memory_pressure) { | 33 switch (mac_memory_pressure_level) { |
| 32 case DISPATCH_MEMORYPRESSURE_NORMAL: | 34 case DISPATCH_MEMORYPRESSURE_NORMAL: |
| 33 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | 35 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
| 34 case DISPATCH_MEMORYPRESSURE_WARN: | 36 case DISPATCH_MEMORYPRESSURE_WARN: |
| 35 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; | 37 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; |
| 36 case DISPATCH_MEMORYPRESSURE_CRITICAL: | 38 case DISPATCH_MEMORYPRESSURE_CRITICAL: |
| 37 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; | 39 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; |
| 38 } | 40 } |
| 39 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | 41 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
| 40 } | 42 } |
| 41 | 43 |
| 44 void MemoryPressureMonitor::OnRunLoopExit(CFRunLoopObserverRef observer, |
| 45 CFRunLoopActivity activity, |
| 46 void* info) { |
| 47 MemoryPressureMonitor* self = static_cast<MemoryPressureMonitor*>(info); |
| 48 self->UpdatePressureLevelOnRunLoopExit(); |
| 49 } |
| 50 |
| 42 MemoryPressureMonitor::MemoryPressureMonitor() | 51 MemoryPressureMonitor::MemoryPressureMonitor() |
| 43 : memory_level_event_source_(dispatch_source_create( | 52 : memory_level_event_source_(dispatch_source_create( |
| 44 DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, | 53 DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, |
| 45 0, | 54 0, |
| 46 DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL | | 55 DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL | |
| 47 DISPATCH_MEMORYPRESSURE_NORMAL, | 56 DISPATCH_MEMORYPRESSURE_NORMAL, |
| 48 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))), | 57 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))), |
| 49 dispatch_callback_( | 58 dispatch_callback_( |
| 50 base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), | 59 base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), |
| 51 last_statistic_report_(CFAbsoluteTimeGetCurrent()), | 60 last_statistic_report_time_(CFAbsoluteTimeGetCurrent()), |
| 52 last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), | 61 last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), |
| 53 reporting_error_(0) { | 62 subtick_seconds_(0) { |
| 54 if (memory_level_event_source_.get() != nullptr) { | 63 // Attach an event handler to the memory pressure event source. |
| 64 if (memory_level_event_source_.get()) { |
| 55 dispatch_source_set_event_handler(memory_level_event_source_, ^{ | 65 dispatch_source_set_event_handler(memory_level_event_source_, ^{ |
| 56 OnMemoryPressureChanged(memory_level_event_source_.get(), | 66 OnMemoryPressureChanged(memory_level_event_source_.get(), |
| 57 dispatch_callback_); | 67 dispatch_callback_); |
| 58 }); | 68 }); |
| 69 |
| 70 // Start monitoring the event source. |
| 59 dispatch_resume(memory_level_event_source_); | 71 dispatch_resume(memory_level_event_source_); |
| 60 } | 72 } |
| 73 |
| 74 // Create a CFRunLoopObserver to check the memory pressure at the end of |
| 75 // every pass through the event loop (modulo kUMATickSize). |
| 76 CFRunLoopObserverContext observer_context = {0, this, NULL, NULL, NULL}; |
| 77 |
| 78 exit_observer_.reset( |
| 79 CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, true, 0, |
| 80 OnRunLoopExit, &observer_context)); |
| 81 |
| 82 CFRunLoopRef run_loop = CFRunLoopGetCurrent(); |
| 83 CFRunLoopAddObserver(run_loop, exit_observer_, kCFRunLoopCommonModes); |
| 84 CFRunLoopAddObserver(run_loop, exit_observer_, |
| 85 kMessageLoopExclusiveRunLoopMode); |
| 86 } |
| 87 |
| 88 MemoryPressureMonitor::~MemoryPressureMonitor() { |
| 89 // Detach from the run loop. |
| 90 CFRunLoopRef run_loop = CFRunLoopGetCurrent(); |
| 91 CFRunLoopRemoveObserver(run_loop, exit_observer_, kCFRunLoopCommonModes); |
| 92 CFRunLoopRemoveObserver(run_loop, exit_observer_, |
| 93 kMessageLoopExclusiveRunLoopMode); |
| 94 |
| 95 // Remove the memory pressure event source. |
| 96 if (memory_level_event_source_.get()) { |
| 97 dispatch_source_cancel(memory_level_event_source_); |
| 98 } |
| 61 } | 99 } |
| 62 | 100 |
| 63 MemoryPressureMonitor::~MemoryPressureMonitor() { | 101 int MemoryPressureMonitor::GetMacMemoryPressureLevel() { |
| 64 if (memory_level_event_source_.get() != nullptr) | 102 // Get the raw memory pressure level from macOS. |
| 65 dispatch_source_cancel(memory_level_event_source_); | 103 int mac_memory_pressure_level; |
| 104 size_t length = sizeof(int); |
| 105 sysctlbyname("kern.memorystatus_vm_pressure_level", |
| 106 &mac_memory_pressure_level, &length, nullptr, 0); |
| 107 |
| 108 return mac_memory_pressure_level; |
| 109 } |
| 110 |
| 111 void MemoryPressureMonitor::UpdatePressureLevel() { |
| 112 // Get the current macOS pressure level and convert to the corresponding |
| 113 // Chrome pressure level. |
| 114 int mac_memory_pressure_level = GetMacMemoryPressureLevel(); |
| 115 MemoryPressureListener::MemoryPressureLevel new_pressure_level = |
| 116 MemoryPressureLevelForMacMemoryPressureLevel(mac_memory_pressure_level); |
| 117 |
| 118 // Compute the number of "ticks" spent at |last_pressure_level_| (since the |
| 119 // last report sent to UMA). |
| 120 CFTimeInterval now = CFAbsoluteTimeGetCurrent(); |
| 121 CFTimeInterval time_since_last_report = now - last_statistic_report_time_; |
| 122 last_statistic_report_time_ = now; |
| 123 |
| 124 double accumulated_time = time_since_last_report + subtick_seconds_; |
| 125 int ticks_to_report = static_cast<int>(accumulated_time / kUMATickSize); |
| 126 // Save for later the seconds that didn't make it into a full tick. |
| 127 subtick_seconds_ = std::fmod(accumulated_time, kUMATickSize); |
| 128 |
| 129 // Round the tick count up on a pressure level change to ensure we capture it. |
| 130 bool pressure_level_changed = (new_pressure_level != last_pressure_level_); |
| 131 if (pressure_level_changed && ticks_to_report < 1) { |
| 132 ticks_to_report = 1; |
| 133 subtick_seconds_ = 0; |
| 134 } |
| 135 |
| 136 // Send elapsed ticks to UMA. |
| 137 if (ticks_to_report >= 1) { |
| 138 RecordMemoryPressure(last_pressure_level_, ticks_to_report); |
| 139 } |
| 140 |
| 141 // Save the now-current memory pressure level. |
| 142 last_pressure_level_ = new_pressure_level; |
| 143 } |
| 144 |
| 145 void MemoryPressureMonitor::UpdatePressureLevelOnRunLoopExit() { |
| 146 // Wait until it's time to check the pressure level. |
| 147 CFTimeInterval now = CFAbsoluteTimeGetCurrent(); |
| 148 if (now >= next_run_loop_update_time_) { |
| 149 UpdatePressureLevel(); |
| 150 |
| 151 // Update again in kUMATickSize seconds. We can update at any frequency, |
| 152 // but because we're only checking memory pressure levels for UMA there's |
| 153 // no need to update more frequently than we're keeping statistics on. |
| 154 next_run_loop_update_time_ = now + kUMATickSize - subtick_seconds_; |
| 155 } |
| 156 } |
| 157 |
| 158 // Static. |
| 159 int MemoryPressureMonitor::GetSecondsPerUMATick() { |
| 160 return kUMATickSize; |
| 66 } | 161 } |
| 67 | 162 |
| 68 MemoryPressureListener::MemoryPressureLevel | 163 MemoryPressureListener::MemoryPressureLevel |
| 69 MemoryPressureMonitor::GetCurrentPressureLevel() { | 164 MemoryPressureMonitor::GetCurrentPressureLevel() { |
| 70 int mac_memory_pressure; | 165 UpdatePressureLevel(); |
| 71 size_t length = sizeof(int); | 166 return last_pressure_level_; |
| 72 sysctlbyname("kern.memorystatus_vm_pressure_level", &mac_memory_pressure, | |
| 73 &length, nullptr, 0); | |
| 74 MemoryPressureListener::MemoryPressureLevel memory_pressure_level = | |
| 75 MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure); | |
| 76 bool pressure_level_changed = false; | |
| 77 if (last_pressure_level_ != memory_pressure_level) { | |
| 78 pressure_level_changed = true; | |
| 79 } | |
| 80 SendStatisticsIfNecessary(pressure_level_changed); | |
| 81 last_pressure_level_ = memory_pressure_level; | |
| 82 return memory_pressure_level; | |
| 83 } | 167 } |
| 84 | 168 |
| 85 void MemoryPressureMonitor::OnMemoryPressureChanged( | 169 void MemoryPressureMonitor::OnMemoryPressureChanged( |
| 86 dispatch_source_s* event_source, | 170 dispatch_source_s* event_source, |
| 87 const MemoryPressureMonitor::DispatchCallback& dispatch_callback) { | 171 const MemoryPressureMonitor::DispatchCallback& dispatch_callback) { |
| 88 int mac_memory_pressure = dispatch_source_get_data(event_source); | 172 // Get the Chrome-equvialent memory pressure level. |
| 173 int mac_memory_pressure_level = dispatch_source_get_data(event_source); |
| 89 MemoryPressureListener::MemoryPressureLevel memory_pressure_level = | 174 MemoryPressureListener::MemoryPressureLevel memory_pressure_level = |
| 90 MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure); | 175 MemoryPressureLevelForMacMemoryPressureLevel(mac_memory_pressure_level); |
| 91 bool pressure_level_changed = false; | 176 |
| 92 if (last_pressure_level_ != memory_pressure_level) { | 177 // Run the callback that's waiting on memory pressure change notifications. |
| 93 pressure_level_changed = true; | 178 // Note that we don't bother with updating |last_pressure_level_| or |
| 94 } | 179 // causing memory pressure stats to be sent to UMA. Memory pressure change |
| 95 SendStatisticsIfNecessary(pressure_level_changed); | 180 // notifications are delayed on the Mac, so the current actual memory pressure |
| 96 last_pressure_level_ = memory_pressure_level; | 181 // level may be different than the incoming pressure level from the event. |
| 182 // |
| 183 // In general we don't want to take action (such as freeing memory) on |
| 184 // memory pressure change events, but that's how the current system is |
| 185 // designed. Given that it's incorrect to act on either stale or current |
| 186 // pressure level info, it's not clear which level is better to send. For |
| 187 // now stick with how it's been implemented to date, which is to send the |
| 188 // stale value. |
| 97 if (memory_pressure_level != | 189 if (memory_pressure_level != |
| 98 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) | 190 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) |
| 99 dispatch_callback.Run(memory_pressure_level); | 191 dispatch_callback.Run(memory_pressure_level); |
| 100 } | 192 } |
| 101 | 193 |
| 102 void MemoryPressureMonitor::SendStatisticsIfNecessary( | |
| 103 bool pressure_level_changed) { | |
| 104 CFTimeInterval now = CFAbsoluteTimeGetCurrent(); | |
| 105 CFTimeInterval since_last_report = now - last_statistic_report_; | |
| 106 last_statistic_report_ = now; | |
| 107 | |
| 108 double accumulated_time = since_last_report + reporting_error_; | |
| 109 int ticks_to_report = static_cast<int>(accumulated_time / kUMATickSize); | |
| 110 reporting_error_ = std::fmod(accumulated_time, kUMATickSize); | |
| 111 | |
| 112 // Round up on change to ensure we capture it | |
| 113 if (pressure_level_changed && ticks_to_report < 1) { | |
| 114 ticks_to_report = 1; | |
| 115 reporting_error_ = 0; | |
| 116 } | |
| 117 | |
| 118 if (ticks_to_report >= 1) | |
| 119 RecordMemoryPressure(last_pressure_level_, ticks_to_report); | |
| 120 } | |
| 121 | |
| 122 void MemoryPressureMonitor::SetDispatchCallback( | 194 void MemoryPressureMonitor::SetDispatchCallback( |
| 123 const DispatchCallback& callback) { | 195 const DispatchCallback& callback) { |
| 124 dispatch_callback_ = callback; | 196 dispatch_callback_ = callback; |
| 125 } | 197 } |
| 126 | 198 |
| 127 } // namespace mac | 199 } // namespace mac |
| 128 } // namespace base | 200 } // namespace base |
| OLD | NEW |