#include "content/browser/preloading/preload_serving_metrics.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
#include "content/browser/preloading/prefetch/prefetch_servable_state.h"
#include "content/browser/preloading/preload_serving_metrics_holder.h"
namespace content {
namespace {
#define PAGE_LOAD_HISTOGRAM(name, sample) \
base::UmaHistogramCustomTimes(name, sample, base::Milliseconds(10), \
base::Minutes(10), 100)
#define WITH(prefix, name) base::StrCat({prefix, name})
void RecordMetricsPrefetchMatchAheadOfPrerenderDebug(
const char* prefix,
const PrefetchMatchMetrics& prefetch_match_metrics) {
if (!prefetch_match_metrics.prerender_debug_metrics) {
return;
}
const PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics*
prefetch_ahead_of_prerender_debug_metrics_nullable =
prefetch_match_metrics.prerender_debug_metrics
->prefetch_ahead_of_prerender_debug_metrics.get();
base::UmaHistogramBoolean(WITH(prefix, "PrefetchMatchMetrics.ExistsPaop"),
prefetch_ahead_of_prerender_debug_metrics_nullable);
if (!prefetch_ahead_of_prerender_debug_metrics_nullable) {
return;
}
const PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics&
prefetch_ahead_of_prerender_debug_metrics =
*prefetch_ahead_of_prerender_debug_metrics_nullable;
base::UmaHistogramEnumeration(
WITH(prefix, "PrefetchMatchMetrics.ExistsPaopThen.PrefetchStatus"),
prefetch_ahead_of_prerender_debug_metrics.prefetch_status);
base::UmaHistogramSparse(
WITH(prefix,
"PrefetchMatchMetrics.ExistsPaopThen.ServableStateAndMatcherAction"),
GetCodeOfPrefetchServableStateAndPrefetchMatchResolverActionForDebug(
prefetch_ahead_of_prerender_debug_metrics.servable_state,
prefetch_ahead_of_prerender_debug_metrics.match_resolver_action));
base::UmaHistogramSparse(
WITH(prefix,
"PrefetchMatchMetrics.ExistsPaopThen."
"PotentialCandidateServingResultAndServableStateAndMatcherAction"),
GetCodeOfPotentialCandidateServingResultAndServableStateAndMatcherAction(
prefetch_match_metrics
.GetPrefetchPotentialCandidateServingResultLast(),
prefetch_ahead_of_prerender_debug_metrics.servable_state,
prefetch_ahead_of_prerender_debug_metrics.match_resolver_action));
base::UmaHistogramCounts100(
WITH(prefix, "PrefetchMatchMetrics.ExistsPaopThen.QueueSize"),
prefetch_ahead_of_prerender_debug_metrics.queue_size);
base::UmaHistogramCounts100(
WITH(prefix, "PrefetchMatchMetrics.ExistsPaopThen.QueueIndexPlus1"),
prefetch_ahead_of_prerender_debug_metrics.queue_index.value_or(-1) + 1);
}
void RecordMetricsInternal(
const PreloadServingMetrics& metrics,
const char* prefix,
bool is_prerender_initial_navigation,
const PrefetchMatchMetrics* prefetch_match_metrics_force_use = nullptr) {
base::UmaHistogramCounts100(WITH(prefix, "PrefetchMatchMetrics.Count"),
metrics.prefetch_match_metrics_list.size());
[&]() {
const PrefetchMatchMetrics* prefetch_match_metrics_nullable;
if (prefetch_match_metrics_force_use) {
prefetch_match_metrics_nullable = prefetch_match_metrics_force_use;
} else {
prefetch_match_metrics_nullable =
metrics.GetMeaningfulPrefetchMatchMetrics();
}
const bool is_potential_match =
prefetch_match_metrics_nullable &&
prefetch_match_metrics_nullable->IsPotentialMatch();
const bool is_potential_match_with_ahead_of_prerender =
is_potential_match &&
prefetch_match_metrics_nullable
->prefetch_potential_candidate_serving_result_ahead_of_prerender
.has_value();
base::UmaHistogramBoolean(
WITH(prefix, "PrefetchMatchMetrics.IsPotentialMatch"),
is_potential_match);
if (is_prerender_initial_navigation) {
base::UmaHistogramBoolean(
WITH(prefix,
"PrefetchMatchMetrics.IsPotentialMatch.WithAheadOfPrerender"),
is_potential_match_with_ahead_of_prerender);
}
if (!is_potential_match) {
return;
}
auto& prefetch_match_metrics = *prefetch_match_metrics_nullable;
base::UmaHistogramCounts100(WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen."
"NumberOfInitialCandidates"),
prefetch_match_metrics.n_initial_candidates);
base::UmaHistogramCounts100(
WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen."
"NumberOfInitialCandidatesBlockUntilHead"),
prefetch_match_metrics.n_initial_candidates_block_until_head);
const bool is_actual_match = prefetch_match_metrics.IsActualMatch();
base::UmaHistogramBoolean(
WITH(prefix, "PrefetchMatchMetrics.PotentialMatchThen.IsActualMatch"),
is_actual_match);
CHECK(prefetch_match_metrics
.prefetch_potential_candidate_serving_result_last.has_value());
base::UmaHistogramEnumeration(
WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen."
"PotentialCandidateServingResult.Last"),
prefetch_match_metrics.prefetch_potential_candidate_serving_result_last
.value());
base::TimeDelta prefetch_match_duration =
prefetch_match_metrics.time_match_end -
prefetch_match_metrics.time_match_start;
base::UmaHistogramMediumTimes(
WITH(prefix, "PrefetchMatchMetrics.PotentialMatchThen.MatchDuration"),
prefetch_match_duration);
if (is_actual_match) {
base::UmaHistogramMediumTimes(
WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen.MatchDuration."
"ForActualMatch"),
prefetch_match_duration);
} else {
base::UmaHistogramMediumTimes(
WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen.MatchDuration."
"ForNotActualMatch"),
prefetch_match_duration);
}
if (is_actual_match) {
CHECK(prefetch_match_metrics.prefetch_container_metrics
->time_added_to_prefetch_service.has_value());
base::TimeDelta time_from_prefetch_container_added_to_match_start =
prefetch_match_metrics.time_match_start -
prefetch_match_metrics.prefetch_container_metrics
->time_added_to_prefetch_service.value();
CHECK_LE(base::Seconds(0),
time_from_prefetch_container_added_to_match_start);
base::UmaHistogramMediumTimes(
WITH(prefix,
"PrefetchMatchMetrics.ActualMatchThen."
"TimeFromPrefetchContainerAddedToMatchStart"),
time_from_prefetch_container_added_to_match_start);
}
if (is_prerender_initial_navigation &&
prefetch_match_metrics
.prefetch_potential_candidate_serving_result_ahead_of_prerender
.has_value()) {
base::UmaHistogramEnumeration(
WITH(prefix,
"PrefetchMatchMetrics.PotentialMatchThen.WithAheadOfPrerender."
"PotentialCandidateServingResult"),
prefetch_match_metrics
.prefetch_potential_candidate_serving_result_ahead_of_prerender
.value());
}
}();
}
}
PrefetchContainerMetrics::PrefetchContainerMetrics() = default;
PrefetchContainerMetrics::~PrefetchContainerMetrics() = default;
PrefetchContainerMetrics::PrefetchContainerMetrics(
const PrefetchContainerMetrics&) = default;
PrefetchContainerMetrics& PrefetchContainerMetrics::operator=(
const PrefetchContainerMetrics&) = default;
PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics::
PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics()
:
match_resolver_action(PrefetchMatchResolverAction(
PrefetchMatchResolverAction::ActionKind::kDrop,
PrefetchContainer::LoadState::kFailed,
std::nullopt)) {}
PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics::
~PrefetchMatchPrefetchAheadOfPrerenderDebugMetrics() = default;
PrefetchMatchPrerenderDebugMetrics::PrefetchMatchPrerenderDebugMetrics() =
default;
PrefetchMatchPrerenderDebugMetrics::~PrefetchMatchPrerenderDebugMetrics() =
default;
PrefetchMatchMetrics::PrefetchMatchMetrics() = default;
PrefetchMatchMetrics::~PrefetchMatchMetrics() = default;
bool PrefetchMatchMetrics::IsPotentialMatch() const {
return n_initial_candidates > 0;
}
bool PrefetchMatchMetrics::IsActualMatch() const {
return !!prefetch_container_metrics;
}
PrefetchPotentialCandidateServingResult
PrefetchMatchMetrics::GetPrefetchPotentialCandidateServingResultLast() const {
return prefetch_potential_candidate_serving_result_last.value_or(
PrefetchPotentialCandidateServingResult::kNotServedNoCandidates);
}
const PrefetchMatchMetrics*
PreloadServingMetrics::GetMeaningfulPrefetchMatchMetrics() const {
if (prefetch_match_metrics_list.size() == 0) {
return nullptr;
}
CHECK(prefetch_match_metrics_list[0]);
if (prefetch_match_metrics_list.size() == 1) {
return prefetch_match_metrics_list[0].get();
}
CHECK(prefetch_match_metrics_list[1]);
if (prefetch_match_metrics_list[0]->expected_service_worker_state ==
PrefetchServiceWorkerState::kControlled &&
prefetch_match_metrics_list[1]->expected_service_worker_state ==
PrefetchServiceWorkerState::kDisallowed &&
prefetch_match_metrics_list[1]->IsPotentialMatch()) {
return prefetch_match_metrics_list[1].get();
} else {
return prefetch_match_metrics_list[0].get();
}
}
void PreloadServingMetrics::RecordMetricsForNonPrerenderNavigationCommitted()
const {
RecordMetricsInternal(*this, "PreloadServingMetrics.ForNavigationCommitted.",
false);
if (prerender_initial_preload_serving_metrics) {
RecordMetricsInternal(
*prerender_initial_preload_serving_metrics,
"PreloadServingMetrics.ForPrerenderInitialNavigationUsed.",
true);
}
}
void PreloadServingMetrics::RecordMetricsForPrerenderInitialNavigationFailed()
const {
CHECK(PreloadServingMetricsCapsule::IsFeatureEnabled());
RecordMetricsInternal(
*this, "PreloadServingMetrics.ForPrerenderInitialNavigationFailed.",
true);
auto& metrics = *this;
[&]() {
const PrefetchMatchMetrics* meaningful_prefetch_match_metrics =
metrics.GetMeaningfulPrefetchMatchMetrics();
const bool is_potential_match =
meaningful_prefetch_match_metrics &&
meaningful_prefetch_match_metrics->IsPotentialMatch();
if (!is_potential_match) {
return;
}
auto& prefetch_match_metrics = *meaningful_prefetch_match_metrics;
base::TimeDelta prefetch_match_duration =
prefetch_match_metrics.time_match_end -
prefetch_match_metrics.time_match_start;
if (prefetch_match_duration >= base::Milliseconds(10000)) {
RecordMetricsInternal(
*this,
"PreloadServingMetrics.ForPrerenderInitialNavigationFailed."
"WithMatchDurationGe10000.",
true);
}
}();
if (metrics.is_prerender_aborted_by_prerender_url_loader_throttle) {
if (metrics.prefetch_match_metrics_list.size() > 0) {
const PrefetchMatchMetrics& prefetch_match_metrics =
*metrics.prefetch_match_metrics_list[0].get();
const char* prefix =
"PreloadServingMetrics.ForPrerenderInitialNavigationFailed."
"FallbackAborted.Match0.";
RecordMetricsPrefetchMatchAheadOfPrerenderDebug(prefix,
prefetch_match_metrics);
RecordMetricsInternal(
*this, prefix,
true,
&prefetch_match_metrics);
}
if (metrics.prefetch_match_metrics_list.size() > 1) {
const PrefetchMatchMetrics& prefetch_match_metrics =
*metrics.prefetch_match_metrics_list[1].get();
const char* prefix =
"PreloadServingMetrics.ForPrerenderInitialNavigationFailed."
"FallbackAborted.Match1.";
RecordMetricsPrefetchMatchAheadOfPrerenderDebug(prefix,
prefetch_match_metrics);
RecordMetricsInternal(
*this, prefix,
true,
&prefetch_match_metrics);
}
}
}
void PreloadServingMetrics::RecordFirstContentfulPaint(
base::TimeDelta corrected_first_contentful_paint) const {
const bool is_prerender_used = !!prerender_initial_preload_serving_metrics;
const PrefetchMatchMetrics* meaningful_prefetch_match_metrics =
GetMeaningfulPrefetchMatchMetrics();
const bool is_prefetch_actual_match =
meaningful_prefetch_match_metrics &&
meaningful_prefetch_match_metrics->IsActualMatch();
const char* suffix;
if (is_prerender_used) {
suffix = ".WithPrerender";
} else if (is_prefetch_actual_match) {
suffix = ".WithPrefetch";
} else {
suffix = ".WithoutPreload";
}
PAGE_LOAD_HISTOGRAM(
base::StrCat({"PreloadServingMetrics.PageLoad.Clients.PaintTiming."
"NavigationToFirstContentfulPaint",
suffix}),
corrected_first_contentful_paint);
}
PreloadServingMetrics::PreloadServingMetrics() {
CHECK(PreloadServingMetricsCapsule::IsFeatureEnabled());
}
PreloadServingMetrics::~PreloadServingMetrics() = default;
std::unique_ptr<PreloadServingMetricsCapsule>
PreloadServingMetricsCapsuleImpl::TakeFromNavigationHandle(
NavigationHandle& navigation_handle) {
CHECK(PreloadServingMetricsCapsule::IsFeatureEnabled());
return base::WrapUnique(new PreloadServingMetricsCapsuleImpl(
PreloadServingMetricsHolder::GetOrCreateForNavigationHandle(
navigation_handle)
->Take()));
}
PreloadServingMetricsCapsuleImpl::PreloadServingMetricsCapsuleImpl(
std::unique_ptr<PreloadServingMetrics> preload_serving_metrics)
: preload_serving_metrics_(std::move(preload_serving_metrics)) {
CHECK(PreloadServingMetricsCapsule::IsFeatureEnabled());
}
PreloadServingMetricsCapsuleImpl::~PreloadServingMetricsCapsuleImpl() = default;
void PreloadServingMetricsCapsuleImpl::
RecordMetricsForNonPrerenderNavigationCommitted() const {
preload_serving_metrics_->RecordMetricsForNonPrerenderNavigationCommitted();
}
void PreloadServingMetricsCapsuleImpl::RecordFirstContentfulPaint(
base::TimeDelta corrected_first_contentful_paint) const {
preload_serving_metrics_->RecordFirstContentfulPaint(
std::move(corrected_first_contentful_paint));
}
}