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