#include "extensions/browser/api/web_request/web_request_time_tracker.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "extensions/buildflags/buildflags.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
ExtensionWebRequestTimeTracker::RequestTimeLog::RequestTimeLog() = default;
ExtensionWebRequestTimeTracker::RequestTimeLog::~RequestTimeLog() = default;
ExtensionWebRequestTimeTracker::ExtensionWebRequestTimeTracker() = default;
ExtensionWebRequestTimeTracker::~ExtensionWebRequestTimeTracker() = default;
void ExtensionWebRequestTimeTracker::LogRequestStartTime(
int64_t request_id,
const base::TimeTicks& start_time,
bool has_listener,
bool has_extra_headers_listener) {
auto iter = request_time_logs_.find(request_id);
if (iter != request_time_logs_.end()) {
return;
}
RequestTimeLog& log = request_time_logs_[request_id];
log.request_start_time = start_time;
log.has_listener = has_listener;
log.has_extra_headers_listener = has_extra_headers_listener;
}
void ExtensionWebRequestTimeTracker::LogBeforeRequestDispatchTime(
int64_t request_id,
base::TimeTicks dispatch_time) {
auto iter = request_time_logs_.find(request_id);
CHECK(iter != request_time_logs_.end());
iter->second.before_request_listener_dispatch_time = dispatch_time;
}
void ExtensionWebRequestTimeTracker::LogBeforeRequestCompletionTime(
int64_t request_id,
base::TimeTicks completion_time) {
auto iter = request_time_logs_.find(request_id);
if (iter == request_time_logs_.end()) {
return;
}
iter->second.before_request_listener_completion_time = completion_time;
}
void ExtensionWebRequestTimeTracker::LogBeforeRequestDNRStartTime(
int64_t request_id,
base::TimeTicks start_time) {
auto iter = request_time_logs_.find(request_id);
CHECK(iter != request_time_logs_.end());
iter->second.before_request_dnr_start_time = start_time;
}
void ExtensionWebRequestTimeTracker::LogBeforeRequestDNRCompletionTime(
int64_t request_id,
base::TimeTicks completion_time) {
auto iter = request_time_logs_.find(request_id);
CHECK(iter != request_time_logs_.end());
iter->second.before_request_dnr_completion_time = completion_time;
}
void ExtensionWebRequestTimeTracker::LogRequestEndTime(
int64_t request_id,
const base::TimeTicks& end_time) {
auto iter = request_time_logs_.find(request_id);
if (iter == request_time_logs_.end()) {
return;
}
AnalyzeLogRequest(iter->second, end_time);
request_time_logs_.erase(iter);
}
void ExtensionWebRequestTimeTracker::AnalyzeLogRequest(
const RequestTimeLog& log,
const base::TimeTicks& end_time) {
base::TimeDelta request_duration = end_time - log.request_start_time;
if (log.has_listener) {
UMA_HISTOGRAM_TIMES("Extensions.WebRequest.TotalRequestTime",
request_duration);
}
if (log.has_extra_headers_listener) {
UMA_HISTOGRAM_TIMES("Extensions.WebRequest.TotalExtraHeadersRequestTime",
request_duration);
}
if (log.block_duration.is_zero()) {
return;
}
UMA_HISTOGRAM_TIMES("Extensions.WebRequest.TotalBlockingRequestTime",
request_duration);
UMA_HISTOGRAM_TIMES("Extensions.NetworkDelay", log.block_duration);
constexpr auto kMinRequestTimeToCare = base::Milliseconds(10);
if (request_duration >= kMinRequestTimeToCare) {
const int percentage =
base::ClampRound(log.block_duration / request_duration * 100);
UMA_HISTOGRAM_PERCENTAGE("Extensions.NetworkDelayPercentage", percentage);
}
constexpr int kBucketCount = 50;
if (!log.before_request_listener_dispatch_time.is_null() &&
!log.before_request_listener_completion_time.is_null()) {
base::TimeDelta listener_time =
log.before_request_listener_completion_time -
log.before_request_listener_dispatch_time;
if (log.before_request_dnr_start_time.is_null()) {
UMA_HISTOGRAM_TIMES(
"Extensions.WebRequest.BeforeRequestListenerEvaluationTime."
"WebRequestOnly",
listener_time);
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Extensions.WebRequest."
"BeforeRequestListenerEvaluationTimeInMicroseconds."
"WebRequestOnly",
listener_time, base::Microseconds(1), base::Seconds(30),
kBucketCount);
} else {
UMA_HISTOGRAM_TIMES(
"Extensions.WebRequest.BeforeRequestListenerEvaluationTime."
"WebRequestAndDeclarativeNetRequest",
listener_time);
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Extensions.WebRequest."
"BeforeRequestListenerEvaluationTimeInMicroseconds."
"WebRequestAndDeclarativeNetRequest",
listener_time, base::Microseconds(1), base::Seconds(30),
kBucketCount);
}
}
if (!log.before_request_dnr_completion_time.is_null()) {
DCHECK(!log.before_request_dnr_start_time.is_null());
base::TimeDelta elapsed_time = log.before_request_dnr_completion_time -
log.before_request_dnr_start_time;
UMA_HISTOGRAM_TIMES(
"Extensions.WebRequest."
"BeforeRequestDeclarativeNetRequestEvaluationTime",
elapsed_time);
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Extensions.WebRequest."
"BeforeRequestDeclarativeNetRequestEvaluationTimeInMicroseconds",
elapsed_time, base::Microseconds(1), base::Seconds(30), kBucketCount);
}
}
void ExtensionWebRequestTimeTracker::IncrementTotalBlockTime(
int64_t request_id,
const base::TimeDelta& block_time) {
auto iter = request_time_logs_.find(request_id);
if (iter != request_time_logs_.end()) {
iter->second.block_duration += block_time;
}
}
void ExtensionWebRequestTimeTracker::SetRequestCanceled(int64_t request_id) {
request_time_logs_.erase(request_id);
}
void ExtensionWebRequestTimeTracker::SetRequestRedirected(int64_t request_id) {
request_time_logs_.erase(request_id);
}