OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "content/browser/memory/memory_coordinator_impl.h" | 5 #include "content/browser/memory/memory_coordinator_impl.h" |
6 | 6 |
| 7 #include "base/memory/memory_coordinator_client_registry.h" |
7 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/process/process_handle.h" |
8 #include "base/process/process_metrics.h" | 10 #include "base/process/process_metrics.h" |
9 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
10 #include "base/threading/thread_task_runner_handle.h" | 12 #include "base/threading/thread_task_runner_handle.h" |
11 #include "base/trace_event/trace_event.h" | 13 #include "base/trace_event/trace_event.h" |
12 #include "content/browser/memory/memory_monitor.h" | 14 #include "content/browser/memory/memory_monitor.h" |
13 #include "content/browser/memory/memory_state_updater.h" | 15 #include "content/browser/memory/memory_state_updater.h" |
| 16 #include "content/public/browser/content_browser_client.h" |
14 #include "content/public/browser/notification_service.h" | 17 #include "content/public/browser/notification_service.h" |
15 #include "content/public/browser/notification_types.h" | 18 #include "content/public/browser/notification_types.h" |
16 #include "content/public/browser/render_process_host.h" | 19 #include "content/public/browser/render_process_host.h" |
17 #include "content/public/browser/render_widget_host.h" | 20 #include "content/public/browser/render_widget_host.h" |
18 #include "content/public/common/content_features.h" | 21 #include "content/public/common/content_features.h" |
| 22 #include "mojo/public/cpp/bindings/binding.h" |
19 | 23 |
20 namespace content { | 24 namespace content { |
21 | 25 |
22 namespace { | 26 namespace { |
23 | 27 |
24 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) { | 28 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) { |
25 switch (state) { | 29 switch (state) { |
26 case base::MemoryState::UNKNOWN: | 30 case base::MemoryState::UNKNOWN: |
27 return mojom::MemoryState::UNKNOWN; | 31 return mojom::MemoryState::UNKNOWN; |
28 case base::MemoryState::NORMAL: | 32 case base::MemoryState::NORMAL: |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 break; | 92 break; |
89 } | 93 } |
90 } else { | 94 } else { |
91 NOTREACHED(); | 95 NOTREACHED(); |
92 } | 96 } |
93 #undef RECORD_METRICS | 97 #undef RECORD_METRICS |
94 } | 98 } |
95 | 99 |
96 } // namespace | 100 } // namespace |
97 | 101 |
| 102 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom |
| 103 // for the role of this class. |
| 104 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { |
| 105 public: |
| 106 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, |
| 107 MemoryCoordinatorImpl* coordinator, |
| 108 int render_process_id); |
| 109 ~MemoryCoordinatorHandleImpl() override; |
| 110 |
| 111 // mojom::MemoryCoordinatorHandle: |
| 112 void AddChild(mojom::ChildMemoryCoordinatorPtr child) override; |
| 113 |
| 114 mojom::ChildMemoryCoordinatorPtr& child() { return child_; } |
| 115 mojo::Binding<mojom::MemoryCoordinatorHandle>& binding() { return binding_; } |
| 116 |
| 117 private: |
| 118 MemoryCoordinatorImpl* coordinator_; |
| 119 int render_process_id_; |
| 120 mojom::ChildMemoryCoordinatorPtr child_; |
| 121 mojo::Binding<mojom::MemoryCoordinatorHandle> binding_; |
| 122 |
| 123 DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorHandleImpl); |
| 124 }; |
| 125 |
| 126 MemoryCoordinatorHandleImpl::MemoryCoordinatorHandleImpl( |
| 127 mojom::MemoryCoordinatorHandleRequest request, |
| 128 MemoryCoordinatorImpl* coordinator, |
| 129 int render_process_id) |
| 130 : coordinator_(coordinator), |
| 131 render_process_id_(render_process_id), |
| 132 binding_(this, std::move(request)) { |
| 133 DCHECK(coordinator_); |
| 134 } |
| 135 |
| 136 MemoryCoordinatorHandleImpl::~MemoryCoordinatorHandleImpl() {} |
| 137 |
| 138 void MemoryCoordinatorHandleImpl::AddChild( |
| 139 mojom::ChildMemoryCoordinatorPtr child) { |
| 140 DCHECK(!child_.is_bound()); |
| 141 child_ = std::move(child); |
| 142 coordinator_->OnChildAdded(render_process_id_); |
| 143 } |
| 144 |
98 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl | 145 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl |
99 // as an actual instance. | 146 // as an actual instance. |
100 struct MemoryCoordinatorSingletonTraits | 147 struct MemoryCoordinatorImplSingletonTraits |
101 : public base::LeakySingletonTraits<MemoryCoordinator> { | 148 : public base::LeakySingletonTraits<MemoryCoordinatorImpl> { |
102 static MemoryCoordinator* New() { | 149 static MemoryCoordinatorImpl* New() { |
103 return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(), | 150 return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(), |
104 CreateMemoryMonitor()); | 151 CreateMemoryMonitor()); |
105 } | 152 } |
106 }; | 153 }; |
107 | 154 |
108 // static | 155 // static |
109 MemoryCoordinator* MemoryCoordinator::GetInstance() { | 156 MemoryCoordinatorImpl* MemoryCoordinatorImpl::GetInstance() { |
110 if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator)) | 157 if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator)) |
111 return nullptr; | 158 return nullptr; |
112 return base::Singleton<MemoryCoordinator, | 159 return base::Singleton<MemoryCoordinatorImpl, |
113 MemoryCoordinatorSingletonTraits>::get(); | 160 MemoryCoordinatorImplSingletonTraits>::get(); |
114 } | 161 } |
115 | 162 |
116 MemoryCoordinatorImpl::MemoryCoordinatorImpl( | 163 MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
117 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 164 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
118 std::unique_ptr<MemoryMonitor> memory_monitor) | 165 std::unique_ptr<MemoryMonitor> memory_monitor) |
119 : memory_monitor_(std::move(memory_monitor)), | 166 : delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()), |
| 167 memory_monitor_(std::move(memory_monitor)), |
120 state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) { | 168 state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) { |
121 DCHECK(memory_monitor_.get()); | 169 DCHECK(memory_monitor_.get()); |
122 } | 170 } |
123 | 171 |
124 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {} | 172 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {} |
125 | 173 |
126 void MemoryCoordinatorImpl::Start() { | 174 void MemoryCoordinatorImpl::Start() { |
127 DCHECK(CalledOnValidThread()); | 175 DCHECK(CalledOnValidThread()); |
128 DCHECK(last_state_change_.is_null()); | 176 DCHECK(last_state_change_.is_null()); |
129 | 177 |
130 notification_registrar_.Add( | 178 notification_registrar_.Add( |
131 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 179 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
132 NotificationService::AllBrowserContextsAndSources()); | 180 NotificationService::AllBrowserContextsAndSources()); |
133 last_state_change_ = base::TimeTicks::Now(); | 181 last_state_change_ = base::TimeTicks::Now(); |
134 state_updater_->ScheduleUpdateState(base::TimeDelta()); | 182 state_updater_->ScheduleUpdateState(base::TimeDelta()); |
135 } | 183 } |
136 | 184 |
137 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { | 185 void MemoryCoordinatorImpl::CreateHandle( |
138 // Populate the global state as an initial state of a newly created process. | 186 int render_process_id, |
139 auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); | 187 mojom::MemoryCoordinatorHandleRequest request) { |
140 SetChildMemoryState(render_process_id, new_state); | 188 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( |
| 189 new MemoryCoordinatorHandleImpl(std::move(request), this, |
| 190 render_process_id)); |
| 191 handle->binding().set_connection_error_handler( |
| 192 base::Bind(&MemoryCoordinatorImpl::OnConnectionError, |
| 193 base::Unretained(this), render_process_id)); |
| 194 CreateChildInfoMapEntry(render_process_id, std::move(handle)); |
| 195 } |
| 196 |
| 197 bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id, |
| 198 mojom::MemoryState memory_state) { |
| 199 // Can't set an invalid memory state. |
| 200 if (memory_state == mojom::MemoryState::UNKNOWN) |
| 201 return false; |
| 202 |
| 203 // Can't send a message to a child that doesn't exist. |
| 204 auto iter = children_.find(render_process_id); |
| 205 if (iter == children_.end()) |
| 206 return false; |
| 207 |
| 208 // Can't send a message to a child that isn't bound. |
| 209 if (!iter->second.handle->child().is_bound()) |
| 210 return false; |
| 211 |
| 212 memory_state = OverrideGlobalState(memory_state, iter->second); |
| 213 |
| 214 // A nop doesn't need to be sent, but is considered successful. |
| 215 if (iter->second.memory_state == memory_state) |
| 216 return true; |
| 217 |
| 218 // Can't suspend the given renderer. |
| 219 if (memory_state == mojom::MemoryState::SUSPENDED && |
| 220 !CanSuspendRenderer(render_process_id)) |
| 221 return false; |
| 222 |
| 223 // Update the internal state and send the message. |
| 224 iter->second.memory_state = memory_state; |
| 225 iter->second.handle->child()->OnStateChange(memory_state); |
| 226 return true; |
| 227 } |
| 228 |
| 229 mojom::MemoryState MemoryCoordinatorImpl::GetChildMemoryState( |
| 230 int render_process_id) const { |
| 231 auto iter = children_.find(render_process_id); |
| 232 if (iter == children_.end()) |
| 233 return mojom::MemoryState::UNKNOWN; |
| 234 return iter->second.memory_state; |
| 235 } |
| 236 |
| 237 void MemoryCoordinatorImpl::RecordMemoryPressure( |
| 238 base::MemoryPressureMonitor::MemoryPressureLevel level) { |
| 239 DCHECK(GetGlobalMemoryState() != base::MemoryState::UNKNOWN); |
| 240 int state = static_cast<int>(GetGlobalMemoryState()); |
| 241 switch (level) { |
| 242 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: |
| 243 UMA_HISTOGRAM_ENUMERATION( |
| 244 "Memory.Coordinator.StateOnModerateNotificationReceived", |
| 245 state, base::kMemoryStateMax); |
| 246 break; |
| 247 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: |
| 248 UMA_HISTOGRAM_ENUMERATION( |
| 249 "Memory.Coordinator.StateOnCriticalNotificationReceived", |
| 250 state, base::kMemoryStateMax); |
| 251 break; |
| 252 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: |
| 253 NOTREACHED(); |
| 254 } |
141 } | 255 } |
142 | 256 |
143 base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const { | 257 base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const { |
144 return current_state_; | 258 return current_state_; |
145 } | 259 } |
146 | 260 |
147 base::MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const { | 261 base::MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const { |
148 // SUSPENDED state may not make sense to the browser process. Use THROTTLED | 262 // SUSPENDED state may not make sense to the browser process. Use THROTTLED |
149 // instead when the global state is SUSPENDED. | 263 // instead when the global state is SUSPENDED. |
150 // TODO(bashi): Maybe worth considering another state for the browser. | 264 // TODO(bashi): Maybe worth considering another state for the browser. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
195 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::ChangeStateIfNeeded", | 309 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::ChangeStateIfNeeded", |
196 "prev", MemoryStateToString(prev_state), | 310 "prev", MemoryStateToString(prev_state), |
197 "next", MemoryStateToString(next_state)); | 311 "next", MemoryStateToString(next_state)); |
198 RecordStateChange(prev_state, next_state, | 312 RecordStateChange(prev_state, next_state, |
199 last_state_change_ - prev_last_state_change); | 313 last_state_change_ - prev_last_state_change); |
200 NotifyStateToClients(); | 314 NotifyStateToClients(); |
201 NotifyStateToChildren(); | 315 NotifyStateToChildren(); |
202 return true; | 316 return true; |
203 } | 317 } |
204 | 318 |
| 319 void MemoryCoordinatorImpl::AddChildForTesting( |
| 320 int dummy_render_process_id, mojom::ChildMemoryCoordinatorPtr child) { |
| 321 mojom::MemoryCoordinatorHandlePtr mch; |
| 322 auto request = mojo::GetProxy(&mch); |
| 323 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( |
| 324 new MemoryCoordinatorHandleImpl(std::move(request), this, |
| 325 dummy_render_process_id)); |
| 326 handle->AddChild(std::move(child)); |
| 327 CreateChildInfoMapEntry(dummy_render_process_id, std::move(handle)); |
| 328 } |
| 329 |
| 330 void MemoryCoordinatorImpl::OnConnectionError(int render_process_id) { |
| 331 children_.erase(render_process_id); |
| 332 } |
| 333 |
| 334 bool MemoryCoordinatorImpl::CanSuspendRenderer(int render_process_id) { |
| 335 // If there is no delegate (i.e. unittests), renderers are always suspendable. |
| 336 if (!delegate_) |
| 337 return true; |
| 338 auto* render_process_host = RenderProcessHost::FromID(render_process_id); |
| 339 if (!render_process_host || !render_process_host->IsProcessBackgrounded()) |
| 340 return false; |
| 341 if (render_process_host->GetWorkerRefCount() > 0) |
| 342 return false; |
| 343 return delegate_->CanSuspendBackgroundedRenderer(render_process_id); |
| 344 } |
| 345 |
| 346 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { |
| 347 // Populate the global state as an initial state of a newly created process. |
| 348 auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); |
| 349 SetChildMemoryState(render_process_id, new_state); |
| 350 } |
| 351 |
| 352 mojom::MemoryState MemoryCoordinatorImpl::OverrideGlobalState( |
| 353 mojom::MemoryState memory_state, |
| 354 const ChildInfo& child) { |
| 355 // We don't suspend foreground renderers. Throttle them instead. |
| 356 if (child.is_visible && memory_state == mojom::MemoryState::SUSPENDED) |
| 357 return mojom::MemoryState::THROTTLED; |
| 358 #if defined(OS_ANDROID) |
| 359 // On Android, we throttle background renderers immediately. |
| 360 // TODO(bashi): Create a specialized class of MemoryCoordinator for Android |
| 361 // and move this ifdef to the class. |
| 362 if (!child.is_visible && memory_state == mojom::MemoryState::NORMAL) |
| 363 return mojom::MemoryState::THROTTLED; |
| 364 // TODO(bashi): Suspend background renderers after a certain period of time. |
| 365 #endif // defined(OS_ANDROID) |
| 366 return memory_state; |
| 367 } |
| 368 |
| 369 void MemoryCoordinatorImpl::SetDelegateForTesting( |
| 370 std::unique_ptr<MemoryCoordinatorDelegate> delegate) { |
| 371 CHECK(!delegate_); |
| 372 delegate_ = std::move(delegate); |
| 373 } |
| 374 |
| 375 void MemoryCoordinatorImpl::CreateChildInfoMapEntry( |
| 376 int render_process_id, |
| 377 std::unique_ptr<MemoryCoordinatorHandleImpl> handle) { |
| 378 auto& child_info = children_[render_process_id]; |
| 379 // Process always start with normal memory state. |
| 380 // We'll set renderer's memory state to the current global state when the |
| 381 // corresponding renderer process is ready to communicate. Renderer processes |
| 382 // call AddChild() when they are ready. |
| 383 child_info.memory_state = mojom::MemoryState::NORMAL; |
| 384 child_info.is_visible = true; |
| 385 child_info.handle = std::move(handle); |
| 386 } |
| 387 |
205 void MemoryCoordinatorImpl::NotifyStateToClients() { | 388 void MemoryCoordinatorImpl::NotifyStateToClients() { |
206 auto state = GetCurrentMemoryState(); | 389 auto state = GetCurrentMemoryState(); |
207 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); | 390 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
208 } | 391 } |
209 | 392 |
210 void MemoryCoordinatorImpl::NotifyStateToChildren() { | 393 void MemoryCoordinatorImpl::NotifyStateToChildren() { |
211 auto mojo_state = ToMojomMemoryState(current_state_); | 394 auto mojo_state = ToMojomMemoryState(current_state_); |
212 // It's OK to call SetChildMemoryState() unconditionally because it checks | 395 // It's OK to call SetChildMemoryState() unconditionally because it checks |
213 // whether this state transition is valid. | 396 // whether this state transition is valid. |
214 for (auto& iter : children()) | 397 for (auto& iter : children()) |
(...skipping 22 matching lines...) Expand all Loading... |
237 render_process_host->GetHandle()); | 420 render_process_host->GetHandle()); |
238 metrics->GetWorkingSetKBytes(&working_set); | 421 metrics->GetWorkingSetKBytes(&working_set); |
239 total_private_kb += working_set.priv; | 422 total_private_kb += working_set.priv; |
240 } | 423 } |
241 #endif | 424 #endif |
242 | 425 |
243 RecordMetricsOnStateChange(prev_state, next_state, duration, | 426 RecordMetricsOnStateChange(prev_state, next_state, duration, |
244 total_private_kb / 1024); | 427 total_private_kb / 1024); |
245 } | 428 } |
246 | 429 |
| 430 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} |
| 431 |
| 432 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { |
| 433 // This is a nop, but exists for compatibility with STL containers. |
| 434 } |
| 435 |
| 436 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} |
| 437 |
247 } // namespace content | 438 } // namespace content |
OLD | NEW |