Home | History | Annotate | Download | only in system_health
      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