blob: b9a371bc720844822e2dcfa96b27074175e27e6e [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/observers/metrics_collector.h"
#include <set>
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/performance_manager/graph/frame_node_impl.h"
#include "chrome/browser/performance_manager/graph/graph.h"
#include "chrome/browser/performance_manager/graph/page_node_impl.h"
#include "chrome/browser/performance_manager/graph/process_node_impl.h"
#include "chrome/browser/performance_manager/resource_coordinator_clock.h"
#include "services/resource_coordinator/public/cpp/coordination_unit_id.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
namespace performance_manager {
// Delay the metrics report from GRC to UMA/UKM for 5 minutes from when the main
// frame navigation is committed.
const base::TimeDelta kMetricsReportDelayTimeout =
base::TimeDelta::FromMinutes(5);
const char kTabFromBackgroundedToFirstFaviconUpdatedUMA[] =
"TabManager.Heuristics.FromBackgroundedToFirstFaviconUpdated";
const char kTabFromBackgroundedToFirstTitleUpdatedUMA[] =
"TabManager.Heuristics.FromBackgroundedToFirstTitleUpdated";
const char kTabFromBackgroundedToFirstNonPersistentNotificationCreatedUMA[] =
"TabManager.Heuristics."
"FromBackgroundedToFirstNonPersistentNotificationCreated";
const int kDefaultFrequencyUkmEQTReported = 5u;
// Gets the number of tabs that are co-resident in all of the render processes
// associated with a |resource_coordinator::CoordinationUnitType::kPage|
// coordination unit.
size_t GetNumCoresidentTabs(const PageNodeImpl* page_cu) {
std::set<NodeBase*> coresident_tabs;
for (auto* process_cu : page_cu->GetAssociatedProcessCoordinationUnits()) {
for (auto* associated_page_cu :
process_cu->GetAssociatedPageCoordinationUnits()) {
coresident_tabs.insert(associated_page_cu);
}
}
// A tab cannot be co-resident with itself.
return coresident_tabs.size() - 1;
}
MetricsCollector::MetricsCollector() {
UpdateWithFieldTrialParams();
}
MetricsCollector::~MetricsCollector() = default;
bool MetricsCollector::ShouldObserve(const NodeBase* coordination_unit) {
return coordination_unit->id().type ==
resource_coordinator::CoordinationUnitType::kFrame ||
coordination_unit->id().type ==
resource_coordinator::CoordinationUnitType::kPage ||
coordination_unit->id().type ==
resource_coordinator::CoordinationUnitType::kProcess;
}
void MetricsCollector::OnNodeCreated(const NodeBase* coordination_unit) {
if (coordination_unit->id().type ==
resource_coordinator::CoordinationUnitType::kPage) {
metrics_report_record_map_.emplace(coordination_unit->id(),
MetricsReportRecord());
}
}
void MetricsCollector::OnBeforeNodeDestroyed(
const NodeBase* coordination_unit) {
if (coordination_unit->id().type ==
resource_coordinator::CoordinationUnitType::kPage) {
metrics_report_record_map_.erase(coordination_unit->id());
ukm_collection_state_map_.erase(coordination_unit->id());
}
}
void MetricsCollector::OnPagePropertyChanged(
const PageNodeImpl* page_cu,
const resource_coordinator::mojom::PropertyType property_type,
int64_t value) {
const auto page_cu_id = page_cu->id();
if (property_type == resource_coordinator::mojom::PropertyType::kVisible) {
if (value) {
// The page becomes visible again, clear all records in order to
// report metrics when page becomes invisible next time.
ResetMetricsReportRecord(page_cu_id);
return;
}
} else if (property_type ==
resource_coordinator::mojom::PropertyType::kUKMSourceId) {
ukm::SourceId ukm_source_id = value;
UpdateUkmSourceIdForPage(page_cu_id, ukm_source_id);
MetricsReportRecord& record =
metrics_report_record_map_.find(page_cu_id)->second;
record.UpdateUKMSourceID(ukm_source_id);
}
}
void MetricsCollector::OnProcessPropertyChanged(
const ProcessNodeImpl* process_cu,
const resource_coordinator::mojom::PropertyType property_type,
int64_t value) {
if (property_type == resource_coordinator::mojom::PropertyType::
kExpectedTaskQueueingDuration) {
for (auto* page_cu : process_cu->GetAssociatedPageCoordinationUnits()) {
if (IsCollectingExpectedQueueingTimeForUkm(page_cu->id())) {
int64_t expected_queueing_time;
if (!page_cu->GetExpectedTaskQueueingDuration(&expected_queueing_time))
continue;
RecordExpectedQueueingTimeForUkm(page_cu->id(), expected_queueing_time);
}
}
}
}
void MetricsCollector::OnFrameEventReceived(
const FrameNodeImpl* frame_cu,
const resource_coordinator::mojom::Event event) {
if (event ==
resource_coordinator::mojom::Event::kNonPersistentNotificationCreated) {
auto* page_cu = frame_cu->GetPageNode();
// Only record metrics while it is backgrounded.
if (!page_cu || page_cu->IsVisible() || !ShouldReportMetrics(page_cu)) {
return;
}
MetricsReportRecord& record =
metrics_report_record_map_.find(page_cu->id())->second;
record.first_non_persistent_notification_created.OnSignalReceived(
frame_cu->IsMainFrame(), page_cu->TimeSinceLastVisibilityChange(),
coordination_unit_graph().ukm_recorder());
}
}
void MetricsCollector::OnPageEventReceived(
const PageNodeImpl* page_cu,
const resource_coordinator::mojom::Event event) {
if (event == resource_coordinator::mojom::Event::kTitleUpdated) {
// Only record metrics while it is backgrounded.
if (page_cu->IsVisible() || !ShouldReportMetrics(page_cu))
return;
MetricsReportRecord& record =
metrics_report_record_map_.find(page_cu->id())->second;
record.first_title_updated.OnSignalReceived(
true, page_cu->TimeSinceLastVisibilityChange(),
coordination_unit_graph().ukm_recorder());
} else if (event == resource_coordinator::mojom::Event::kFaviconUpdated) {
// Only record metrics while it is backgrounded.
if (page_cu->IsVisible() || !ShouldReportMetrics(page_cu))
return;
MetricsReportRecord& record =
metrics_report_record_map_.find(page_cu->id())->second;
record.first_favicon_updated.OnSignalReceived(
true, page_cu->TimeSinceLastVisibilityChange(),
coordination_unit_graph().ukm_recorder());
}
}
bool MetricsCollector::ShouldReportMetrics(const PageNodeImpl* page_cu) {
return page_cu->TimeSinceLastNavigation() > kMetricsReportDelayTimeout;
}
bool MetricsCollector::IsCollectingExpectedQueueingTimeForUkm(
const resource_coordinator::CoordinationUnitID& page_cu_id) {
UkmCollectionState& state = ukm_collection_state_map_[page_cu_id];
return state.ukm_source_id != ukm::kInvalidSourceId &&
++state.num_unreported_eqt_measurements >= frequency_ukm_eqt_reported_;
}
void MetricsCollector::RecordExpectedQueueingTimeForUkm(
const resource_coordinator::CoordinationUnitID& page_cu_id,
int64_t expected_queueing_time) {
UkmCollectionState& state = ukm_collection_state_map_[page_cu_id];
state.num_unreported_eqt_measurements = 0u;
ukm::builders::ResponsivenessMeasurement(state.ukm_source_id)
.SetExpectedTaskQueueingDuration(expected_queueing_time)
.Record(coordination_unit_graph().ukm_recorder());
}
void MetricsCollector::UpdateUkmSourceIdForPage(
const resource_coordinator::CoordinationUnitID& page_cu_id,
ukm::SourceId ukm_source_id) {
UkmCollectionState& state = ukm_collection_state_map_[page_cu_id];
state.ukm_source_id = ukm_source_id;
// Updating the |ukm_source_id| restarts usage collection.
state.num_unreported_eqt_measurements = 0u;
}
void MetricsCollector::UpdateWithFieldTrialParams() {
frequency_ukm_eqt_reported_ = base::GetFieldTrialParamByFeatureAsInt(
ukm::kUkmFeature, "FrequencyUKMExpectedQueueingTime",
kDefaultFrequencyUkmEQTReported);
}
void MetricsCollector::ResetMetricsReportRecord(
resource_coordinator::CoordinationUnitID cu_id) {
DCHECK(metrics_report_record_map_.find(cu_id) !=
metrics_report_record_map_.end());
metrics_report_record_map_.find(cu_id)->second.Reset();
}
MetricsCollector::MetricsReportRecord::MetricsReportRecord() = default;
MetricsCollector::MetricsReportRecord::MetricsReportRecord(
const MetricsReportRecord& other) = default;
void MetricsCollector::MetricsReportRecord::UpdateUKMSourceID(
int64_t ukm_source_id) {
first_favicon_updated.SetUKMSourceID(ukm_source_id);
first_non_persistent_notification_created.SetUKMSourceID(ukm_source_id);
first_title_updated.SetUKMSourceID(ukm_source_id);
}
void MetricsCollector::MetricsReportRecord::Reset() {
first_favicon_updated.Reset();
first_non_persistent_notification_created.Reset();
first_title_updated.Reset();
}
} // namespace performance_manager