all files / lighthouse-core/lib/traces/ tracing-processor.js

43.18% Statements 19/44
33.33% Branches 4/12
25% Functions 2/8
43.18% Lines 19/44
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146                                                                                                                                                                                                                                                             
/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
'use strict';
 
Eif (typeof global.window === 'undefined') {
  global.window = global;
}
 
// we need gl-matrix and jszip for traceviewer
// since it has internal forks for isNode and they get mixed up during
// browserify, we require them locally here and global-ize them.
 
// from catapult/tracing/tracing/base/math.html
const glMatrixModule = require('gl-matrix');
Object.keys(glMatrixModule).forEach(exportName => {
  global[exportName] = glMatrixModule[exportName];
});
// from catapult/tracing/tracing/extras/importer/jszip.html
global.JSZip = require('jszip/dist/jszip.min.js');
 
global.HTMLImportsLoader = {};
global.HTMLImportsLoader.hrefToAbsolutePath = function(path) {
  if (path === '/gl-matrix-min.js') {
    return 'empty-module';
  }
  Eif (path === '/jszip.min.js') {
    return 'jszip/dist/jszip.min.js';
  }
};
 
require('../../third_party/traceviewer-js/');
const traceviewer = global.tr;
 
class TraceProcessor {
  get RESPONSE() {
    return 'Response';
  }
 
  get ANIMATION() {
    return 'Animation';
  }
 
  get LOAD() {
    return 'Load';
  }
 
  init(contents) {
    let contentsJSON = null;
 
    try {
      contentsJSON = typeof contents === 'string' ? JSON.parse(contents) :
          contents;
 
      // If the file already wrapped the trace events in a
      // traceEvents object, grab the contents of the object.
      if (contentsJSON !== null &&
        typeof contentsJSON.traceEvents !== 'undefined') {
        contentsJSON = contentsJSON.traceEvents;
      }
    } catch (e) {
      throw new Error('Invalid trace contents: ' + e.message);
    }
 
    const events = [JSON.stringify({
      traceEvents: contentsJSON
    })];
 
    return this.convertEventsToModel(events);
  }
 
  // Create the importer and import the trace contents to a model.
  convertEventsToModel(events) {
    const io = new traceviewer.importer.ImportOptions();
    io.showImportWarnings = false;
    io.pruneEmptyContainers = false;
    io.shiftWorldToZero = true;
 
    const model = new traceviewer.Model();
    const importer = new traceviewer.importer.Import(model, io);
    importer.importTraces(events);
 
    return model;
  }
 
  getInputReadiness(model) {
    // Now set up the user expectations model.
    // this fake idle Interaction Record is used to grab the readiness out of
    // TODO(paullewis) start the idle at firstPaint/fCP and end it at end of recording
    const idle = new traceviewer.model.um.IdleExpectation(model, 'test', 0, 10000);
    model.userModel.expectations.push(idle);
 
    // Set up a value list for the hazard metric.
    // TODO use new approach from ben
    //   https://github.com/GoogleChrome/lighthouse/pull/284#issuecomment-217263964
    const valueList = new traceviewer.metrics.ValueList();
    traceviewer.metrics.sh.hazardMetric(valueList, model);
    // grab last item, as it matches the fake idle we push()'d into the model'
    const metricValue = valueList.valueDicts[valueList.valueDicts.length - 1];
    return metricValue;
  }
 
  /**
   * Uses traceviewer's statistics package to create a log-normal distribution.
   * Specified by providing the median value, at which the score will be 0.5,
   * and the falloff, the initial point of diminishing returns where any
   * improvement in value will yield increasingly smaller gains in score. Both
   * values should be in the same units (e.g. milliseconds). See
   *   https://www.desmos.com/calculator/tx1wcjk8ch
   * for an interactive view of the relationship between these parameters and
   * the typical parameterization (location and shape) of the log-normal
   * distribution.
   * @param {number} median
   * @param {number} falloff
   * @return {!Statistics.LogNormalDistribution}
   */
  static getLogNormalDistribution(median, falloff) {
    const location = Math.log(median);
 
    // The "falloff" value specified the location of the smaller of the positive
    // roots of the third derivative of the log-normal CDF. Calculate the shape
    // parameter in terms of that value and the median.
    const logRatio = Math.log(falloff / median);
    const shape = 0.5 * Math.sqrt(1 - 3 * logRatio -
        Math.sqrt((logRatio - 3) * (logRatio - 3) - 8));
 
    return new traceviewer.b.Statistics.LogNormalDistribution(location, shape);
  }
}
 
module.exports = TraceProcessor;