1 <!DOCTYPE html> 2 <!-- 3 Copyright (c) 2015 The Chromium Authors. All rights reserved. 4 Use of this source code is governed by a BSD-style license that can be 5 found in the LICENSE file. 6 --> 7 8 <link rel="import" href="/tracing/base/statistics.html"> 9 <link rel="import" href="/tracing/metrics/metric_registry.html"> 10 <link rel="import" href="/tracing/metrics/system_health/utils.html"> 11 <link rel="import" href="/tracing/model/user_model/animation_expectation.html"> 12 <link rel="import" href="/tracing/model/user_model/load_expectation.html"> 13 <link rel="import" href="/tracing/model/user_model/response_expectation.html"> 14 <link rel="import" href="/tracing/value/histogram.html"> 15 16 <script> 17 'use strict'; 18 19 tr.exportTo('tr.metrics.sh', function() { 20 // In the case of Response, Load, and DiscreteAnimation IRs, Responsiveness is 21 // derived from the time between when the user thinks they begin an interation 22 // (expectedStart) and the time when the screen first changes to reflect the 23 // interaction (actualEnd). There may be a delay between expectedStart and 24 // when chrome first starts processing the interaction (actualStart) if the 25 // main thread is busy. The user doesn't know when actualStart is, they only 26 // know when expectedStart is. User responsiveness, by definition, considers 27 // only what the user experiences, so "duration" is defined as actualEnd - 28 // expectedStart. 29 30 function computeAnimationThroughput(animationExpectation) { 31 if (animationExpectation.frameEvents === undefined || 32 animationExpectation.frameEvents.length === 0) 33 throw new Error('Animation missing frameEvents ' + 34 animationExpectation.stableId); 35 36 var durationInS = tr.b.convertUnit(animationExpectation.duration, 37 tr.b.UnitScale.Metric.MILLI, tr.b.UnitScale.Metric.NONE); 38 return animationExpectation.frameEvents.length / durationInS; 39 } 40 41 function computeAnimationframeTimeDiscrepancy(animationExpectation) { 42 if (animationExpectation.frameEvents === undefined || 43 animationExpectation.frameEvents.length === 0) 44 throw new Error('Animation missing frameEvents ' + 45 animationExpectation.stableId); 46 47 var frameTimestamps = animationExpectation.frameEvents; 48 frameTimestamps = frameTimestamps.toArray().map(function(event) { 49 return event.start; 50 }); 51 52 var absolute = true; 53 return tr.b.Statistics.timestampsDiscrepancy(frameTimestamps, absolute); 54 } 55 56 /** 57 * @param {!tr.v.ValueSet} values 58 * @param {!tr.model.Model} model 59 * @param {!Object=} opt_options 60 */ 61 function responsivenessMetric(values, model, opt_options) { 62 var responseNumeric = new tr.v.Histogram('response latency', 63 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, 64 tr.v.HistogramBinBoundaries.createLinear(100, 1e3, 50)); 65 var throughputNumeric = new tr.v.Histogram('animation throughput', 66 tr.b.Unit.byName.unitlessNumber_biggerIsBetter, 67 tr.v.HistogramBinBoundaries.createLinear(10, 60, 10)); 68 var frameTimeDiscrepancyNumeric = new tr.v.Histogram( 69 'animation frameTimeDiscrepancy', 70 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, 71 tr.v.HistogramBinBoundaries.createLinear(0, 1e3, 50). 72 addExponentialBins(1e4, 10)); 73 var latencyNumeric = new tr.v.Histogram('animation latency', 74 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, 75 tr.v.HistogramBinBoundaries.createLinear(0, 300, 60)); 76 77 model.userModel.expectations.forEach(function(ue) { 78 if (opt_options && opt_options.rangeOfInterest && 79 !opt_options.rangeOfInterest.intersectsExplicitRangeInclusive( 80 ue.start, ue.end)) 81 return; 82 83 var sampleDiagnosticMap = tr.v.d.DiagnosticMap.fromObject( 84 {relatedEvents: new tr.v.d.RelatedEventSet([ue])}); 85 86 // Responsiveness is not defined for Idle or Startup expectations. 87 if (ue instanceof tr.model.um.IdleExpectation) { 88 return; 89 } else if (ue instanceof tr.model.um.StartupExpectation) { 90 return; 91 } else if (ue instanceof tr.model.um.LoadExpectation) { 92 // This is already covered by loadingMetric. 93 } else if (ue instanceof tr.model.um.ResponseExpectation) { 94 responseNumeric.addSample(ue.duration, sampleDiagnosticMap); 95 } else if (ue instanceof tr.model.um.AnimationExpectation) { 96 if (ue.frameEvents === undefined || ue.frameEvents.length === 0) { 97 // Ignore animation stages that do not have associated frames: 98 // https://github.com/catapult-project/catapult/issues/2446 99 return; 100 } 101 var throughput = computeAnimationThroughput(ue); 102 if (throughput === undefined) 103 throw new Error('Missing throughput for ' + 104 ue.stableId); 105 106 throughputNumeric.addSample(throughput, sampleDiagnosticMap); 107 108 var frameTimeDiscrepancy = computeAnimationframeTimeDiscrepancy(ue); 109 if (frameTimeDiscrepancy === undefined) 110 throw new Error('Missing frameTimeDiscrepancy for ' + 111 ue.stableId); 112 113 frameTimeDiscrepancyNumeric.addSample( 114 frameTimeDiscrepancy, sampleDiagnosticMap); 115 116 ue.associatedEvents.forEach(function(event) { 117 if (!(event instanceof tr.e.cc.InputLatencyAsyncSlice)) 118 return; 119 120 latencyNumeric.addSample(event.duration, sampleDiagnosticMap); 121 }); 122 } else { 123 throw new Error('Unrecognized stage for ' + ue.stableId); 124 } 125 }); 126 127 [ 128 responseNumeric, throughputNumeric, frameTimeDiscrepancyNumeric, 129 latencyNumeric 130 ].forEach(function(numeric) { 131 numeric.customizeSummaryOptions({ 132 avg: true, 133 max: true, 134 min: true, 135 std: true 136 }); 137 }); 138 139 values.addHistogram(responseNumeric); 140 values.addHistogram(throughputNumeric); 141 values.addHistogram(frameTimeDiscrepancyNumeric); 142 values.addHistogram(latencyNumeric); 143 } 144 145 tr.metrics.MetricRegistry.register(responsivenessMetric, { 146 supportsRangeOfInterest: true 147 }); 148 149 return { 150 responsivenessMetric: responsivenessMetric, 151 }; 152 }); 153 </script> 154