| <!DOCTYPE html> |
| <!-- |
| Copyright 2019 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/tracing/extras/chrome/event_finder_utils.html"> |
| <link rel="import" href="/tracing/metrics/metric_registry.html"> |
| <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> |
| <link rel="import" href="/tracing/value/histogram.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.metrics', function() { |
| const timeDurationInMs_smallerIsBetter = |
| tr.b.Unit.byName.timeDurationInMs_smallerIsBetter; |
| const unitlessNumber_smallerIsBetter = |
| tr.b.Unit.byName.unitlessNumber_smallerIsBetter; |
| const EventFinderUtils = tr.e.chrome.EventFinderUtils; |
| |
| const METRIC_BOUNDARIES = tr.v.HistogramBinBoundaries |
| .createLinear(0, 1e3, 20) // 50ms step to 1s |
| .addLinearBins(3e3, 20) // 100ms step to 3s |
| .addExponentialBins(80e3, 30); |
| |
| const SUMMARY_OPTIONS = { |
| avg: true, |
| count: false, |
| max: true, |
| min: true, |
| std: true, |
| sum: false, |
| }; |
| |
| /** |
| * Computes the following results |
| * - reported_by_page:time_to_viewable, |
| * - reported_by_page:time_to_interactive, |
| * - reported_by_page:benchmark_time. |
| * |
| * The metric is intended for pages that call |
| * - performance.mark(telemetry:reported_by_page:viewable), |
| * when the page showed meaningful content to the user. |
| * - performance.mark(telemetry:reported_by_page:interactive) |
| * when the page is ready to interact with the user. |
| * - performance.mark(telemetry:reported_by_page:benchmark_begin) |
| * when an important operation starts. |
| * - performance.mark(telemetry:reported_by_page:benchmark_end) |
| * when the important operation ends. |
| */ |
| function reportedByPageMetric(histograms, model) { |
| const timeToViewable = histograms.createHistogram( |
| 'reported_by_page:time_to_viewable', |
| timeDurationInMs_smallerIsBetter, [], { |
| binBoundaries: METRIC_BOUNDARIES, |
| description: 'Time from navigation start' + |
| 'to telemetry:reported_by_page:viewable', |
| summaryOptions: SUMMARY_OPTIONS, |
| }); |
| const timeToInteractive = histograms.createHistogram( |
| 'reported_by_page:time_to_interactive', |
| timeDurationInMs_smallerIsBetter, [], { |
| binBoundaries: METRIC_BOUNDARIES, |
| description: 'Time from navigation start ' + |
| 'to telemetry:reported_by_page:interactive', |
| summaryOptions: SUMMARY_OPTIONS, |
| }); |
| const benchmarkTime = histograms.createHistogram( |
| 'reported_by_page:benchmark_time', |
| timeDurationInMs_smallerIsBetter, [], { |
| binBoundaries: METRIC_BOUNDARIES, |
| description: |
| 'Time from telemetry:reported_by_page:benchmark_begin ' + |
| 'to telemetry:reported_by_page:benchmark_end', |
| summaryOptions: SUMMARY_OPTIONS, |
| }); |
| const chromeHelper = model.getOrCreateHelper( |
| tr.model.helpers.ChromeModelHelper); |
| for (const pid in chromeHelper.rendererHelpers) { |
| const rendererHelper = chromeHelper.rendererHelpers[pid]; |
| if (rendererHelper.isChromeTracingUI) continue; |
| // Main thread may be missing. See crbug.com/1059726 for an example. |
| if (rendererHelper.mainThread === undefined) continue; |
| measureUserTime(rendererHelper, |
| 'navigationStart', |
| 'telemetry:reported_by_page:viewable', |
| timeToViewable); |
| measureUserTime(rendererHelper, |
| 'navigationStart', |
| 'telemetry:reported_by_page:interactive', |
| timeToInteractive); |
| measureUserTime(rendererHelper, |
| 'telemetry:reported_by_page:benchmark_begin', |
| 'telemetry:reported_by_page:benchmark_end', |
| benchmarkTime); |
| } |
| } |
| |
| /* |
| * Finds all |startName|,|endName| event pairs that have the matching |
| * navigation ids on the main thread of the given renderer process |
| * and adds the duration from the start event to the end event to the |
| * given |histogram|. |
| */ |
| function measureUserTime(rendererHelper, startName, endName, histogram) { |
| // Maps navigation ids to the start events. |
| const startEventByNavId = new Map(); |
| // Iterate events in the sorted order. |
| for (const event of rendererHelper.mainThread.sliceGroup.childEvents()) { |
| const navId = getNavigationId(event); |
| if (!navId) continue; |
| if (EventFinderUtils.hasCategoryAndName( |
| event, 'blink.user_timing', startName)) { |
| // Found a start event, save it. |
| startEventByNavId.set(navId, event); |
| } |
| if (EventFinderUtils.hasCategoryAndName( |
| event, 'blink.user_timing', endName)) { |
| if (!startEventByNavId.has(navId)) { |
| throw Error(`Missing ${startName} for ${endName} at {event.start}`); |
| } |
| // Found an end event. Compute the time from the previous start event. |
| const range = tr.b.math.Range.fromExplicitRange( |
| startEventByNavId.get(navId).start, event.start); |
| histogram.addSample(range.duration); |
| startEventByNavId.delete(navId); |
| } |
| } |
| } |
| |
| function getNavigationId(event) { |
| return event.args.data && event.args.data.navigationId; |
| } |
| |
| tr.metrics.MetricRegistry.register(reportedByPageMetric); |
| |
| return { |
| reportedByPageMetric |
| }; |
| }); |
| </script> |