1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/service_worker/service_worker_metrics.h"
6
7 #include <limits>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/metrics/histogram_functions.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/strcat.h"
14 #include "base/strings/string_util.h"
15 #include "base/task/post_task.h"
16 #include "base/time/time.h"
17 #include "content/browser/service_worker/embedded_worker_status.h"
18 #include "content/browser/service_worker/service_worker_context_wrapper.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/common/content_client.h"
22 #include "net/url_request/url_request.h"
23 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
24
25 namespace content {
26
27 namespace {
28
StartSituationToSuffix(ServiceWorkerMetrics::StartSituation situation)29 const char* StartSituationToSuffix(
30 ServiceWorkerMetrics::StartSituation situation) {
31 // Don't change these returned strings. They are written (in hashed form) into
32 // logs.
33 switch (situation) {
34 case ServiceWorkerMetrics::StartSituation::UNKNOWN:
35 NOTREACHED();
36 return ".Unknown";
37 case ServiceWorkerMetrics::StartSituation::DURING_STARTUP:
38 return ".DuringStartup";
39 case ServiceWorkerMetrics::StartSituation::NEW_PROCESS:
40 return ".NewProcess";
41 case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS:
42 return ".ExistingUnreadyProcess";
43 case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS:
44 return ".ExistingReadyProcess";
45 }
46 NOTREACHED() << static_cast<int>(situation);
47 return ".Unknown";
48 }
49
50 // TODO(falken): Remove this when the associated UMA are removed.
StartSituationToDeprecatedSuffix(ServiceWorkerMetrics::StartSituation situation)51 const char* StartSituationToDeprecatedSuffix(
52 ServiceWorkerMetrics::StartSituation situation) {
53 // Don't change this returned string. It is written (in hashed form) into
54 // logs.
55 switch (situation) {
56 case ServiceWorkerMetrics::StartSituation::UNKNOWN:
57 NOTREACHED();
58 return "_Unknown";
59 case ServiceWorkerMetrics::StartSituation::DURING_STARTUP:
60 return "_DuringStartup";
61 case ServiceWorkerMetrics::StartSituation::NEW_PROCESS:
62 return "_NewProcess";
63 case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS:
64 return "_ExistingUnreadyProcess";
65 case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS:
66 return "_ExistingReadyProcess";
67 }
68 NOTREACHED() << static_cast<int>(situation);
69 return "_Unknown";
70 }
71
EventTypeToSuffix(ServiceWorkerMetrics::EventType event_type)72 const char* EventTypeToSuffix(ServiceWorkerMetrics::EventType event_type) {
73 // Don't change these returned strings. They are written (in hashed form) into
74 // logs.
75 switch (event_type) {
76 case ServiceWorkerMetrics::EventType::ACTIVATE:
77 return "_ACTIVATE";
78 case ServiceWorkerMetrics::EventType::INSTALL:
79 return "_INSTALL";
80 case ServiceWorkerMetrics::EventType::SYNC:
81 return "_SYNC";
82 case ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK:
83 return "_NOTIFICATION_CLICK";
84 case ServiceWorkerMetrics::EventType::PUSH:
85 return "_PUSH";
86 case ServiceWorkerMetrics::EventType::MESSAGE:
87 return "_MESSAGE";
88 case ServiceWorkerMetrics::EventType::NOTIFICATION_CLOSE:
89 return "_NOTIFICATION_CLOSE";
90 case ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME:
91 return "_FETCH_MAIN_FRAME";
92 case ServiceWorkerMetrics::EventType::FETCH_SUB_FRAME:
93 return "_FETCH_SUB_FRAME";
94 case ServiceWorkerMetrics::EventType::FETCH_SHARED_WORKER:
95 return "_FETCH_SHARED_WORKER";
96 case ServiceWorkerMetrics::EventType::FETCH_SUB_RESOURCE:
97 return "_FETCH_SUB_RESOURCE";
98 case ServiceWorkerMetrics::EventType::UNKNOWN:
99 return "_UNKNOWN";
100 case ServiceWorkerMetrics::EventType::FETCH_WAITUNTIL:
101 return "_FETCH_WAITUNTIL";
102 case ServiceWorkerMetrics::EventType::EXTERNAL_REQUEST:
103 return "_EXTERNAL_REQUEST";
104 case ServiceWorkerMetrics::EventType::PAYMENT_REQUEST:
105 return "_PAYMENT_REQUEST";
106 case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT:
107 return "_BACKGROUND_FETCH_ABORT";
108 case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK:
109 return "_BACKGROUND_FETCH_CLICK";
110 case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL:
111 return "_BACKGROUND_FETCH_FAIL";
112 case ServiceWorkerMetrics::EventType::NAVIGATION_HINT:
113 return "_NAVIGATION_HINT";
114 case ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT:
115 return "_CAN_MAKE_PAYMENT";
116 case ServiceWorkerMetrics::EventType::ABORT_PAYMENT:
117 return "_ABORT_PAYMENT";
118 case ServiceWorkerMetrics::EventType::COOKIE_CHANGE:
119 return "_COOKIE_CHANGE";
120 case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS:
121 return "_BACKGROUND_FETCH_SUCCESS";
122 case ServiceWorkerMetrics::EventType::PERIODIC_SYNC:
123 return "_PERIODIC_SYNC";
124 case ServiceWorkerMetrics::EventType::CONTENT_DELETE:
125 return "_CONTENT_DELETE";
126 }
127 return "_UNKNOWN";
128 }
129
130 } // namespace
131
EventTypeToString(EventType event_type)132 const char* ServiceWorkerMetrics::EventTypeToString(EventType event_type) {
133 switch (event_type) {
134 case EventType::ACTIVATE:
135 return "Activate";
136 case EventType::INSTALL:
137 return "Install";
138 case EventType::SYNC:
139 return "Sync";
140 case EventType::NOTIFICATION_CLICK:
141 return "Notification Click";
142 case EventType::NOTIFICATION_CLOSE:
143 return "Notification Close";
144 case EventType::PUSH:
145 return "Push";
146 case EventType::MESSAGE:
147 return "Message";
148 case EventType::FETCH_MAIN_FRAME:
149 return "Fetch Main Frame";
150 case EventType::FETCH_SUB_FRAME:
151 return "Fetch Sub Frame";
152 case EventType::FETCH_SHARED_WORKER:
153 return "Fetch Shared Worker";
154 case EventType::FETCH_SUB_RESOURCE:
155 return "Fetch Subresource";
156 case EventType::UNKNOWN:
157 return "Unknown";
158 case EventType::FETCH_WAITUNTIL:
159 return "Fetch WaitUntil";
160 case EventType::EXTERNAL_REQUEST:
161 return "External Request";
162 case EventType::PAYMENT_REQUEST:
163 return "Payment Request";
164 case EventType::BACKGROUND_FETCH_ABORT:
165 return "Background Fetch Abort";
166 case EventType::BACKGROUND_FETCH_CLICK:
167 return "Background Fetch Click";
168 case EventType::BACKGROUND_FETCH_FAIL:
169 return "Background Fetch Fail";
170 case EventType::NAVIGATION_HINT:
171 return "Navigation Hint";
172 case EventType::CAN_MAKE_PAYMENT:
173 return "Can Make Payment";
174 case EventType::ABORT_PAYMENT:
175 return "Abort Payment";
176 case EventType::COOKIE_CHANGE:
177 return "Cookie Change";
178 case EventType::BACKGROUND_FETCH_SUCCESS:
179 return "Background Fetch Success";
180 case EventType::PERIODIC_SYNC:
181 return "Periodic Sync";
182 case EventType::CONTENT_DELETE:
183 return "Content Delete";
184 }
185 NOTREACHED() << "Got unexpected event type: " << static_cast<int>(event_type);
186 return "error";
187 }
188
StartSituationToString(StartSituation start_situation)189 const char* ServiceWorkerMetrics::StartSituationToString(
190 StartSituation start_situation) {
191 switch (start_situation) {
192 case StartSituation::UNKNOWN:
193 return "Unknown";
194 case StartSituation::DURING_STARTUP:
195 return "During startup";
196 case StartSituation::NEW_PROCESS:
197 return "New process";
198 case StartSituation::EXISTING_UNREADY_PROCESS:
199 return "Existing unready process";
200 case StartSituation::EXISTING_READY_PROCESS:
201 return "Existing ready process";
202 break;
203 }
204 NOTREACHED() << "Got unexpected start situation: "
205 << static_cast<int>(start_situation);
206 return "error";
207 }
208
SiteFromURL(const GURL & url)209 ServiceWorkerMetrics::Site ServiceWorkerMetrics::SiteFromURL(const GURL& url) {
210 // TODO(falken): Plumb through ContentBrowserClient::GetMetricSuffixForURL or
211 // figure out a way to remove ServiceWorkerMetrics::Site entirely instead of
212 // hardcoding sites in //content.
213
214 // This inaccurately matches google.example.com, see the TODO above.
215 static const char google_like_scope_prefix[] = "https://www.google.";
216 static const char ntp_scope_path[] = "/_/chrome/";
217 if (base::StartsWith(url.spec(), google_like_scope_prefix,
218 base::CompareCase::INSENSITIVE_ASCII) &&
219 base::StartsWith(url.path(), ntp_scope_path,
220 base::CompareCase::SENSITIVE)) {
221 return ServiceWorkerMetrics::Site::NEW_TAB_PAGE;
222 }
223
224 const base::StringPiece host = url.host_piece();
225 if (host == "plus.google.com")
226 return ServiceWorkerMetrics::Site::PLUS;
227 if (host == "inbox.google.com")
228 return ServiceWorkerMetrics::Site::INBOX;
229 if (host == "docs.google.com")
230 return ServiceWorkerMetrics::Site::DOCS;
231 if (host == "drive.google.com") {
232 // TODO(falken): This should not be DOCS but historically we logged them
233 // together.
234 return ServiceWorkerMetrics::Site::DOCS;
235 }
236 return ServiceWorkerMetrics::Site::OTHER;
237 }
238
CountInitDiskCacheResult(bool result)239 void ServiceWorkerMetrics::CountInitDiskCacheResult(bool result) {
240 UMA_HISTOGRAM_BOOLEAN("ServiceWorker.DiskCache.InitResult", result);
241 }
242
CountReadResponseResult(ServiceWorkerMetrics::ReadResponseResult result)243 void ServiceWorkerMetrics::CountReadResponseResult(
244 ServiceWorkerMetrics::ReadResponseResult result) {
245 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.ReadResponseResult",
246 result, NUM_READ_RESPONSE_RESULT_TYPES);
247 }
248
CountWriteResponseResult(ServiceWorkerMetrics::WriteResponseResult result)249 void ServiceWorkerMetrics::CountWriteResponseResult(
250 ServiceWorkerMetrics::WriteResponseResult result) {
251 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.WriteResponseResult",
252 result, NUM_WRITE_RESPONSE_RESULT_TYPES);
253 }
254
RecordPurgeResourceResult(int net_error)255 void ServiceWorkerMetrics::RecordPurgeResourceResult(int net_error) {
256 base::UmaHistogramSparse("ServiceWorker.Storage.PurgeResourceResult",
257 std::abs(net_error));
258 }
259
RecordDeleteAndStartOverResult(DeleteAndStartOverResult result)260 void ServiceWorkerMetrics::RecordDeleteAndStartOverResult(
261 DeleteAndStartOverResult result) {
262 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.Storage.DeleteAndStartOverResult",
263 result, NUM_DELETE_AND_START_OVER_RESULT_TYPES);
264 }
265
CountControlledPageLoad(Site site,bool is_main_frame_load)266 void ServiceWorkerMetrics::CountControlledPageLoad(Site site,
267 bool is_main_frame_load) {
268 DCHECK_NE(site, Site::OTHER);
269 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.PageLoad", site);
270 if (is_main_frame_load) {
271 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.MainFramePageLoad", site);
272 }
273 }
274
RecordStartInstalledWorkerStatus(blink::ServiceWorkerStatusCode status,EventType purpose)275 void ServiceWorkerMetrics::RecordStartInstalledWorkerStatus(
276 blink::ServiceWorkerStatusCode status,
277 EventType purpose) {
278 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status);
279 base::UmaHistogramEnumeration(
280 base::StrCat({"ServiceWorker.StartWorker.StatusByPurpose",
281 EventTypeToSuffix(purpose)}),
282 status);
283 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Purpose", purpose);
284 if (status == blink::ServiceWorkerStatusCode::kErrorTimeout) {
285 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Timeout.StartPurpose",
286 purpose);
287 }
288 }
289
RecordStartWorkerTime(base::TimeDelta time,bool is_installed,StartSituation start_situation,EventType purpose)290 void ServiceWorkerMetrics::RecordStartWorkerTime(base::TimeDelta time,
291 bool is_installed,
292 StartSituation start_situation,
293 EventType purpose) {
294 if (is_installed) {
295 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time", time);
296 base::UmaHistogramMediumTimes(
297 base::StrCat({"ServiceWorker.StartWorker.Time",
298 StartSituationToDeprecatedSuffix(start_situation)}),
299 time);
300 base::UmaHistogramMediumTimes(
301 base::StrCat({"ServiceWorker.StartWorker.Time",
302 StartSituationToDeprecatedSuffix(start_situation),
303 EventTypeToSuffix(purpose)}),
304 time);
305 } else {
306 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartNewWorker.Time", time);
307 }
308 }
309
RecordWorkerStopped(StopStatus status)310 void ServiceWorkerMetrics::RecordWorkerStopped(StopStatus status) {
311 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.WorkerStopped", status);
312 }
313
RecordStopWorkerTime(base::TimeDelta time)314 void ServiceWorkerMetrics::RecordStopWorkerTime(base::TimeDelta time) {
315 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StopWorker.Time", time);
316 }
317
RecordActivateEventStatus(blink::ServiceWorkerStatusCode status,bool is_shutdown)318 void ServiceWorkerMetrics::RecordActivateEventStatus(
319 blink::ServiceWorkerStatusCode status,
320 bool is_shutdown) {
321 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus", status);
322 if (is_shutdown) {
323 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_InShutdown",
324 status);
325 } else {
326 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_NotInShutdown",
327 status);
328 }
329 }
330
RecordInstallEventStatus(blink::ServiceWorkerStatusCode status)331 void ServiceWorkerMetrics::RecordInstallEventStatus(
332 blink::ServiceWorkerStatusCode status) {
333 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.InstallEventStatus", status);
334 }
335
RecordEventDuration(EventType event,base::TimeDelta time,bool was_handled)336 void ServiceWorkerMetrics::RecordEventDuration(EventType event,
337 base::TimeDelta time,
338 bool was_handled) {
339 switch (event) {
340 case EventType::ACTIVATE:
341 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ActivateEvent.Time", time);
342 break;
343 case EventType::INSTALL:
344 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.InstallEvent.Time", time);
345 break;
346 case EventType::FETCH_MAIN_FRAME:
347 case EventType::FETCH_SUB_FRAME:
348 case EventType::FETCH_SHARED_WORKER:
349 case EventType::FETCH_SUB_RESOURCE:
350 if (was_handled) {
351 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.HasResponse.Time",
352 time);
353 } else {
354 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.Fallback.Time",
355 time);
356 }
357 break;
358 case EventType::FETCH_WAITUNTIL:
359 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.WaitUntil.Time",
360 time);
361 break;
362 case EventType::SYNC:
363 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundSyncEvent.Time",
364 time);
365 break;
366 case EventType::NOTIFICATION_CLICK:
367 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationClickEvent.Time",
368 time);
369 break;
370 case EventType::NOTIFICATION_CLOSE:
371 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationCloseEvent.Time",
372 time);
373 break;
374 case EventType::PUSH:
375 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PushEvent.Time", time);
376 break;
377 case EventType::MESSAGE:
378 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ExtendableMessageEvent.Time",
379 time);
380 break;
381 case EventType::EXTERNAL_REQUEST:
382 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ExternalRequest.Time", time);
383 break;
384 case EventType::PAYMENT_REQUEST:
385 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PaymentRequestEvent.Time",
386 time);
387 break;
388 case EventType::BACKGROUND_FETCH_ABORT:
389 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchAbortEvent.Time",
390 time);
391 break;
392 case EventType::BACKGROUND_FETCH_CLICK:
393 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchClickEvent.Time",
394 time);
395 break;
396 case EventType::BACKGROUND_FETCH_FAIL:
397 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchFailEvent.Time",
398 time);
399 break;
400 case EventType::BACKGROUND_FETCH_SUCCESS:
401 UMA_HISTOGRAM_MEDIUM_TIMES(
402 "ServiceWorker.BackgroundFetchSuccessEvent.Time", time);
403 break;
404 case EventType::CAN_MAKE_PAYMENT:
405 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.CanMakePaymentEvent.Time",
406 time);
407 break;
408 case EventType::ABORT_PAYMENT:
409 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.AbortPaymentEvent.Time", time);
410 break;
411 case EventType::COOKIE_CHANGE:
412 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.CookieChangeEvent.Time", time);
413 break;
414 case EventType::PERIODIC_SYNC:
415 UMA_HISTOGRAM_MEDIUM_TIMES(
416 "ServiceWorker.PeriodicBackgroundSyncEvent.Time", time);
417 break;
418 case EventType::CONTENT_DELETE:
419 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ContentDeleteEvent.Time", time);
420 break;
421
422 case EventType::NAVIGATION_HINT:
423 // The navigation hint should not be sent as an event.
424 case EventType::UNKNOWN:
425 NOTREACHED() << "Invalid event type";
426 break;
427 }
428 }
429
RecordFetchEventStatus(bool is_main_resource,blink::ServiceWorkerStatusCode status)430 void ServiceWorkerMetrics::RecordFetchEventStatus(
431 bool is_main_resource,
432 blink::ServiceWorkerStatusCode status) {
433 if (is_main_resource) {
434 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.MainResource.Status",
435 status);
436 } else {
437 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.Subresource.Status",
438 status);
439 }
440 }
441
RecordProcessCreated(bool is_new_process)442 void ServiceWorkerMetrics::RecordProcessCreated(bool is_new_process) {
443 UMA_HISTOGRAM_BOOLEAN("EmbeddedWorkerInstance.ProcessCreated",
444 is_new_process);
445 }
446
RecordStartWorkerTiming(const StartTimes & times,StartSituation situation)447 void ServiceWorkerMetrics::RecordStartWorkerTiming(const StartTimes& times,
448 StartSituation situation) {
449 if (!ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
450 // This is in-process timing, so process consistency doesn't matter.
451 constexpr base::TimeDelta kMinTime = base::TimeDelta::FromMicroseconds(1);
452 constexpr base::TimeDelta kMaxTime = base::TimeDelta::FromMilliseconds(100);
453 constexpr int kBuckets = 50;
454 UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
455 "ServiceWorker.StartTiming.BrowserThreadHopTime", times.thread_hop_time,
456 kMinTime, kMaxTime, kBuckets);
457 }
458
459 // Bail if the timings across processes weren't consistent.
460 if (!base::TimeTicks::IsHighResolution() ||
461 !base::TimeTicks::IsConsistentAcrossProcesses()) {
462 RecordStartWorkerTimingClockConsistency(
463 CrossProcessTimeDelta::INACCURATE_CLOCK);
464 return;
465 }
466 if (times.remote_start_worker_received < times.local_start_worker_sent ||
467 times.local_end < times.remote_script_evaluation_end) {
468 RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NEGATIVE);
469 return;
470 }
471 RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NORMAL);
472
473 // Total duration.
474 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartTiming.Duration",
475 times.local_end - times.local_start);
476 base::UmaHistogramMediumTimes(
477 base::StrCat({"ServiceWorker.StartTiming.Duration",
478 StartSituationToSuffix(situation)}),
479 times.local_end - times.local_start);
480
481 // SentStartWorker milestone.
482 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartTiming.StartToSentStartWorker",
483 times.local_start_worker_sent - times.local_start);
484
485 // ReceivedStartWorker milestone.
486 UMA_HISTOGRAM_MEDIUM_TIMES(
487 "ServiceWorker.StartTiming.StartToReceivedStartWorker",
488 times.remote_start_worker_received - times.local_start);
489 UMA_HISTOGRAM_MEDIUM_TIMES(
490 "ServiceWorker.StartTiming.SentStartWorkerToReceivedStartWorker",
491 times.remote_start_worker_received - times.local_start_worker_sent);
492
493 // ScriptEvaluationStart milestone.
494 UMA_HISTOGRAM_MEDIUM_TIMES(
495 "ServiceWorker.StartTiming.StartToScriptEvaluationStart",
496 times.remote_script_evaluation_start - times.local_start);
497 UMA_HISTOGRAM_MEDIUM_TIMES(
498 "ServiceWorker.StartTiming.ReceivedStartWorkerToScriptEvaluationStart",
499 times.remote_script_evaluation_start -
500 times.remote_start_worker_received);
501
502 // ScriptEvaluationEnd milestone.
503 UMA_HISTOGRAM_MEDIUM_TIMES(
504 "ServiceWorker.StartTiming.StartToScriptEvaluationEnd",
505 times.remote_script_evaluation_end - times.local_start);
506 UMA_HISTOGRAM_MEDIUM_TIMES(
507 "ServiceWorker.StartTiming.ScriptEvaluationStartToScriptEvaluationEnd",
508 times.remote_script_evaluation_end -
509 times.remote_script_evaluation_start);
510
511 // End milestone.
512 UMA_HISTOGRAM_MEDIUM_TIMES(
513 "ServiceWorker.StartTiming.ScriptEvaluationEndToEnd",
514 times.local_end - times.remote_script_evaluation_end);
515 }
516
RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta type)517 void ServiceWorkerMetrics::RecordStartWorkerTimingClockConsistency(
518 CrossProcessTimeDelta type) {
519 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartTiming.ClockConsistency", type);
520 }
521
RecordStartStatusAfterFailure(int failure_count,blink::ServiceWorkerStatusCode status)522 void ServiceWorkerMetrics::RecordStartStatusAfterFailure(
523 int failure_count,
524 blink::ServiceWorkerStatusCode status) {
525 DCHECK_GT(failure_count, 0);
526
527 if (status == blink::ServiceWorkerStatusCode::kOk) {
528 UMA_HISTOGRAM_COUNTS_1000("ServiceWorker.StartWorker.FailureStreakEnded",
529 failure_count);
530 } else if (failure_count < std::numeric_limits<int>::max()) {
531 UMA_HISTOGRAM_COUNTS_1000("ServiceWorker.StartWorker.FailureStreak",
532 failure_count + 1);
533 }
534
535 if (failure_count == 1) {
536 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_1",
537 status);
538 } else if (failure_count == 2) {
539 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_2",
540 status);
541 } else if (failure_count == 3) {
542 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_3",
543 status);
544 }
545 }
546
RecordNavigationPreloadRequestHeaderSize(size_t size)547 void ServiceWorkerMetrics::RecordNavigationPreloadRequestHeaderSize(
548 size_t size) {
549 UMA_HISTOGRAM_COUNTS_100000("ServiceWorker.NavigationPreload.HeaderSize",
550 size);
551 }
552
RecordRuntime(base::TimeDelta time)553 void ServiceWorkerMetrics::RecordRuntime(base::TimeDelta time) {
554 // Start at 1 second since we expect service worker to last at least this
555 // long: the update timer and idle timeout timer run on the order of seconds.
556 constexpr base::TimeDelta kMin = base::TimeDelta::FromSeconds(1);
557 // End at 1 day since service workers can conceivably run as long as the the
558 // browser is open; we have to cap somewhere.
559 constexpr base::TimeDelta kMax = base::TimeDelta::FromDays(1);
560 // Set the bucket count to 50 since that is the recommended value for all
561 // histograms.
562 const int kBucketCount = 50;
563
564 UMA_HISTOGRAM_CUSTOM_TIMES("ServiceWorker.Runtime", time, kMin, kMax,
565 kBucketCount);
566 }
567
RecordStartServiceWorkerForNavigationHintResult(StartServiceWorkerForNavigationHintResult result)568 void ServiceWorkerMetrics::RecordStartServiceWorkerForNavigationHintResult(
569 StartServiceWorkerForNavigationHintResult result) {
570 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartForNavigationHint.Result",
571 result);
572 }
573
RecordRegisteredOriginCount(size_t origin_count)574 void ServiceWorkerMetrics::RecordRegisteredOriginCount(size_t origin_count) {
575 UMA_HISTOGRAM_COUNTS_1M("ServiceWorker.RegisteredOriginCount", origin_count);
576 }
577
RecordLookupRegistrationTime(blink::ServiceWorkerStatusCode status,base::TimeDelta duration)578 void ServiceWorkerMetrics::RecordLookupRegistrationTime(
579 blink::ServiceWorkerStatusCode status,
580 base::TimeDelta duration) {
581 if (status == blink::ServiceWorkerStatusCode::kOk) {
582 UMA_HISTOGRAM_TIMES(
583 "ServiceWorker.LookupRegistration.MainResource.Time.Exists", duration);
584 } else if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
585 UMA_HISTOGRAM_TIMES(
586 "ServiceWorker.LookupRegistration.MainResource.Time.DoesNotExist",
587 duration);
588 } else {
589 UMA_HISTOGRAM_TIMES(
590 "ServiceWorker.LookupRegistration.MainResource.Time.Error", duration);
591 }
592 }
593
RecordByteForByteUpdateCheckStatus(blink::ServiceWorkerStatusCode status,bool has_found_update)594 void ServiceWorkerMetrics::RecordByteForByteUpdateCheckStatus(
595 blink::ServiceWorkerStatusCode status,
596 bool has_found_update) {
597 DCHECK(blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled());
598 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.UpdateCheck.Result", status);
599 if (status == blink::ServiceWorkerStatusCode::kOk) {
600 UMA_HISTOGRAM_BOOLEAN("ServiceWorker.UpdateCheck.UpdateFound",
601 has_found_update);
602 }
603 }
604
605 } // namespace content
606