blob: fa9d1092586ca024bcd5444b5f6e234b84451c9e [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2017 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/base/math/range.html">
<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/metrics/spa_navigation_helper.html">
<link rel="import" href="/tracing/metrics/system_health/breakdown_tree_helpers.html">
<link rel="import" href="/tracing/metrics/system_health/loading_metric.html">
<link rel="import" href="/tracing/value/histogram.html">
<script>
'use strict';
tr.exportTo('tr.metrics', function() {
const SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY =
tr.v.HistogramBinBoundaries.createExponential(1, 1000, 50);
/**
* This metric measures the duration between the input event
* causing a SPA navigation and the first paint event after it.
*/
function spaNavigationMetric(histograms, model) {
const histogram = new tr.v.Histogram(
'spaNavigationStartToFpDuration',
tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,
SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY);
histogram.description = 'Latency between the input event causing' +
' a SPA navigation and the first paint event after it';
histogram.customizeSummaryOptions({
count: false,
sum: false,
});
const modelHelper = model.getOrCreateHelper(
tr.model.helpers.ChromeModelHelper);
if (!modelHelper) {
// Chrome isn't present.
return;
}
const rendererHelpers = modelHelper.rendererHelpers;
if (!rendererHelpers) {
// We couldn't find any renderer processes.
return;
}
const browserHelper = modelHelper.browserHelper;
for (const rendererHelper of Object.values(rendererHelpers)) {
const spaNavigations = tr.metrics.findSpaNavigationsOnRenderer(
rendererHelper, browserHelper);
for (const spaNav of spaNavigations) {
let beginTs = 0;
if (spaNav.navStartCandidates.inputLatencyAsyncSlice) {
const beginData =
spaNav.navStartCandidates.inputLatencyAsyncSlice.args.data;
// TODO(sunjian): rename convertTimestampToModelTime to something like
// convertTraceEventTsToModelTs and get rid of the first parameter.
beginTs = model.convertTimestampToModelTime(
'traceEventClock',
beginData.INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT.time);
} else {
beginTs = spaNav.navStartCandidates.goToIndexSlice.start;
}
const rangeOfInterest = tr.b.math.Range.fromExplicitRange(
beginTs, spaNav.firstPaintEvent.start);
const networkEvents =
tr.e.chrome.EventFinderUtils.getNetworkEventsInRange(
rendererHelper.process, rangeOfInterest);
const breakdownDict = tr.metrics.sh.generateWallClockTimeBreakdownTree(
rendererHelper.mainThread, networkEvents, rangeOfInterest);
const breakdownDiagnostic = new tr.v.d.Breakdown();
breakdownDiagnostic.colorScheme =
tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;
for (const label in breakdownDict) {
breakdownDiagnostic.set(label,
parseInt(breakdownDict[label].total * 1e3) / 1e3);
}
histogram.addSample(
rangeOfInterest.duration,
{
'Breakdown of [navStart, firstPaint]': breakdownDiagnostic,
'Start': new tr.v.d.RelatedEventSet(spaNav.navigationStart),
'End': new tr.v.d.RelatedEventSet(spaNav.firstPaintEvent),
'Navigation infos': new tr.v.d.GenericSet([{
url: spaNav.url,
pid: rendererHelper.pid,
navStart: beginTs,
firstPaint: spaNav.firstPaintEvent.start
}]),
});
}
}
histograms.addHistogram(histogram);
}
tr.metrics.MetricRegistry.register(spaNavigationMetric);
return {
spaNavigationMetric,
};
});
</script>