blob: b6b75d607ba2b5b78a84eadb8a5fcaf70a60f62b [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/graph/process_node_impl.h"
#include "base/logging.h"
#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
#include "chrome/browser/performance_manager/graph/page_node_impl.h"
namespace performance_manager {
ProcessNodeImpl::ProcessNodeImpl(
const resource_coordinator::CoordinationUnitID& id,
Graph* graph)
: CoordinationUnitInterface(id, graph) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProcessNodeImpl::~ProcessNodeImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Make as if we're transitioning to the null PID before we die to clear this
// instance from the PID map.
if (process_id_ != base::kNullProcessId)
graph()->BeforeProcessPidChange(this, base::kNullProcessId);
for (auto* child_frame : frame_coordination_units_)
child_frame->RemoveProcessNode(this);
}
void ProcessNodeImpl::AddFrame(FrameNodeImpl* frame_cu) {
const bool inserted = frame_coordination_units_.insert(frame_cu).second;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(inserted);
if (frame_cu->lifecycle_state() ==
resource_coordinator::mojom::LifecycleState::kFrozen)
IncrementNumFrozenFrames();
}
void ProcessNodeImpl::SetCPUUsage(double cpu_usage) {
SetProperty(resource_coordinator::mojom::PropertyType::kCPUUsage,
cpu_usage * 1000);
}
void ProcessNodeImpl::SetExpectedTaskQueueingDuration(
base::TimeDelta duration) {
SetProperty(
resource_coordinator::mojom::PropertyType::kExpectedTaskQueueingDuration,
duration.InMilliseconds());
}
void ProcessNodeImpl::SetLaunchTime(base::Time launch_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(launch_time_.is_null());
launch_time_ = launch_time;
}
void ProcessNodeImpl::SetMainThreadTaskLoadIsLow(
bool main_thread_task_load_is_low) {
SetProperty(
resource_coordinator::mojom::PropertyType::kMainThreadTaskLoadIsLow,
main_thread_task_load_is_low);
}
void ProcessNodeImpl::SetPID(base::ProcessId pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Either this is the initial process associated with this process CU,
// or it's a subsequent process. In the latter case, there must have been
// an exit status associated with the previous process.
DCHECK(process_id_ == base::kNullProcessId || exit_status_.has_value());
graph()->BeforeProcessPidChange(this, pid);
process_id_ = pid;
// Clear launch time and exit status for the previous process (if any).
launch_time_ = base::Time();
exit_status_.reset();
// Also clear the measurement data (if any), as it references the previous
// process.
private_footprint_kb_ = 0;
cumulative_cpu_usage_ = base::TimeDelta();
SetProperty(resource_coordinator::mojom::PropertyType::kPID, pid);
}
void ProcessNodeImpl::SetProcessExitStatus(int32_t exit_status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
exit_status_ = exit_status;
}
void ProcessNodeImpl::OnRendererIsBloated() {
SendEvent(resource_coordinator::mojom::Event::kRendererIsBloated);
}
const std::set<FrameNodeImpl*>& ProcessNodeImpl::GetFrameNodes() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return frame_coordination_units_;
}
// There is currently not a direct relationship between processes and
// pages. However, frames are children of both processes and frames, so we
// find all of the pages that are reachable from the process's child
// frames.
std::set<PageNodeImpl*> ProcessNodeImpl::GetAssociatedPageCoordinationUnits()
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::set<PageNodeImpl*> page_cus;
for (auto* frame_cu : frame_coordination_units_) {
if (auto* page_cu = frame_cu->GetPageNode())
page_cus.insert(page_cu);
}
return page_cus;
}
void ProcessNodeImpl::OnFrameLifecycleStateChanged(
FrameNodeImpl* frame_cu,
resource_coordinator::mojom::LifecycleState old_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(base::ContainsKey(frame_coordination_units_, frame_cu));
DCHECK_NE(old_state, frame_cu->lifecycle_state());
if (old_state == resource_coordinator::mojom::LifecycleState::kFrozen)
DecrementNumFrozenFrames();
else if (frame_cu->lifecycle_state() ==
resource_coordinator::mojom::LifecycleState::kFrozen)
IncrementNumFrozenFrames();
}
void ProcessNodeImpl::OnEventReceived(
resource_coordinator::mojom::Event event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
observer.OnProcessEventReceived(this, event);
}
void ProcessNodeImpl::OnPropertyChanged(
const resource_coordinator::mojom::PropertyType property_type,
int64_t value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers())
observer.OnProcessPropertyChanged(this, property_type, value);
}
void ProcessNodeImpl::RemoveFrame(FrameNodeImpl* frame_cu) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(base::ContainsKey(frame_coordination_units_, frame_cu));
frame_coordination_units_.erase(frame_cu);
if (frame_cu->lifecycle_state() ==
resource_coordinator::mojom::LifecycleState::kFrozen)
DecrementNumFrozenFrames();
}
void ProcessNodeImpl::DecrementNumFrozenFrames() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
--num_frozen_frames_;
DCHECK_GE(num_frozen_frames_, 0);
}
void ProcessNodeImpl::IncrementNumFrozenFrames() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++num_frozen_frames_;
DCHECK_LE(num_frozen_frames_,
static_cast<int>(frame_coordination_units_.size()));
if (num_frozen_frames_ ==
static_cast<int>(frame_coordination_units_.size())) {
for (auto& observer : observers())
observer.OnAllFramesInProcessFrozen(this);
}
}
} // namespace performance_manager