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

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

Issue 1587273002: [Mac] Collect real-time memory pressure stats, in an energy-efficient way (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix nits. Created 3 years, 10 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 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;
lgrey 2017/02/22 21:33:40 Should we account for the error? (`now + kUMATickS
shrike 2017/02/22 23:56:25 OK.
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698