blob: 43e2198c236f73013cd17a6337061295c9c1f9e2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/service_worker/service_worker_metrics.h"
#include <limits>
#include <string>
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_client.h"
#include "third_party/blink/public/common/service_worker/embedded_worker_status.h"
namespace content {
namespace {
const char* StartSituationToSuffix(
ServiceWorkerMetrics::StartSituation situation) {
// Don't change these returned strings. They are written (in hashed form) into
// logs.
switch (situation) {
case ServiceWorkerMetrics::StartSituation::UNKNOWN:
NOTREACHED();
case ServiceWorkerMetrics::StartSituation::DURING_STARTUP:
return ".DuringStartup";
case ServiceWorkerMetrics::StartSituation::NEW_PROCESS:
return ".NewProcess";
case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS:
return ".ExistingUnreadyProcess";
case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS:
return ".ExistingReadyProcess";
}
NOTREACHED() << static_cast<int>(situation);
}
// TODO(falken): Remove this when the associated UMA are removed.
const char* StartSituationToDeprecatedSuffix(
ServiceWorkerMetrics::StartSituation situation) {
// Don't change this returned string. It is written (in hashed form) into
// logs.
switch (situation) {
case ServiceWorkerMetrics::StartSituation::UNKNOWN:
NOTREACHED();
case ServiceWorkerMetrics::StartSituation::DURING_STARTUP:
return "_DuringStartup";
case ServiceWorkerMetrics::StartSituation::NEW_PROCESS:
return "_NewProcess";
case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS:
return "_ExistingUnreadyProcess";
case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS:
return "_ExistingReadyProcess";
}
NOTREACHED() << static_cast<int>(situation);
}
const char* EventTypeToSuffix(ServiceWorkerMetrics::EventType event_type) {
// Don't change these returned strings. They are written (in hashed form) into
// logs.
switch (event_type) {
case ServiceWorkerMetrics::EventType::ACTIVATE:
return "_ACTIVATE";
case ServiceWorkerMetrics::EventType::INSTALL:
return "_INSTALL";
case ServiceWorkerMetrics::EventType::SYNC:
return "_SYNC";
case ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK:
return "_NOTIFICATION_CLICK";
case ServiceWorkerMetrics::EventType::PUSH:
return "_PUSH";
case ServiceWorkerMetrics::EventType::MESSAGE:
return "_MESSAGE";
case ServiceWorkerMetrics::EventType::NOTIFICATION_CLOSE:
return "_NOTIFICATION_CLOSE";
case ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME:
return "_FETCH_MAIN_FRAME";
case ServiceWorkerMetrics::EventType::FETCH_SUB_FRAME:
return "_FETCH_SUB_FRAME";
case ServiceWorkerMetrics::EventType::FETCH_SHARED_WORKER:
return "_FETCH_SHARED_WORKER";
case ServiceWorkerMetrics::EventType::FETCH_SUB_RESOURCE:
return "_FETCH_SUB_RESOURCE";
case ServiceWorkerMetrics::EventType::UNKNOWN:
return "_UNKNOWN";
case ServiceWorkerMetrics::EventType::FETCH_WAITUNTIL:
return "_FETCH_WAITUNTIL";
case ServiceWorkerMetrics::EventType::EXTERNAL_REQUEST:
return "_EXTERNAL_REQUEST";
case ServiceWorkerMetrics::EventType::PAYMENT_REQUEST:
return "_PAYMENT_REQUEST";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT:
return "_BACKGROUND_FETCH_ABORT";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK:
return "_BACKGROUND_FETCH_CLICK";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL:
return "_BACKGROUND_FETCH_FAIL";
case ServiceWorkerMetrics::EventType::NAVIGATION_HINT:
return "_NAVIGATION_HINT";
case ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT:
return "_CAN_MAKE_PAYMENT";
case ServiceWorkerMetrics::EventType::ABORT_PAYMENT:
return "_ABORT_PAYMENT";
case ServiceWorkerMetrics::EventType::COOKIE_CHANGE:
return "_COOKIE_CHANGE";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS:
return "_BACKGROUND_FETCH_SUCCESS";
case ServiceWorkerMetrics::EventType::PERIODIC_SYNC:
return "_PERIODIC_SYNC";
case ServiceWorkerMetrics::EventType::CONTENT_DELETE:
return "_CONTENT_DELETE";
case ServiceWorkerMetrics::EventType::PUSH_SUBSCRIPTION_CHANGE:
return "_PUSH_SUBSCRIPTION_CHANGE";
case ServiceWorkerMetrics::EventType::FETCH_FENCED_FRAME:
return "_FETCH_FENCED_FRAME";
case ServiceWorkerMetrics::EventType::BYPASS_MAIN_RESOURCE:
return "_BYPASS_MAIN_RESOURCE";
case ServiceWorkerMetrics::EventType::SKIP_EMPTY_FETCH_HANDLER:
return "_SKIP_EMPTY_FETCH_HANDLER";
case ServiceWorkerMetrics::EventType::
BYPASS_ONLY_IF_SERVICE_WORKER_NOT_STARTED:
return "_BYPASS_ONLY_IF_SERVICE_WORKER_NOT_STARTED";
case ServiceWorkerMetrics::EventType::WARM_UP:
return "_WARM_UP";
case ServiceWorkerMetrics::EventType::STATIC_ROUTER:
return "_STATIC_ROUTING";
}
return "_UNKNOWN";
}
} // namespace
const char* ServiceWorkerMetrics::EventTypeToString(EventType event_type) {
switch (event_type) {
case EventType::ACTIVATE:
return "Activate";
case EventType::INSTALL:
return "Install";
case EventType::SYNC:
return "Sync";
case EventType::NOTIFICATION_CLICK:
return "Notification Click";
case EventType::NOTIFICATION_CLOSE:
return "Notification Close";
case EventType::PUSH:
return "Push";
case EventType::MESSAGE:
return "Message";
case EventType::FETCH_MAIN_FRAME:
return "Fetch Main Frame";
case EventType::FETCH_SUB_FRAME:
return "Fetch Sub Frame";
case EventType::FETCH_SHARED_WORKER:
return "Fetch Shared Worker";
case EventType::FETCH_SUB_RESOURCE:
return "Fetch Subresource";
case EventType::UNKNOWN:
return "Unknown";
case EventType::FETCH_WAITUNTIL:
return "Fetch WaitUntil";
case EventType::EXTERNAL_REQUEST:
return "External Request";
case EventType::PAYMENT_REQUEST:
return "Payment Request";
case EventType::BACKGROUND_FETCH_ABORT:
return "Background Fetch Abort";
case EventType::BACKGROUND_FETCH_CLICK:
return "Background Fetch Click";
case EventType::BACKGROUND_FETCH_FAIL:
return "Background Fetch Fail";
case EventType::NAVIGATION_HINT:
return "Navigation Hint";
case EventType::CAN_MAKE_PAYMENT:
return "Can Make Payment";
case EventType::ABORT_PAYMENT:
return "Abort Payment";
case EventType::COOKIE_CHANGE:
return "Cookie Change";
case EventType::BACKGROUND_FETCH_SUCCESS:
return "Background Fetch Success";
case EventType::PERIODIC_SYNC:
return "Periodic Sync";
case EventType::CONTENT_DELETE:
return "Content Delete";
case EventType::PUSH_SUBSCRIPTION_CHANGE:
return "Push Subscription Change";
case EventType::FETCH_FENCED_FRAME:
return "Fetch Fenced Frame";
case ServiceWorkerMetrics::EventType::BYPASS_MAIN_RESOURCE:
return "_BYPASS_MAIN_RESOURCE";
case ServiceWorkerMetrics::EventType::SKIP_EMPTY_FETCH_HANDLER:
return "Skip Empty Fetch Handler";
case ServiceWorkerMetrics::EventType::
BYPASS_ONLY_IF_SERVICE_WORKER_NOT_STARTED:
return "Bypass Only If ServiceWorker Is Not Started";
case ServiceWorkerMetrics::EventType::WARM_UP:
return "Warm Up";
case ServiceWorkerMetrics::EventType::STATIC_ROUTER:
return "Static Routing";
}
NOTREACHED() << "Got unexpected event type: " << static_cast<int>(event_type);
}
const char* ServiceWorkerMetrics::StartSituationToString(
StartSituation start_situation) {
switch (start_situation) {
case StartSituation::UNKNOWN:
return "Unknown";
case StartSituation::DURING_STARTUP:
return "During startup";
case StartSituation::NEW_PROCESS:
return "New process";
case StartSituation::EXISTING_UNREADY_PROCESS:
return "Existing unready process";
case StartSituation::EXISTING_READY_PROCESS:
return "Existing ready process";
}
NOTREACHED() << "Got unexpected start situation: "
<< static_cast<int>(start_situation);
}
void ServiceWorkerMetrics::CountReadResponseResult(
ServiceWorkerMetrics::ReadResponseResult result) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.ReadResponseResult",
result, NUM_READ_RESPONSE_RESULT_TYPES);
}
void ServiceWorkerMetrics::CountWriteResponseResult(
ServiceWorkerMetrics::WriteResponseResult result) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.WriteResponseResult",
result, NUM_WRITE_RESPONSE_RESULT_TYPES);
}
void ServiceWorkerMetrics::RecordStartInstalledWorkerStatus(
blink::ServiceWorkerStatusCode status,
EventType purpose) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status);
base::UmaHistogramEnumeration(
base::StrCat({"ServiceWorker.StartWorker.StatusByPurpose",
EventTypeToSuffix(purpose)}),
status);
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Purpose", purpose);
if (status == blink::ServiceWorkerStatusCode::kErrorTimeout) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Timeout.StartPurpose",
purpose);
}
}
void ServiceWorkerMetrics::RecordRunAfterStartWorkerStatus(
blink::EmbeddedWorkerStatus running_status,
EventType purpose) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.MaybeStartWorker.RunningStatus",
running_status);
base::UmaHistogramEnumeration(
base::StrCat({"ServiceWorker.MaybeStartWorker.RunningStatusByPurpose",
EventTypeToSuffix(purpose)}),
running_status);
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.MaybeStartWorker.Purpose", purpose);
}
void ServiceWorkerMetrics::RecordStartWorkerTime(base::TimeDelta time,
bool is_installed,
StartSituation start_situation,
EventType purpose) {
if (is_installed) {
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time",
time);
base::UmaHistogramMediumTimes(
base::StrCat({"ServiceWorker.StartWorker.Time",
StartSituationToDeprecatedSuffix(start_situation)}),
time);
base::UmaHistogramMediumTimes(
base::StrCat({"ServiceWorker.StartWorker.Time",
StartSituationToDeprecatedSuffix(start_situation),
EventTypeToSuffix(purpose)}),
time);
base::UmaHistogramMediumTimes(
base::StrCat(
{"ServiceWorker.StartWorker.Time_Any", EventTypeToSuffix(purpose)}),
time);
} else {
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartNewWorker.Time",
time);
}
}
void ServiceWorkerMetrics::RecordActivateEventStatus(
blink::ServiceWorkerStatusCode status,
bool is_shutdown) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus", status);
if (is_shutdown) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_InShutdown",
status);
} else {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_NotInShutdown",
status);
}
}
void ServiceWorkerMetrics::RecordInstallEventStatus(
blink::ServiceWorkerStatusCode status,
uint32_t fetch_count) {
base::UmaHistogramEnumeration("ServiceWorker.InstallEvent.All.Status",
status);
base::UmaHistogramCounts1000("ServiceWorker.InstallEvent.All.FetchCount",
fetch_count);
if (fetch_count > 0) {
base::UmaHistogramEnumeration("ServiceWorker.InstallEvent.WithFetch.Status",
status);
}
}
void ServiceWorkerMetrics::RecordEventDuration(EventType event,
base::TimeDelta time,
bool was_handled,
uint32_t fetch_count) {
switch (event) {
case EventType::ACTIVATE:
base::UmaHistogramMediumTimes("ServiceWorker.ActivateEvent.Time2", time);
break;
case EventType::INSTALL:
base::UmaHistogramMediumTimes("ServiceWorker.InstallEvent.All.Time",
time);
if (fetch_count) {
base::UmaHistogramMediumTimes(
"ServiceWorker.InstallEvent.WithFetch.Time", time);
}
break;
case EventType::MESSAGE:
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.ExtendableMessageEvent.Time", time);
break;
case EventType::FETCH_MAIN_FRAME:
case EventType::FETCH_SUB_FRAME:
case EventType::FETCH_SHARED_WORKER:
case EventType::FETCH_SUB_RESOURCE:
case EventType::FETCH_FENCED_FRAME:
if (was_handled) {
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.FetchEvent.HasResponse.Time", time);
} else {
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.FetchEvent.Fallback.Time", time);
}
break;
case EventType::PAYMENT_REQUEST:
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.PaymentRequestEvent.Time", time);
break;
case EventType::CAN_MAKE_PAYMENT:
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.CanMakePaymentEvent.Time", time);
break;
case EventType::ABORT_PAYMENT:
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.AbortPaymentEvent.Time", time);
break;
case EventType::PERIODIC_SYNC:
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.PeriodicBackgroundSyncEvent.Time", time);
break;
case EventType::SYNC:
case EventType::NOTIFICATION_CLICK:
case EventType::PUSH:
case EventType::NOTIFICATION_CLOSE:
case EventType::FETCH_WAITUNTIL:
case EventType::EXTERNAL_REQUEST:
case EventType::BACKGROUND_FETCH_ABORT:
case EventType::BACKGROUND_FETCH_CLICK:
case EventType::BACKGROUND_FETCH_FAIL:
case EventType::COOKIE_CHANGE:
case EventType::BACKGROUND_FETCH_SUCCESS:
case EventType::CONTENT_DELETE:
case EventType::PUSH_SUBSCRIPTION_CHANGE:
case EventType::WARM_UP:
// Do nothing: the warm up should not be sent as an event.
break;
case EventType::NAVIGATION_HINT:
// The navigation hint should not be sent as an event.
case EventType::BYPASS_MAIN_RESOURCE:
// The bypass main resource should not be sent as an event.
case EventType::SKIP_EMPTY_FETCH_HANDLER:
// The skip empty fetch handler should not be sent as an event.
case EventType::BYPASS_ONLY_IF_SERVICE_WORKER_NOT_STARTED:
// The bypass_only_if_service_worker_not_started should not be sent as an
// event.
case EventType::STATIC_ROUTER:
// Static Routing should not be sent as an event.
case EventType::UNKNOWN:
NOTREACHED() << "Invalid event type";
}
}
void ServiceWorkerMetrics::RecordFetchEventStatus(
bool is_main_resource,
blink::ServiceWorkerStatusCode status) {
if (is_main_resource) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.MainResource.Status",
status);
} else {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.Subresource.Status",
status);
}
}
void ServiceWorkerMetrics::RecordStartWorkerTiming(const StartTimes& times,
StartSituation situation) {
// Bail if the timings across processes weren't consistent.
if (!base::TimeTicks::IsHighResolution() ||
!base::TimeTicks::IsConsistentAcrossProcesses()) {
RecordStartWorkerTimingClockConsistency(
CrossProcessTimeDelta::INACCURATE_CLOCK);
return;
}
if (times.remote_start_worker_received < times.local_start_worker_sent ||
times.local_end < times.remote_script_evaluation_end) {
RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NEGATIVE);
return;
}
RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NORMAL);
// Total duration.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartTiming.Duration",
times.local_end - times.local_start);
base::UmaHistogramMediumTimes(
base::StrCat({"ServiceWorker.StartTiming.Duration",
StartSituationToSuffix(situation)}),
times.local_end - times.local_start);
// SentStartWorker milestone.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.StartToSentStartWorker",
times.local_start_worker_sent - times.local_start);
// ReceivedStartWorker milestone.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.StartToReceivedStartWorker",
times.remote_start_worker_received - times.local_start);
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.SentStartWorkerToReceivedStartWorker",
times.remote_start_worker_received - times.local_start_worker_sent);
// ScriptEvaluationStart milestone.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.StartToScriptEvaluationStart",
times.remote_script_evaluation_start - times.local_start);
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.ReceivedStartWorkerToScriptEvaluationStart",
times.remote_script_evaluation_start -
times.remote_start_worker_received);
// ScriptEvaluationEnd milestone.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.StartToScriptEvaluationEnd",
times.remote_script_evaluation_end - times.local_start);
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.ScriptEvaluationStartToScriptEvaluationEnd",
times.remote_script_evaluation_end -
times.remote_script_evaluation_start);
// End milestone.
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES(
"ServiceWorker.StartTiming.ScriptEvaluationEndToEnd",
times.local_end - times.remote_script_evaluation_end);
}
void ServiceWorkerMetrics::RecordStartWorkerTimingClockConsistency(
CrossProcessTimeDelta type) {
UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartTiming.ClockConsistency", type);
}
void ServiceWorkerMetrics::RecordSkipServiceWorkerOnNavigation(
bool skip_service_worker) {
static bool is_first_call = true;
if (is_first_call) {
is_first_call = false;
if (!GetContentClient()->browser()->IsBrowserStartupComplete()) {
base::UmaHistogramBoolean(
"ServiceWorker.OnBrowserStartup.SkipServiceWorkerOnFirstNavigation",
skip_service_worker);
}
} else {
if (GetContentClient()->browser()->IsBrowserStartupComplete()) {
base::UmaHistogramBoolean(
"ServiceWorker.SkipCallingFindRegistrationForClientUrl",
skip_service_worker);
}
}
}
void ServiceWorkerMetrics::RecordFindRegistrationForClientUrlTime(
base::TimeDelta time) {
static bool is_first_call = true;
if (is_first_call) {
is_first_call = false;
if (!GetContentClient()->browser()->IsBrowserStartupComplete()) {
base::UmaHistogramMediumTimes(
"ServiceWorker.OnBrowserStartup.FirstFindRegistrationForClientUrl."
"Time",
time);
}
} else {
if (GetContentClient()->browser()->IsBrowserStartupComplete()) {
base::UmaHistogramMediumTimes(
"ServiceWorker.FindRegistrationForClientUrl.Time", time);
}
}
}
} // namespace content