blob: 92bb7c71fb11e1a9db9e09832bc5dfe95534c2c9 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 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/chrome_processes.html">
<link rel="import" href="/tracing/model/user_model/user_expectation.html">
<script>
'use strict';
tr.exportTo('tr.metrics.sh', function() {
// Returns a weight for this score.
// score should be a number between 0 and 1 inclusive.
// This function is expected to be passed to tr.b.math.Statistics.weightedMean
// as its weightCallback.
function perceptualBlend(ir, index, score) {
// Lower scores are exponentially more important than higher scores
// due to the Peak-end rule.
// Other than that general rule, there is no specific reasoning behind this
// specific formula -- it is fairly arbitrary.
return Math.exp(1 - score);
}
function filterExpectationsByRange(irs, opt_range) {
const filteredExpectations = [];
irs.forEach(function(ir) {
if (!(ir instanceof tr.model.um.UserExpectation)) return;
if (!opt_range ||
opt_range.intersectsExplicitRangeInclusive(ir.start, ir.end)) {
filteredExpectations.push(ir);
}
});
return filteredExpectations;
}
/**
* Splits the global memory dumps in |model| by browser name.
*
* @param {!tr.Model} model The trace model from which the global dumps
* should be extracted.
* @param {!tr.b.math.Range=} opt_rangeOfInterest If provided, global memory
* dumps that do not inclusively intersect the range will be skipped.
* @return {!Map<string, !Array<!tr.model.GlobalMemoryDump>} A map from
* browser names to the associated global memory dumps.
*/
function splitGlobalDumpsByBrowserName(model, opt_rangeOfInterest) {
const chromeModelHelper =
model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);
const browserNameToGlobalDumps = new Map();
const globalDumpToBrowserHelper = new WeakMap();
// 1. For each browser process in the model, add its global memory dumps to
// |browserNameToGlobalDumps|. |chromeModelHelper| can be undefined if
// it fails to find any browser, renderer or GPU process (see
// tr.model.helpers.ChromeModelHelper.supportsModel).
if (chromeModelHelper) {
chromeModelHelper.browserHelpers.forEach(function(helper) {
// Retrieve the associated global memory dumps and check that they
// haven't been classified as belonging to another browser process.
const globalDumps = skipDumpsThatDoNotIntersectRange(
helper.process.memoryDumps.map(d => d.globalMemoryDump),
opt_rangeOfInterest);
globalDumps.forEach(function(globalDump) {
const existingHelper = globalDumpToBrowserHelper.get(globalDump);
if (existingHelper !== undefined) {
throw new Error('Memory dump ID clash across multiple browsers ' +
'with PIDs: ' + existingHelper.pid + ' and ' + helper.pid);
}
globalDumpToBrowserHelper.set(globalDump, helper);
});
makeKeyUniqueAndSet(browserNameToGlobalDumps,
tr.e.chrome.chrome_processes.canonicalizeName(helper.browserName),
globalDumps);
});
}
// 2. If any global memory dump does not have any associated browser
// process for some reason, associate it with an 'unknown_browser' browser
// so that we don't lose the data.
const unclassifiedGlobalDumps = skipDumpsThatDoNotIntersectRange(
model.globalMemoryDumps.filter(g => !globalDumpToBrowserHelper.has(g)),
opt_rangeOfInterest);
if (unclassifiedGlobalDumps.length > 0) {
makeKeyUniqueAndSet(
browserNameToGlobalDumps, 'unknown_browser', unclassifiedGlobalDumps);
}
return browserNameToGlobalDumps;
}
/**
* Function for adding entries with duplicate keys to a map without
* overriding existing entries.
*
* This is achieved by appending numeric indices (2, 3, 4, ...) to duplicate
* keys. Example:
*
* const map = new Map();
* // map = Map {}.
*
* makeKeyUniqueAndSet(map, 'key', 'a');
* // map = Map {"key" => "a"}.
*
* makeKeyUniqueAndSet(map, 'key', 'b');
* // map = Map {"key" => "a", "key2" => "b"}.
* ^^^^
* makeKeyUniqueAndSet(map, 'key', 'c');
* // map = Map {"key" => "a", "key2" => "b", "key3" => "c"}.
* ^^^^ ^^^^
*/
function makeKeyUniqueAndSet(map, key, value) {
let uniqueKey = key;
let nextIndex = 2;
while (map.has(uniqueKey)) {
uniqueKey = key + nextIndex;
nextIndex++;
}
map.set(uniqueKey, value);
}
function skipDumpsThatDoNotIntersectRange(dumps, opt_range) {
if (!opt_range) return dumps;
return dumps.filter(d => opt_range.intersectsExplicitRangeInclusive(
d.start, d.end));
}
/**
* Returns true if |category| is one of the categories of |event|, and |event|
* has title |title|.
*
* TODO(dproy): Make this a method on a suitable parent class of the
* event/slice classes that are used with this function.
*/
function hasCategoryAndName(event, category, title) {
return event.title === title && event.category &&
tr.b.getCategoryParts(event.category).includes(category);
}
return {
hasCategoryAndName,
filterExpectationsByRange,
perceptualBlend,
splitGlobalDumpsByBrowserName
};
});
</script>