Index: tracing/tracing/metrics/system_health/utils.html |
diff --git a/tracing/tracing/metrics/system_health/utils.html b/tracing/tracing/metrics/system_health/utils.html |
index fa60f7ea1506e4e519821018b6607b6e3ce92012..445fe3308ac6dc560b2c499d183fe4557e257e34 100644 |
--- a/tracing/tracing/metrics/system_health/utils.html |
+++ b/tracing/tracing/metrics/system_health/utils.html |
@@ -4,6 +4,7 @@ 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> |
@@ -35,9 +36,104 @@ tr.exportTo('tr.metrics.sh', function() { |
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)); |
+ } |
+ |
return { |
perceptualBlend, |
filterExpectationsByRange, |
+ splitGlobalDumpsByBrowserName |
}; |
}); |
</script> |