diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-11-01 03:03:29 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-11-01 03:03:29 +0000 |
commit | 34579d7c7e8be8eca462fd2f66bd516a1afae77f (patch) | |
tree | 4b32ed3904208724a5b1ec4a5481861c67677c99 | |
parent | 05e40b813296c7f71f161ccec62561266ae68edb (diff) | |
parent | ff01e0c3c2defa411bd1d8372936bce7c95c70a7 (diff) | |
download | chromium-trace-34579d7c7e8be8eca462fd2f66bd516a1afae77f.tar.gz |
Snap for 5103990 from ff01e0c3c2defa411bd1d8372936bce7c95c70a7 to qt-release
Change-Id: Id58843bf247ac447b06e77260b4c781944661bcf
36 files changed, 1415 insertions, 436 deletions
diff --git a/UPSTREAM_REVISION b/UPSTREAM_REVISION index ef75e069..60f3d3ac 100644 --- a/UPSTREAM_REVISION +++ b/UPSTREAM_REVISION @@ -1 +1 @@ -eae13a4b34ccf54974dfacf7d72effd99729f543 +5e1c1c293b07ef04a247dd8dff50972d207663a4 diff --git a/catapult/common/lab/commits.py b/catapult/common/lab/commits.py index ce02ee37..6d47b916 100755 --- a/catapult/common/lab/commits.py +++ b/catapult/common/lab/commits.py @@ -15,7 +15,7 @@ import urllib2 _BASE_URL = 'https://chromium.googlesource.com' # Can be up to 10,000. -_REVISION_COUNT = 1000 +_REVISION_COUNT = 10000 _REPOSITORIES = [ 'chromium/src', @@ -67,33 +67,37 @@ def CommitTimes(repository, revision_count): commit_time_string = revision['committer']['time'] commit_time = datetime.datetime.strptime( commit_time_string, '%a %b %d %H:%M:%S %Y') - commit_times.append(commit_time) + commit_times.append(commit_time - datetime.timedelta(hours=7)) return commit_times +def IsWeekday(time): + return time.weekday() >= 0 and time.weekday() < 5 + + def main(): for repository in _REPOSITORIES: commit_times = CommitTimes(repository, _REVISION_COUNT) commit_durations = [] for time1, time2 in Pairwise(commit_times): - commit_durations.append((time1 - time2).total_seconds()) + #if not (IsWeekday(time1) and IsWeekday(time2)): + # continue + commit_durations.append((time1 - time2).total_seconds() / 60.) commit_durations.sort() print 'REPOSITORY:', repository - print 'Start Date:', min(commit_times) - print ' End Date:', max(commit_times) + print 'Start Date:', min(commit_times), 'PDT' + print ' End Date:', max(commit_times), 'PDT' print ' Duration:', max(commit_times) - min(commit_times) print ' n:', len(commit_times) - for p in (0.00, 0.05, 0.25, 0.50, 0.75, 0.95, 1.00): + for p in (0.25, 0.50, 0.90): percentile = Percentile(commit_durations, p) - print '%3d%% commit duration:' % (p * 100), '%6ds' % percentile + print '%3d%% commit duration:' % (p * 100), '%6.1fm' % percentile mean = math.fsum(commit_durations) / len(commit_durations) - print ' Min commit duration:', '%6ds' % min(commit_durations) - print 'Mean commit duration:', '%6ds' % mean - print ' Max commit duration:', '%6ds' % max(commit_durations) + print 'Mean commit duration:', '%6.1fm' % mean print diff --git a/catapult/common/node_runner/node_runner/minify b/catapult/common/node_runner/node_runner/minify index 9d5bb2f1..a5a24cf2 100755 --- a/catapult/common/node_runner/node_runner/minify +++ b/catapult/common/node_runner/node_runner/minify @@ -14,12 +14,6 @@ const espree = require('espree'); const fs = require('fs'); const nopt = require('nopt'); -const ESPREE_OPTIONS = { - attachComment: false, - comments: false, - ecmaVersion: 2018, -}; - const args = nopt(); const filename = args.argv.remain[0]; @@ -44,9 +38,10 @@ for (const node of dom5.nodeWalkAll(parsedHtml, () => true)) { .replace(/\n+/g, '\n')); } else if (dom5.predicates.hasTagName('script')(node) && !dom5.predicates.hasAttr('src')(node)) { - dom5.setTextContent(node, escodegen.generate( - espree.parse(dom5.getTextContent(node), ESPREE_OPTIONS), - {format: {indent: {style: ''}}})); + let text = dom5.getTextContent(node); + const ast = espree.parse(text, {ecmaVersion: 2018}); + text = escodegen.generate(ast, {format: {indent: {style: ''}}}); + dom5.setTextContent(node, text); } else if (dom5.predicates.hasTagName('style')(node)) { dom5.setTextContent(node, dom5.getTextContent(node) .replace(/[\r\n]/g, '') diff --git a/catapult/common/node_runner/node_runner/package.json b/catapult/common/node_runner/node_runner/package.json index 9a92270d..d743e001 100644 --- a/catapult/common/node_runner/node_runner/package.json +++ b/catapult/common/node_runner/node_runner/package.json @@ -16,11 +16,13 @@ "private": true, "dependencies": { "dom5": "^1.0.0", - "escodegen": "^1.0.0", + "escodegen": "^1.11.0", "eslint": "^4.0.0", "eslint-config-google": "^0.6.0", "eslint-plugin-html": "^4.0.0", "espree": "^3.0.0", - "vulcanize": "^1.16.0" + "vulcanize": "^1.16.0", + "webpack": "^4.16.1", + "webpack-command": "^0.4.1" } } diff --git a/catapult/dependency_manager/dependency_manager/__init__.py b/catapult/dependency_manager/dependency_manager/__init__.py index 68efbdb3..84cca5a2 100644 --- a/catapult/dependency_manager/dependency_manager/__init__.py +++ b/catapult/dependency_manager/dependency_manager/__init__.py @@ -25,7 +25,7 @@ _AddDirToPythonPath(CATAPULT_THIRD_PARTY_PATH, 'zipfile') _AddDirToPythonPath(DEPENDENCY_MANAGER_PATH) -# pylint: disable=unused-import +# pylint: disable=unused-import,wrong-import-position from .archive_info import ArchiveInfo from .base_config import BaseConfig from .cloud_storage_info import CloudStorageInfo diff --git a/catapult/dependency_manager/dependency_manager/archive_info.py b/catapult/dependency_manager/dependency_manager/archive_info.py index ff80b63c..f28028c9 100644 --- a/catapult/dependency_manager/dependency_manager/archive_info.py +++ b/catapult/dependency_manager/dependency_manager/archive_info.py @@ -62,8 +62,7 @@ class ArchiveInfo(object): def ShouldUnzipArchive(self): if not self._has_minimum_data: raise exceptions.ArchiveError( - 'Missing needed info to unzip archive. Known data: %s', - self.data_string) + 'Missing needed info to unzip archive. Know data: %s' % self) return not os.path.exists(self._dependency_path) @property diff --git a/catapult/dependency_manager/dependency_manager/cloud_storage_info_unittest.py b/catapult/dependency_manager/dependency_manager/cloud_storage_info_unittest.py index 699cd501..844465da 100644 --- a/catapult/dependency_manager/dependency_manager/cloud_storage_info_unittest.py +++ b/catapult/dependency_manager/dependency_manager/cloud_storage_info_unittest.py @@ -144,7 +144,7 @@ class TestGetRemotePath(fake_filesystem_unittest.TestCase): # All of the needed information is given, but the downloaded path doesn't # exists after calling cloud storage. self.fs.RemoveObject(self.download_path) - cs_get_mock.side_effect = [True] + cs_get_mock.side_effect = [True] # pylint: disable=redefined-variable-type self.assertRaises( exceptions.FileNotFoundError, self.cs_info.GetRemotePath) diff --git a/catapult/dependency_manager/dependency_manager/dependency_info.py b/catapult/dependency_manager/dependency_manager/dependency_info.py index 942d57e9..899657ef 100644 --- a/catapult/dependency_manager/dependency_manager/dependency_info.py +++ b/catapult/dependency_manager/dependency_manager/dependency_info.py @@ -16,7 +16,7 @@ class DependencyInfo(object): for error messages to improve debugging. Optional: - local_paths: A list of paths to search in order for a local file. + local_path_info: A LocalPathInfo instance. cloud_storage_info: An instance of CloudStorageInfo. """ # TODO(aiolos): update the above doc string for A) the usage of zip files diff --git a/catapult/dependency_manager/dependency_manager/local_path_info.py b/catapult/dependency_manager/dependency_manager/local_path_info.py index 0103e8f7..de544c77 100644 --- a/catapult/dependency_manager/dependency_manager/local_path_info.py +++ b/catapult/dependency_manager/dependency_manager/local_path_info.py @@ -8,9 +8,29 @@ import os class LocalPathInfo(object): def __init__(self, path_priority_groups): + """Container for a set of local file paths where a given dependency + can be stored. + + Organized as a list of groups, where each group is itself a file path list. + See GetLocalPath() to understand how they are used. + + Args: + path_priority_groups: Can be either None, or a list of file path + strings (corresponding to a list of groups, where each group has + a single file path), or a list of a list of file path strings + (i.e. a list of groups). + """ self._path_priority_groups = self._ParseLocalPaths(path_priority_groups) def GetLocalPath(self): + """Look for a local file, and return its path. + + Looks for the first group which has at least one existing file path. Then + returns the most-recent of these files. + + Returns: + Local file path, if found, or None otherwise. + """ for priority_group in self._path_priority_groups: priority_group = filter(os.path.exists, priority_group) if not priority_group: @@ -19,10 +39,19 @@ class LocalPathInfo(object): return None def IsPathInLocalPaths(self, path): + """Returns true if |path| is in one of this instance's file path lists.""" return any( path in priority_group for priority_group in self._path_priority_groups) def Update(self, local_path_info): + """Update this object from the content of another LocalPathInfo instance. + + Any file path from |local_path_info| that is not already contained in the + current instance will be added into new groups to it. + + Args: + local_path_info: Another LocalPathInfo instance, or None. + """ if not local_path_info: return for priority_group in local_path_info._path_priority_groups: diff --git a/catapult/dependency_manager/dependency_manager/local_path_info_unittest.py b/catapult/dependency_manager/dependency_manager/local_path_info_unittest.py new file mode 100644 index 00000000..83921fad --- /dev/null +++ b/catapult/dependency_manager/dependency_manager/local_path_info_unittest.py @@ -0,0 +1,136 @@ +# Copyright 2018 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. + +import os + +from pyfakefs import fake_filesystem_unittest + +import dependency_manager + +def _CreateFile(path): + """Create file at specific |path|, with specific |content|.""" + with open(path, 'wb') as f: + f.write('x') + + +def _ChangeFileTime(path, time0, days): + new_time = time0 + (days * 24 * 60 * 60) + os.utime(path, (new_time, new_time)) + + +class LocalPathInfoTest(fake_filesystem_unittest.TestCase): + + def setUp(self): + self.setUpPyfakefs() + + def tearDown(self): + self.tearDownPyfakefs() + + def testEmptyInstance(self): + path_info = dependency_manager.LocalPathInfo(None) + self.assertIsNone(path_info.GetLocalPath()) + self.assertFalse(path_info.IsPathInLocalPaths('file.txt')) + + def testSimpleGroupWithOnePath(self): + path_info = dependency_manager.LocalPathInfo(['file.txt']) + self.assertTrue(path_info.IsPathInLocalPaths('file.txt')) + self.assertFalse(path_info.IsPathInLocalPaths('other.txt')) + + # GetLocalPath returns None if the file doesn't exist. + # Otherwise it will return the file path. + self.assertIsNone(path_info.GetLocalPath()) + _CreateFile('file.txt') + self.assertEqual('file.txt', path_info.GetLocalPath()) + + def testSimpleGroupsWithMultiplePaths(self): + path_info = dependency_manager.LocalPathInfo( + [['file1', 'file2', 'file3']]) + self.assertTrue(path_info.IsPathInLocalPaths('file1')) + self.assertTrue(path_info.IsPathInLocalPaths('file2')) + self.assertTrue(path_info.IsPathInLocalPaths('file3')) + + _CreateFile('file1') + _CreateFile('file2') + _CreateFile('file3') + s = os.stat('file1') + time0 = s.st_mtime + + _ChangeFileTime('file1', time0, 4) + _ChangeFileTime('file2', time0, 2) + _ChangeFileTime('file3', time0, 0) + self.assertEqual('file1', path_info.GetLocalPath()) + + _ChangeFileTime('file1', time0, 0) + _ChangeFileTime('file2', time0, 4) + _ChangeFileTime('file3', time0, 2) + self.assertEqual('file2', path_info.GetLocalPath()) + + _ChangeFileTime('file1', time0, 2) + _ChangeFileTime('file2', time0, 0) + _ChangeFileTime('file3', time0, 4) + self.assertEqual('file3', path_info.GetLocalPath()) + + def testMultipleGroupsWithSinglePaths(self): + path_info = dependency_manager.LocalPathInfo( + ['file1', 'file2', 'file3']) + self.assertTrue(path_info.IsPathInLocalPaths('file1')) + self.assertTrue(path_info.IsPathInLocalPaths('file2')) + self.assertTrue(path_info.IsPathInLocalPaths('file3')) + + self.assertIsNone(path_info.GetLocalPath()) + _CreateFile('file3') + self.assertEqual('file3', path_info.GetLocalPath()) + _CreateFile('file2') + self.assertEqual('file2', path_info.GetLocalPath()) + _CreateFile('file1') + self.assertEqual('file1', path_info.GetLocalPath()) + + def testMultipleGroupsWithMultiplePaths(self): + path_info = dependency_manager.LocalPathInfo([ + ['file1', 'file2'], + ['file3', 'file4']]) + self.assertTrue(path_info.IsPathInLocalPaths('file1')) + self.assertTrue(path_info.IsPathInLocalPaths('file2')) + self.assertTrue(path_info.IsPathInLocalPaths('file3')) + self.assertTrue(path_info.IsPathInLocalPaths('file4')) + + _CreateFile('file1') + _CreateFile('file3') + s = os.stat('file1') + time0 = s.st_mtime + + # Check that file1 is always returned, even if it is not the most recent + # file, because it is part of the first group and exists. + _ChangeFileTime('file1', time0, 2) + _ChangeFileTime('file3', time0, 0) + self.assertEqual('file1', path_info.GetLocalPath()) + + _ChangeFileTime('file1', time0, 0) + _ChangeFileTime('file3', time0, 2) + self.assertEqual('file1', path_info.GetLocalPath()) + + def testUpdate(self): + path_info1 = dependency_manager.LocalPathInfo( + [['file1', 'file2']]) # One group with two files. + path_info2 = dependency_manager.LocalPathInfo( + ['file1', 'file2', 'file3']) # Three groups + self.assertTrue(path_info1.IsPathInLocalPaths('file1')) + self.assertTrue(path_info1.IsPathInLocalPaths('file2')) + self.assertFalse(path_info1.IsPathInLocalPaths('file3')) + + _CreateFile('file3') + self.assertIsNone(path_info1.GetLocalPath()) + + path_info1.Update(path_info2) + self.assertTrue(path_info1.IsPathInLocalPaths('file1')) + self.assertTrue(path_info1.IsPathInLocalPaths('file2')) + self.assertTrue(path_info1.IsPathInLocalPaths('file3')) + self.assertEqual('file3', path_info1.GetLocalPath()) + + _CreateFile('file1') + time0 = os.stat('file1').st_mtime + _ChangeFileTime('file3', time0, 2) # Make file3 more recent. + + # Check that file3 is in a later group. + self.assertEqual('file1', path_info1.GetLocalPath()) diff --git a/catapult/dependency_manager/dependency_manager/manager.py b/catapult/dependency_manager/dependency_manager/manager.py index 7ebb46d3..28fc5320 100644 --- a/catapult/dependency_manager/dependency_manager/manager.py +++ b/catapult/dependency_manager/dependency_manager/manager.py @@ -33,7 +33,7 @@ class DependencyManager(object): local files for the same platform will first look in those from config1, then those from config2, and finally those from config3. """ - if configs is None or type(configs) != list: + if configs is None or not isinstance(configs, list): raise ValueError( 'Must supply a list of config files to DependencyManager') # self._lookup_dict is a dictionary with the following format: diff --git a/catapult/dependency_manager/dependency_manager/uploader.py b/catapult/dependency_manager/dependency_manager/uploader.py index e88b7c04..d00d20cc 100644 --- a/catapult/dependency_manager/dependency_manager/uploader.py +++ b/catapult/dependency_manager/dependency_manager/uploader.py @@ -100,7 +100,7 @@ class CloudStorageUploader(object): return cloud_storage_changed def __eq__(self, other, msg=None): - if type(self) != type(other): + if not isinstance(self, type(other)): return False return (self._local_path == other._local_path and self._cs_remote_path == other._cs_remote_path and diff --git a/catapult/devil/BUILD.gn b/catapult/devil/BUILD.gn new file mode 100644 index 00000000..661a24fd --- /dev/null +++ b/catapult/devil/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2017 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. + +import("//build/symlink.gni") +import("//testing/android/empty_apk/empty_apk.gni") + +empty_apk("empty_system_webview_apk") { + package_name = "com.android.webview" + apk_name = "EmptySystemWebView" +} + +group("devil") { + testonly = true + deps = [ + ":empty_system_webview_apk", + "//buildtools/third_party/libc++($host_toolchain)", + "//tools/android/forwarder2", + "//tools/android/md5sum", + ] +} diff --git a/catapult/devil/devil/android/battery_utils.py b/catapult/devil/devil/android/battery_utils.py index a8a08a96..9c83b5b0 100644 --- a/catapult/devil/devil/android/battery_utils.py +++ b/catapult/devil/devil/android/battery_utils.py @@ -11,6 +11,7 @@ import contextlib import csv import logging +from devil.android import crash_handler from devil.android import decorators from devil.android import device_errors from devil.android import device_utils @@ -374,7 +375,12 @@ class BatteryUtils(object): Returns: True if the device is charging, false otherwise. """ - battery_info = self.GetBatteryInfo() + # Wrapper function so that we can use `RetryOnSystemCrash`. + def GetBatteryInfoHelper(device): + return self.GetBatteryInfo() + + battery_info = crash_handler.RetryOnSystemCrash( + GetBatteryInfoHelper, self._device) for k in ('AC powered', 'USB powered', 'Wireless powered'): if (k in battery_info and battery_info[k].lower() in ('true', '1', 'yes')): diff --git a/catapult/devil/devil/android/decorators.py b/catapult/devil/devil/android/decorators.py index 3844b49a..93e10544 100644 --- a/catapult/devil/devil/android/decorators.py +++ b/catapult/devil/devil/android/decorators.py @@ -59,7 +59,7 @@ def _TimeoutRetryWrapper( raise device_errors.CommandTimeoutError(str(e)), None, ( sys.exc_info()[2]) except cmd_helper.TimeoutError as e: - raise device_errors.CommandTimeoutError(str(e)), None, ( + raise device_errors.CommandTimeoutError(str(e), output=e.output), None, ( sys.exc_info()[2]) return timeout_retry_wrapper diff --git a/catapult/devil/devil/android/device_errors.py b/catapult/devil/devil/android/device_errors.py index cd0266c8..e6893a4f 100644 --- a/catapult/devil/devil/android/device_errors.py +++ b/catapult/devil/devil/android/device_errors.py @@ -148,7 +148,9 @@ class AdbShellCommandFailedError(AdbCommandFailedError): class CommandTimeoutError(base_error.BaseError): """Exception for command timeouts.""" - pass + def __init__(self, message, is_infra_error=False, output=None): + super(CommandTimeoutError, self).__init__(message, is_infra_error) + self.output = output class DeviceUnreachableError(base_error.BaseError): diff --git a/catapult/devil/devil/android/device_utils.py b/catapult/devil/devil/android/device_utils.py index 518e4393..575865bd 100644 --- a/catapult/devil/devil/android/device_utils.py +++ b/catapult/devil/devil/android/device_utils.py @@ -13,6 +13,7 @@ import collections import fnmatch import json import logging +import math import os import posixpath import pprint @@ -209,6 +210,7 @@ _SPECIAL_ROOT_DEVICE_LIST = [ 'marlin', # Pixel XL 'sailfish', # Pixel 'taimen', # Pixel 2 XL + 'vega', # Lenovo Mirage Solo 'walleye', # Pixel 2 ] _IMEI_RE = re.compile(r' Device ID = (.+)$') @@ -425,6 +427,20 @@ class DeviceUtils(object): def HasRoot(self, timeout=None, retries=None): """Checks whether or not adbd has root privileges. + A device is considered to have root if all commands are implicitly run + with elevated privileges, i.e. without having to use "su" to run them. + + Note that some devices do not allow this implicit privilige elevation, + but _can_ run commands as root just fine when done explicitly with "su". + To check if your device can run commands with elevated privileges at all + use: + + device.HasRoot() or device.NeedsSU() + + Luckily, for the most part you don't need to worry about this and using + RunShellCommand(cmd, as_root=True) will figure out for you the right + command incantation to run with elevated privileges. + Args: timeout: timeout in seconds retries: number of retries @@ -1004,8 +1020,8 @@ class DeviceUtils(object): @decorators.WithTimeoutAndRetriesFromInstance() def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None, env=None, run_as=None, as_root=False, single_line=False, - large_output=False, raw_output=False, timeout=None, - retries=None): + large_output=False, raw_output=False, + ensure_logs_on_timeout=False, timeout=None, retries=None): """Run an ADB shell command. The command to run |cmd| should be a sequence of program arguments @@ -1048,6 +1064,10 @@ class DeviceUtils(object): this large output will be truncated. raw_output: Whether to only return the raw output (no splitting into lines). + ensure_logs_on_timeout: If True, will use a slightly smaller timeout for + the internal adb command, which allows to retrive logs on timeout. + Note that that logs are not guaranteed to be produced with this option + as adb command may still hang and fail to respect the reduced timeout. timeout: timeout in seconds retries: number of retries @@ -1071,7 +1091,7 @@ class DeviceUtils(object): return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) def run(cmd): - return self.adb.Shell(cmd) + return self.adb.Shell(cmd, ensure_logs_on_timeout=ensure_logs_on_timeout) def handle_check_return(cmd): try: @@ -2805,7 +2825,7 @@ class DeviceUtils(object): return parallelizer.SyncParallelizer(devices) @classmethod - def HealthyDevices(cls, blacklist=None, device_arg='default', retry=True, + def HealthyDevices(cls, blacklist=None, device_arg='default', retries=1, abis=None, **kwargs): """Returns a list of DeviceUtils instances. @@ -2828,8 +2848,9 @@ class DeviceUtils(object): blacklisted. ['A', 'B', ...] -> Returns instances for the subset that is not blacklisted. - retry: If true, will attempt to restart adb server and query it again if - no devices are found. + retries: Number of times to restart adb server and query it again if no + devices are found on the previous attempts, with exponential backoffs + up to 60s between each retry. abis: A list of ABIs for which the device needs to support at least one of (optional). A device serial, or a list of device serials (optional). @@ -2891,15 +2912,20 @@ class DeviceUtils(object): raise device_errors.MultipleDevicesError(devices) return sorted(devices) - try: - return _get_devices() - except device_errors.NoDevicesError: - if not retry: - raise - logger.warning( - 'No devices found. Will try again after restarting adb server.') - RestartServer() - return _get_devices() + for attempt in xrange(retries+1): + try: + return _get_devices() + except device_errors.NoDevicesError: + if attempt == retries: + logging.error('No devices found after exhausting all retries.') + raise + # math.pow returns floats, so cast to int for easier testing + sleep_s = min(int(math.pow(2, attempt + 1)), 60) + logger.warning( + 'No devices found. Will try again after restarting adb server ' + 'and a short nap of %d s.', sleep_s) + time.sleep(sleep_s) + RestartServer() @decorators.WithTimeoutAndRetriesFromInstance() def RestartAdbd(self, timeout=None, retries=None): diff --git a/catapult/devil/devil/android/device_utils_test.py b/catapult/devil/devil/android/device_utils_test.py index 88c91b51..e0ed666c 100755 --- a/catapult/devil/devil/android/device_utils_test.py +++ b/catapult/devil/devil/android/device_utils_test.py @@ -316,28 +316,32 @@ class DeviceUtilsHasRootTest(DeviceUtilsTest): def testHasRoot_true(self): with self.patch_call(self.call.device.product_name, return_value='notasailfish'), ( - self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n')): + self.assertCall(self.call.adb.Shell( + 'ls /root', ensure_logs_on_timeout=False), 'foo\n')): self.assertTrue(self.device.HasRoot()) def testhasRootSpecial_true(self): with self.patch_call(self.call.device.product_name, return_value='sailfish'), ( - self.assertCall(self.call.adb.Shell('getprop service.adb.root'), - '1\n')): + self.assertCall( + self.call.adb.Shell('getprop service.adb.root', + ensure_logs_on_timeout=False), '1\n')): self.assertTrue(self.device.HasRoot()) def testHasRoot_false(self): with self.patch_call(self.call.device.product_name, return_value='notasailfish'), ( - self.assertCall(self.call.adb.Shell('ls /root'), - self.ShellError())): + self.assertCall( + self.call.adb.Shell( + 'ls /root', ensure_logs_on_timeout=False), self.ShellError())): self.assertFalse(self.device.HasRoot()) def testHasRootSpecial_false(self): with self.patch_call(self.call.device.product_name, return_value='sailfish'), ( - self.assertCall(self.call.adb.Shell('getprop service.adb.root'), - '\n')): + self.assertCall( + self.call.adb.Shell( + 'getprop service.adb.root', ensure_logs_on_timeout=False), '\n')): self.assertFalse(self.device.HasRoot()) @@ -445,7 +449,8 @@ class DeviceUtils_GetApplicationVersionTest(DeviceUtilsTest): def test_GetApplicationVersion_exists(self): with self.assertCalls( - (self.call.adb.Shell('dumpsys package com.android.chrome'), + (self.call.adb.Shell( + 'dumpsys package com.android.chrome', ensure_logs_on_timeout=False), 'Packages:\n' ' Package [com.android.chrome] (3901ecfb):\n' ' userId=1234 gids=[123, 456, 789]\n' @@ -456,13 +461,16 @@ class DeviceUtils_GetApplicationVersionTest(DeviceUtilsTest): def test_GetApplicationVersion_notExists(self): with self.assertCalls( - (self.call.adb.Shell('dumpsys package com.android.chrome'), '')): + (self.call.adb.Shell( + 'dumpsys package com.android.chrome', ensure_logs_on_timeout=False), + '')): self.assertEquals(None, self.device.GetApplicationVersion('com.android.chrome')) def test_GetApplicationVersion_fails(self): with self.assertCalls( - (self.call.adb.Shell('dumpsys package com.android.chrome'), + (self.call.adb.Shell( + 'dumpsys package com.android.chrome', ensure_logs_on_timeout=False), 'Packages:\n' ' Package [com.android.chrome] (3901ecfb):\n' ' userId=1234 gids=[123, 456, 789]\n' @@ -520,7 +528,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -534,7 +543,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -542,7 +552,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): # boot_completed (self.call.device.GetProp('sys.boot_completed', cache=False), '1'), # wifi_enabled - (self.call.adb.Shell('dumpsys wifi'), + (self.call.adb.Shell( + 'dumpsys wifi', ensure_logs_on_timeout=False), 'stuff\nWi-Fi is enabled\nmore stuff\n')): self.device.WaitUntilFullyBooted(wifi=True) @@ -559,7 +570,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): (self.call.device.GetExternalStoragePath(), self.AdbCommandError()), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -573,7 +585,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -598,13 +611,18 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), + self.ShellError()), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), + self.ShellError()), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), self.TimeoutError())): with self.assertRaises(device_errors.CommandTimeoutError): self.device.WaitUntilFullyBooted(wifi=False) @@ -614,7 +632,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -635,7 +654,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -655,7 +675,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): self.call.adb.WaitForDevice(), # sd_card_ready (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), - (self.call.adb.Shell('test -d /fake/storage/path'), ''), + (self.call.adb.Shell( + 'test -d /fake/storage/path', ensure_logs_on_timeout=False), ''), # pm_ready (self.call.device._GetApplicationPathsInternal('android', skip_cache=True), @@ -663,11 +684,14 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest): # boot_completed (self.call.device.GetProp('sys.boot_completed', cache=False), '1'), # wifi_enabled - (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'), + (self.call.adb.Shell( + 'dumpsys wifi', ensure_logs_on_timeout=False), 'stuff\nmore stuff\n'), # wifi_enabled - (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'), + (self.call.adb.Shell( + 'dumpsys wifi', ensure_logs_on_timeout=False), 'stuff\nmore stuff\n'), # wifi_enabled - (self.call.adb.Shell('dumpsys wifi'), self.TimeoutError())): + (self.call.adb.Shell( + 'dumpsys wifi', ensure_logs_on_timeout=False), self.TimeoutError())): with self.assertRaises(device_errors.CommandTimeoutError): self.device.WaitUntilFullyBooted(wifi=True) @@ -930,30 +954,36 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): self.device.NeedsSU = mock.Mock(return_value=False) def testRunShellCommand_commandAsList(self): - with self.assertCall(self.call.adb.Shell('pm list packages'), ''): + with self.assertCall(self.call.adb.Shell( + 'pm list packages', ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( ['pm', 'list', 'packages'], check_return=True) def testRunShellCommand_commandAsListQuoted(self): - with self.assertCall(self.call.adb.Shell("echo 'hello world' '$10'"), ''): + with self.assertCall(self.call.adb.Shell( + "echo 'hello world' '$10'", ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( ['echo', 'hello world', '$10'], check_return=True) def testRunShellCommand_commandAsString(self): - with self.assertCall(self.call.adb.Shell('echo "$VAR"'), ''): + with self.assertCall(self.call.adb.Shell( + 'echo "$VAR"', ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( 'echo "$VAR"', shell=True, check_return=True) def testNewRunShellImpl_withEnv(self): with self.assertCall( - self.call.adb.Shell('VAR=some_string echo "$VAR"'), ''): + self.call.adb.Shell( + 'VAR=some_string echo "$VAR"', ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( 'echo "$VAR"', shell=True, check_return=True, env={'VAR': 'some_string'}) def testNewRunShellImpl_withEnvQuoted(self): with self.assertCall( - self.call.adb.Shell('PATH="$PATH:/other/path" run_this'), ''): + self.call.adb.Shell( + 'PATH="$PATH:/other/path" run_this', ensure_logs_on_timeout=False), + ''): self.device.RunShellCommand( ['run_this'], check_return=True, env={'PATH': '$PATH:/other/path'}) @@ -963,13 +993,17 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): ['some_cmd'], check_return=True, env={'INVALID NAME': 'value'}) def testNewRunShellImpl_withCwd(self): - with self.assertCall(self.call.adb.Shell('cd /some/test/path && ls'), ''): + with self.assertCall(self.call.adb.Shell( + 'cd /some/test/path && ls', ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( ['ls'], check_return=True, cwd='/some/test/path') def testNewRunShellImpl_withCwdQuoted(self): with self.assertCall( - self.call.adb.Shell("cd '/some test/path with/spaces' && ls"), ''): + self.call.adb.Shell( + "cd '/some test/path with/spaces' && ls", + ensure_logs_on_timeout=False), + ''): self.device.RunShellCommand( ['ls'], check_return=True, cwd='/some test/path with/spaces') @@ -980,7 +1014,9 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): (mock.call.devil.android.device_temp_file.DeviceTempFile( self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')), self.call.device._WriteFileWithPush('/sdcard/temp-123.sh', expected_cmd), - (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')): + (self.call.adb.Shell( + 'sh /sdcard/temp-123.sh', ensure_logs_on_timeout=False), + payload + '\n')): self.assertEquals( [payload], self.device.RunShellCommand(['echo', payload], check_return=True)) @@ -995,7 +1031,9 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): (mock.call.devil.android.device_temp_file.DeviceTempFile( self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')), self.call.device._WriteFileWithPush('/sdcard/temp-123.sh', expected_cmd), - (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')): + (self.call.adb.Shell( + 'sh /sdcard/temp-123.sh', ensure_logs_on_timeout=False), + payload + '\n')): self.assertEquals( [payload], self.device.RunShellCommand( @@ -1007,7 +1045,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.NeedsSU(), True), (self.call.device._Su(expected_cmd_without_su), expected_cmd), - (self.call.adb.Shell(expected_cmd), '')): + (self.call.adb.Shell( + expected_cmd, ensure_logs_on_timeout=False), '')): self.device.RunShellCommand( ['setprop', 'service.adb.root', '0'], check_return=True, as_root=True) @@ -1016,7 +1055,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): expected_cmd_without_run_as = "sh -c 'mkdir -p files'" expected_cmd = ( 'run-as org.devil.test_package %s' % expected_cmd_without_run_as) - with self.assertCall(self.call.adb.Shell(expected_cmd), ''): + with self.assertCall(self.call.adb.Shell( + expected_cmd, ensure_logs_on_timeout=False), ''): self.device.RunShellCommand( ['mkdir', '-p', 'files'], check_return=True, run_as='org.devil.test_package') @@ -1031,7 +1071,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.NeedsSU(), True), (self.call.device._Su(expected_cmd_without_su), expected_cmd), - (self.call.adb.Shell(expected_cmd), '')): + (self.call.adb.Shell( + expected_cmd, ensure_logs_on_timeout=False), '')): self.device.RunShellCommand( ['mkdir', '-p', 'files'], check_return=True, run_as='org.devil.test_package', @@ -1039,14 +1080,16 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_manyLines(self): cmd = 'ls /some/path' - with self.assertCall(self.call.adb.Shell(cmd), 'file1\nfile2\nfile3\n'): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), 'file1\nfile2\nfile3\n'): self.assertEquals( ['file1', 'file2', 'file3'], self.device.RunShellCommand(cmd.split(), check_return=True)) def testRunShellCommand_manyLinesRawOutput(self): cmd = 'ls /some/path' - with self.assertCall(self.call.adb.Shell(cmd), '\rfile1\nfile2\r\nfile3\n'): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), '\rfile1\nfile2\r\nfile3\n'): self.assertEquals( '\rfile1\nfile2\r\nfile3\n', self.device.RunShellCommand( @@ -1054,7 +1097,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_singleLine_success(self): cmd = 'echo $VALUE' - with self.assertCall(self.call.adb.Shell(cmd), 'some value\n'): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), 'some value\n'): self.assertEquals( 'some value', self.device.RunShellCommand( @@ -1062,7 +1106,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_singleLine_successEmptyLine(self): cmd = 'echo $VALUE' - with self.assertCall(self.call.adb.Shell(cmd), '\n'): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), '\n'): self.assertEquals( '', self.device.RunShellCommand( @@ -1070,7 +1115,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_singleLine_successWithoutEndLine(self): cmd = 'echo -n $VALUE' - with self.assertCall(self.call.adb.Shell(cmd), 'some value'): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), 'some value'): self.assertEquals( 'some value', self.device.RunShellCommand( @@ -1078,7 +1124,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_singleLine_successNoOutput(self): cmd = 'echo -n $VALUE' - with self.assertCall(self.call.adb.Shell(cmd), ''): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), ''): self.assertEquals( '', self.device.RunShellCommand( @@ -1086,7 +1133,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_singleLine_failTooManyLines(self): cmd = 'echo $VALUE' - with self.assertCall(self.call.adb.Shell(cmd), + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), 'some value\nanother value\n'): with self.assertRaises(device_errors.CommandFailedError): self.device.RunShellCommand( @@ -1095,7 +1143,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_checkReturn_success(self): cmd = 'echo $ANDROID_DATA' output = '/data\n' - with self.assertCall(self.call.adb.Shell(cmd), output): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), output): self.assertEquals( [output.rstrip()], self.device.RunShellCommand(cmd, shell=True, check_return=True)) @@ -1103,14 +1152,16 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_checkReturn_failure(self): cmd = 'ls /root' output = 'opendir failed, Permission denied\n' - with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), self.ShellError(output)): with self.assertRaises(device_errors.AdbCommandFailedError): self.device.RunShellCommand(cmd.split(), check_return=True) def testRunShellCommand_checkReturn_disabled(self): cmd = 'ls /root' output = 'opendir failed, Permission denied\n' - with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), self.ShellError(output)): self.assertEquals( [output.rstrip()], self.device.RunShellCommand(cmd.split(), check_return=False)) @@ -1122,7 +1173,7 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): with self.assertCalls( (mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb), temp_file), - (self.call.adb.Shell(cmd_redirect)), + (self.call.adb.Shell(cmd_redirect, ensure_logs_on_timeout=False)), (self.call.device.ReadFile(temp_file.name, force_pull=True), 'something')): self.assertEquals( @@ -1132,7 +1183,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): def testRunShellCommand_largeOutput_disabledNoTrigger(self): cmd = 'something' - with self.assertCall(self.call.adb.Shell(cmd), self.ShellError('')): + with self.assertCall(self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), self.ShellError('')): with self.assertRaises(device_errors.AdbCommandFailedError): self.device.RunShellCommand([cmd], check_return=True) @@ -1141,10 +1193,12 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest): temp_file = MockTempFile('/sdcard/temp-123') cmd_redirect = '( %s )>%s 2>&1' % (cmd, temp_file.name) with self.assertCalls( - (self.call.adb.Shell(cmd), self.ShellError('', None)), + (self.call.adb.Shell( + cmd, ensure_logs_on_timeout=False), self.ShellError('', None)), (mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb), temp_file), - (self.call.adb.Shell(cmd_redirect)), + (self.call.adb.Shell( + cmd_redirect, ensure_logs_on_timeout=False)), (self.call.device.ReadFile(mock.ANY, force_pull=True), 'something')): self.assertEquals( @@ -1210,7 +1264,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process.thing', 5678))), - (self.call.adb.Shell('kill -9 1234 5678'), '')): + (self.call.adb.Shell( + 'kill -9 1234 5678', ensure_logs_on_timeout=False), '')): self.assertEquals( 2, self.device.KillAll('some.process', blocking=False)) @@ -1218,7 +1273,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process.thing', 5678))), - (self.call.adb.Shell('kill -9 1234 5678'), ''), + (self.call.adb.Shell( + 'kill -9 1234 5678', ensure_logs_on_timeout=False), ''), (self.call.device.ListProcesses('some.process'), Processes(('some.process.thing', 5678))), (self.call.device.ListProcesses('some.process'), @@ -1231,7 +1287,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process.thing', 5678))), - (self.call.adb.Shell('kill -9 1234'), '')): + (self.call.adb.Shell( + 'kill -9 1234', ensure_logs_on_timeout=False), '')): self.assertEquals( 1, self.device.KillAll('some.process', exact=True, blocking=False)) @@ -1239,7 +1296,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process.thing', 5678))), - (self.call.adb.Shell('kill -9 1234'), ''), + (self.call.adb.Shell( + 'kill -9 1234', ensure_logs_on_timeout=False), ''), (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process.thing', 5678))), (self.call.device.ListProcesses('some.process'), @@ -1254,7 +1312,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): (self.call.device.NeedsSU(), True), (self.call.device._Su("sh -c 'kill -9 1234'"), "su -c sh -c 'kill -9 1234'"), - (self.call.adb.Shell("su -c sh -c 'kill -9 1234'"), '')): + (self.call.adb.Shell( + "su -c sh -c 'kill -9 1234'", ensure_logs_on_timeout=False), '')): self.assertEquals( 1, self.device.KillAll('some.process', as_root=True)) @@ -1262,7 +1321,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234))), - (self.call.adb.Shell('kill -15 1234'), '')): + (self.call.adb.Shell( + 'kill -15 1234', ensure_logs_on_timeout=False), '')): self.assertEquals( 1, self.device.KillAll('some.process', signum=device_signal.SIGTERM)) @@ -1270,7 +1330,8 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.ListProcesses('some.process'), Processes(('some.process', 1234), ('some.process', 4567))), - (self.call.adb.Shell('kill -15 1234 4567'), '')): + (self.call.adb.Shell( + 'kill -15 1234 4567', ensure_logs_on_timeout=False), '')): self.assertEquals( 2, self.device.KillAll('some.process', signum=device_signal.SIGTERM)) @@ -1280,8 +1341,10 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): def testStartActivity_actionOnly(self): test_intent = intent.Intent(action='android.intent.action.VIEW') with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1290,9 +1353,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): package='test.package', activity='.Main') with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1301,9 +1366,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): package='test.package', activity='.Main') with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Error: Failed to start test activity'): with self.assertRaises(device_errors.CommandFailedError): self.device.StartActivity(test_intent) @@ -1313,10 +1380,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): package='test.package', activity='.Main') with self.assertCall( - self.call.adb.Shell('am start ' - '-W ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-W ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent, blocking=True) @@ -1326,10 +1395,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): activity='.Main', category='android.intent.category.HOME') with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-c android.intent.category.HOME ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-c android.intent.category.HOME ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1340,11 +1411,13 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): category=['android.intent.category.HOME', 'android.intent.category.BROWSABLE']) with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-c android.intent.category.HOME ' - '-c android.intent.category.BROWSABLE ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-c android.intent.category.HOME ' + '-c android.intent.category.BROWSABLE ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1354,10 +1427,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): activity='.Main', data='http://www.google.com/') with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-d http://www.google.com/ ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-d http://www.google.com/ ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1367,10 +1442,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): activity='.Main', extras={'foo': 'test'}) with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main ' - '--es foo test'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main ' + '--es foo test', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1380,10 +1457,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): activity='.Main', extras={'foo': True}) with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main ' - '--ez foo True'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main ' + '--ez foo True', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1393,10 +1472,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): activity='.Main', extras={'foo': 123}) with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main ' - '--ei foo 123'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main ' + '--ei foo 123', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1405,10 +1486,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): package='test.package', activity='.Main') with self.assertCall( - self.call.adb.Shell('am start ' - '--start-profiler test_trace_file.out ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '--start-profiler test_trace_file.out ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent, trace_file_name='test_trace_file.out') @@ -1418,10 +1501,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): package='test.package', activity='.Main') with self.assertCall( - self.call.adb.Shell('am start ' - '-S ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start ' + '-S ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent, force_stop=True) @@ -1434,10 +1519,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest): intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED ]) with self.assertCall( - self.call.adb.Shell('am start ' - '-a android.intent.action.VIEW ' - '-n test.package/.Main ' - '-f 0x10200000'), + self.call.adb.Shell( + 'am start ' + '-a android.intent.action.VIEW ' + '-n test.package/.Main ' + '-f 0x10200000', + ensure_logs_on_timeout=False), 'Starting: Intent { act=android.intent.action.VIEW }'): self.device.StartActivity(test_intent) @@ -1450,9 +1537,11 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest): with self.patch_call(self.call.device.build_version_sdk, return_value=version_codes.NOUGAT): with self.assertCall( - self.call.adb.Shell('am startservice ' - '-a android.intent.action.START ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am startservice ' + '-a android.intent.action.START ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting service: Intent { act=android.intent.action.START }'): self.device.StartService(test_intent) @@ -1463,9 +1552,11 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest): with self.patch_call(self.call.device.build_version_sdk, return_value=version_codes.NOUGAT): with self.assertCall( - self.call.adb.Shell('am startservice ' - '-a android.intent.action.START ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am startservice ' + '-a android.intent.action.START ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Error: Failed to start test service'): with self.assertRaises(device_errors.CommandFailedError): self.device.StartService(test_intent) @@ -1477,10 +1568,12 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest): with self.patch_call(self.call.device.build_version_sdk, return_value=version_codes.NOUGAT): with self.assertCall( - self.call.adb.Shell('am startservice ' - '--user TestUser ' - '-a android.intent.action.START ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am startservice ' + '--user TestUser ' + '-a android.intent.action.START ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting service: Intent { act=android.intent.action.START }'): self.device.StartService(test_intent, user_id='TestUser') @@ -1491,9 +1584,11 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest): with self.patch_call(self.call.device.build_version_sdk, return_value=version_codes.OREO): with self.assertCall( - self.call.adb.Shell('am start-service ' - '-a android.intent.action.START ' - '-n test.package/.Main'), + self.call.adb.Shell( + 'am start-service ' + '-a android.intent.action.START ' + '-n test.package/.Main', + ensure_logs_on_timeout=False), 'Starting service: Intent { act=android.intent.action.START }'): self.device.StartService(test_intent) @@ -1546,7 +1641,9 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest): def testBroadcastIntent_noExtras(self): test_intent = intent.Intent(action='test.package.with.an.INTENT') with self.assertCall( - self.call.adb.Shell('am broadcast -a test.package.with.an.INTENT'), + self.call.adb.Shell( + 'am broadcast -a test.package.with.an.INTENT', + ensure_logs_on_timeout=False), 'Broadcasting: Intent { act=test.package.with.an.INTENT } '): self.device.BroadcastIntent(test_intent) @@ -1555,7 +1652,8 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest): extras={'foo': 'bar value'}) with self.assertCall( self.call.adb.Shell( - "am broadcast -a test.package.with.an.INTENT --es foo 'bar value'"), + "am broadcast -a test.package.with.an.INTENT --es foo 'bar value'", + ensure_logs_on_timeout=False), 'Broadcasting: Intent { act=test.package.with.an.INTENT } '): self.device.BroadcastIntent(test_intent) @@ -1564,7 +1662,8 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest): extras={'foo': None}) with self.assertCall( self.call.adb.Shell( - 'am broadcast -a test.package.with.an.INTENT --esn foo'), + 'am broadcast -a test.package.with.an.INTENT --esn foo', + ensure_logs_on_timeout=False), 'Broadcasting: Intent { act=test.package.with.an.INTENT } '): self.device.BroadcastIntent(test_intent) @@ -1728,7 +1827,8 @@ class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest): class DeviceUtilsSendKeyEventTest(DeviceUtilsTest): def testSendKeyEvent(self): - with self.assertCall(self.call.adb.Shell('input keyevent 66'), ''): + with self.assertCall(self.call.adb.Shell( + 'input keyevent 66', ensure_logs_on_timeout=False), ''): self.device.SendKeyEvent(66) @@ -2067,12 +2167,14 @@ class DeviceUtilsWriteFileTest(DeviceUtilsTest): def testWriteFile_withEcho(self): with self.assertCall(self.call.adb.Shell( - "echo -n the.contents > /test/file/to.write"), ''): + "echo -n the.contents > /test/file/to.write", + ensure_logs_on_timeout=False), ''): self.device.WriteFile('/test/file/to.write', 'the.contents') def testWriteFile_withEchoAndQuotes(self): with self.assertCall(self.call.adb.Shell( - "echo -n 'the contents' > '/test/file/to write'"), ''): + "echo -n 'the contents' > '/test/file/to write'", + ensure_logs_on_timeout=False), ''): self.device.WriteFile('/test/file/to write', 'the contents') def testWriteFile_withEchoAndSU(self): @@ -2081,7 +2183,8 @@ class DeviceUtilsWriteFileTest(DeviceUtilsTest): with self.assertCalls( (self.call.device.NeedsSU(), True), (self.call.device._Su(expected_cmd_without_su), expected_cmd), - (self.call.adb.Shell(expected_cmd), + (self.call.adb.Shell( + expected_cmd, ensure_logs_on_timeout=False), '')): self.device.WriteFile('/test/file', 'contents', as_root=True) @@ -2562,51 +2665,60 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest): class DeviceUtilsGetSetEnforce(DeviceUtilsTest): def testGetEnforce_Enforcing(self): - with self.assertCall(self.call.adb.Shell('getenforce'), 'Enforcing'): + with self.assertCall(self.call.adb.Shell( + 'getenforce', ensure_logs_on_timeout=False), 'Enforcing'): self.assertEqual(True, self.device.GetEnforce()) def testGetEnforce_Permissive(self): - with self.assertCall(self.call.adb.Shell('getenforce'), 'Permissive'): + with self.assertCall(self.call.adb.Shell( + 'getenforce', ensure_logs_on_timeout=False), 'Permissive'): self.assertEqual(False, self.device.GetEnforce()) def testGetEnforce_Disabled(self): - with self.assertCall(self.call.adb.Shell('getenforce'), 'Disabled'): + with self.assertCall(self.call.adb.Shell( + 'getenforce', ensure_logs_on_timeout=False), 'Disabled'): self.assertEqual(None, self.device.GetEnforce()) def testSetEnforce_Enforcing(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 1'), '')): + (self.call.adb.Shell( + 'setenforce 1', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled=True) def testSetEnforce_Permissive(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 0'), '')): + (self.call.adb.Shell( + 'setenforce 0', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled=False) def testSetEnforce_EnforcingWithInt(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 1'), '')): + (self.call.adb.Shell( + 'setenforce 1', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled=1) def testSetEnforce_PermissiveWithInt(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 0'), '')): + (self.call.adb.Shell( + 'setenforce 0', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled=0) def testSetEnforce_EnforcingWithStr(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 1'), '')): + (self.call.adb.Shell( + 'setenforce 1', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled='1') def testSetEnforce_PermissiveWithStr(self): with self.assertCalls( (self.call.device.NeedsSU(), False), - (self.call.adb.Shell('setenforce 0'), '')): + (self.call.adb.Shell( + 'setenforce 0', ensure_logs_on_timeout=False), '')): self.device.SetEnforce(enabled='0') # Not recommended but it works! @@ -2614,12 +2726,14 @@ class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest): def testSetWebViewImplementation_success(self): with self.assertCall(self.call.adb.Shell( - 'cmd webviewupdate set-webview-implementation foo.org'), 'Success'): + 'cmd webviewupdate set-webview-implementation foo.org', + ensure_logs_on_timeout=False), 'Success'): self.device.SetWebViewImplementation('foo.org') def testSetWebViewImplementation_failure(self): with self.assertCall(self.call.adb.Shell( - 'cmd webviewupdate set-webview-implementation foo.org'), 'Oops!'): + 'cmd webviewupdate set-webview-implementation foo.org', + ensure_logs_on_timeout=False), 'Oops!'): with self.assertRaises(device_errors.CommandFailedError): self.device.SetWebViewImplementation('foo.org') @@ -2631,7 +2745,9 @@ class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest): (mock.call.devil.android.device_temp_file.DeviceTempFile( self.adb, suffix='.png'), MockTempFile('/tmp/path/temp-123.png')), - (self.call.adb.Shell('/system/bin/screencap -p /tmp/path/temp-123.png'), + (self.call.adb.Shell( + '/system/bin/screencap -p /tmp/path/temp-123.png', + ensure_logs_on_timeout=False), ''), self.call.device.PullFile('/tmp/path/temp-123.png', '/test/host/screenshot.png')): @@ -2775,7 +2891,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase): (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [_AdbWrapperMock(s) for s in test_serials])): with self.assertRaises(device_errors.NoDevicesError): - device_utils.DeviceUtils.HealthyDevices(device_arg=None, retry=False) + device_utils.DeviceUtils.HealthyDevices(device_arg=None, retries=0) def testHealthyDevices_noneDeviceArg_multiple_attached_ANDROID_SERIAL(self): try: @@ -2818,17 +2934,23 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase): (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [_AdbWrapperMock(s) for s in test_serials])): with self.assertRaises(device_errors.NoDevicesError): - device_utils.DeviceUtils.HealthyDevices(device_arg=[], retry=False) - - def testHealthyDevices_EmptyListDeviceArg_no_attached_with_retry(self): - test_serials = [] - with self.assertCalls( - (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), - [_AdbWrapperMock(s) for s in test_serials]), - (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), - [_AdbWrapperMock(s) for s in test_serials])): + device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=0) + + @mock.patch('time.sleep') + @mock.patch('devil.android.device_utils.RestartServer') + def testHealthyDevices_EmptyListDeviceArg_no_attached_with_retry( + self, mock_restart, mock_sleep): + with self.assertCalls( + (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []), + (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []), + (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []), + (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []), + (mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [])): with self.assertRaises(device_errors.NoDevicesError): - device_utils.DeviceUtils.HealthyDevices(device_arg=[], retry=True) + device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=4) + self.assertEquals(mock_restart.call_count, 4) + self.assertEquals(mock_sleep.call_args_list, [ + mock.call(2), mock.call(4), mock.call(8), mock.call(16)]) def testHealthyDevices_ListDeviceArg(self): device_arg = ['0123456789abcdef', 'fedcba9876543210'] @@ -2850,7 +2972,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase): (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), ARM32_ABI)): with self.assertRaises(device_errors.NoDevicesError): - device_utils.DeviceUtils.HealthyDevices(device_arg=[], retry=False, + device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=0, abis=[ARM64_ABI]) def testHealthyDevices_abisArg_filter_on_abi(self): @@ -2863,7 +2985,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase): (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), ARM32_ABI)): devices = device_utils.DeviceUtils.HealthyDevices(device_arg=[], - retry=False, + retries=0, abis=[ARM64_ABI]) self.assertEquals(1, len(devices)) @@ -3078,7 +3200,9 @@ class DeviceUtilsGetIMEITest(DeviceUtilsTest): ' Device ID = 123454321') with self.assertCalls( (self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'), - (self.call.adb.Shell('dumpsys iphonesubinfo'), dumpsys_output)): + (self.call.adb.Shell( + 'dumpsys iphonesubinfo', ensure_logs_on_timeout=False), + dumpsys_output)): self.assertEquals(self.device.GetIMEI(), '123454321') def testSuccessfulServiceCall(self): @@ -3090,20 +3214,25 @@ class DeviceUtilsGetIMEITest(DeviceUtilsTest): """ with self.assertCalls( (self.call.device.GetProp('ro.build.version.sdk', cache=True), '24'), - (self.call.adb.Shell('service call iphonesubinfo 1'), service_output)): + (self.call.adb.Shell( + 'service call iphonesubinfo 1', ensure_logs_on_timeout=False), + service_output)): self.assertEquals(self.device.GetIMEI(), '765432101234567') def testNoIMEI(self): with self.assertCalls( (self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'), - (self.call.adb.Shell('dumpsys iphonesubinfo'), 'no device id')): + (self.call.adb.Shell( + 'dumpsys iphonesubinfo', ensure_logs_on_timeout=False), + 'no device id')): with self.assertRaises(device_errors.CommandFailedError): self.device.GetIMEI() def testAdbError(self): with self.assertCalls( (self.call.device.GetProp('ro.build.version.sdk', cache=True), '24'), - (self.call.adb.Shell('service call iphonesubinfo 1'), + (self.call.adb.Shell( + 'service call iphonesubinfo 1', ensure_logs_on_timeout=False), self.ShellError())): with self.assertRaises(device_errors.CommandFailedError): self.device.GetIMEI() diff --git a/catapult/devil/devil/android/flag_changer.py b/catapult/devil/devil/android/flag_changer.py index 9cb1c02e..c96dbadc 100644 --- a/catapult/devil/devil/android/flag_changer.py +++ b/catapult/devil/devil/android/flag_changer.py @@ -93,7 +93,8 @@ class FlagChanger(object): A list of flags. """ if self._device.PathExists(self._cmdline_path): - command_line = self._device.ReadFile(self._cmdline_path).strip() + command_line = self._device.ReadFile( + self._cmdline_path, as_root=True).strip() else: command_line = '' flags = _ParseFlags(command_line) @@ -216,9 +217,9 @@ class FlagChanger(object): """ command_line = _SerializeFlags(self._state_stack[-1]) if command_line is not None: - self._device.WriteFile(self._cmdline_path, command_line) + self._device.WriteFile(self._cmdline_path, command_line, as_root=True) else: - self._device.RemovePath(self._cmdline_path, force=True) + self._device.RemovePath(self._cmdline_path, force=True, as_root=True) current_flags = self.GetCurrentFlags() logger.info('Flags now set on the device: %s', current_flags) diff --git a/catapult/devil/devil/android/perf/perf_control.py b/catapult/devil/devil/android/perf/perf_control.py index 9ac85ebc..2aa3b2f2 100644 --- a/catapult/devil/devil/android/perf/perf_control.py +++ b/catapult/devil/devil/android/perf/perf_control.py @@ -9,6 +9,84 @@ import re from devil.android import device_errors logger = logging.getLogger(__name__) +_atexit_messages = set() + + +# Defines how to switch between the default performance configuration +# ('default_mode') and the mode for use when benchmarking ('high_perf_mode'). +# For devices not in the list the defaults are to set up the scaling governor to +# 'performance' and reset it back to 'ondemand' when benchmarking is finished. +# +# The 'default_mode_governor' is mandatory to define, while +# 'high_perf_mode_governor' is not taken into account. The latter is because the +# governor 'performance' is currently used for all benchmarking on all devices. +# +# TODO(crbug.com/383566): Add definitions for all devices used in the perf +# waterfall. +_PERFORMANCE_MODE_DEFINITIONS = { + # Fire TV Edition - 4K + 'AFTKMST12': { + 'default_mode_governor': 'interactive', + }, + 'GT-I9300': { + 'default_mode_governor': 'pegasusq', + }, + 'Galaxy Nexus': { + 'default_mode_governor': 'interactive', + }, + 'Nexus 7': { + 'default_mode_governor': 'interactive', + }, + 'Nexus 10': { + 'default_mode_governor': 'interactive', + }, + 'Nexus 4': { + 'high_perf_mode': { + 'bring_cpu_cores_online': True, + }, + 'default_mode_governor': 'ondemand', + }, + 'Nexus 5': { + # The list of possible GPU frequency values can be found in: + # /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies. + # For CPU cores the possible frequency values are at: + # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies + 'high_perf_mode': { + 'bring_cpu_cores_online': True, + 'cpu_max_freq': 1190400, + 'gpu_max_freq': 200000000, + }, + 'default_mode': { + 'cpu_max_freq': 2265600, + 'gpu_max_freq': 450000000, + }, + 'default_mode_governor': 'ondemand', + }, + 'Nexus 5X': { + 'high_perf_mode': { + 'bring_cpu_cores_online': True, + 'cpu_max_freq': 1248000, + 'gpu_max_freq': 300000000, + }, + 'default_mode': { + 'governor': 'ondemand', + # The SoC is ARM big.LITTLE. The cores 4..5 are big, the 0..3 are LITTLE. + 'cpu_max_freq': {'0..3': 1440000, '4..5': 1824000}, + 'gpu_max_freq': 600000000, + }, + 'default_mode_governor': 'ondemand', + }, +} + + +def _NoisyWarning(message): + message += ' Results may be NOISY!!' + logger.warning(message) + # Add an additional warning at exit, such that it's clear that any results + # may be different/noisy (due to the lack of intended performance mode). + if message not in _atexit_messages: + _atexit_messages.add(message) + atexit.register(logger.warning, message) class PerfControl(object): @@ -21,10 +99,10 @@ class PerfControl(object): def __init__(self, device): self._device = device - self._cpu_files = [ - filename - for filename in self._device.ListDirectory(self._CPU_PATH, as_root=True) - if self._CPU_FILE_PATTERN.match(filename)] + self._cpu_files = [] + for file_name in self._device.ListDirectory(self._CPU_PATH, as_root=True): + if self._CPU_FILE_PATTERN.match(file_name): + self._cpu_files.append(file_name) assert self._cpu_files, 'Failed to detect CPUs.' self._cpu_file_list = ' '.join(self._cpu_files) logger.info('CPUs found: %s', self._cpu_file_list) @@ -36,34 +114,75 @@ class PerfControl(object): (cpu, raw_governors.strip().split() if not exit_code else None) for cpu, raw_governors, exit_code in raw] + def _SetMaxFrequenciesFromMode(self, mode): + """Set maximum frequencies for GPU and CPU cores. + + Args: + mode: A dictionary mapping optional keys 'cpu_max_freq' and 'gpu_max_freq' + to integer values of frequency supported by the device. + """ + cpu_max_freq = mode.get('cpu_max_freq') + if cpu_max_freq: + if not isinstance(cpu_max_freq, dict): + self._SetScalingMaxFreqForCpus(cpu_max_freq, self._cpu_file_list) + else: + for key, max_frequency in cpu_max_freq.iteritems(): + # Convert 'X' to 'cpuX' and 'X..Y' to 'cpuX cpu<X+1> .. cpuY'. + if '..' in key: + range_min, range_max = key.split('..') + range_min, range_max = int(range_min), int(range_max) + else: + range_min = range_max = int(key) + cpu_files = ['cpu%d' % number + for number in xrange(range_min, range_max + 1)] + # Set the |max_frequency| on requested subset of the cores. + self._SetScalingMaxFreqForCpus(max_frequency, ' '.join(cpu_files)) + gpu_max_freq = mode.get('gpu_max_freq') + if gpu_max_freq: + self._SetMaxGpuClock(gpu_max_freq) + def SetHighPerfMode(self): """Sets the highest stable performance mode for the device.""" try: self._device.EnableRoot() except device_errors.CommandFailedError: - message = 'Need root for performance mode. Results may be NOISY!!' - logger.warning(message) - # Add an additional warning at exit, such that it's clear that any results - # may be different/noisy (due to the lack of intended performance mode). - atexit.register(logger.warning, message) + _NoisyWarning('Need root for performance mode.') return - - product_model = self._device.product_model - # TODO(epenner): Enable on all devices (http://crbug.com/383566) - if product_model == 'Nexus 4': - self._ForceAllCpusOnline(True) - if not self._AllCpusAreOnline(): - logger.warning('Failed to force CPUs online. Results may be NOISY!') + mode_definitions = _PERFORMANCE_MODE_DEFINITIONS.get( + self._device.product_model) + if not mode_definitions: + self.SetScalingGovernor('performance') + return + high_perf_mode = mode_definitions.get('high_perf_mode') + if not high_perf_mode: self.SetScalingGovernor('performance') - elif product_model == 'Nexus 5': + return + if high_perf_mode.get('bring_cpu_cores_online', False): self._ForceAllCpusOnline(True) if not self._AllCpusAreOnline(): - logger.warning('Failed to force CPUs online. Results may be NOISY!') - self.SetScalingGovernor('performance') - self._SetScalingMaxFreq(1190400) - self._SetMaxGpuClock(200000000) + _NoisyWarning('Failed to force CPUs online.') + # Scaling governor must be set _after_ bringing all CPU cores online, + # otherwise it would not affect the cores that are currently offline. + self.SetScalingGovernor('performance') + self._SetMaxFrequenciesFromMode(high_perf_mode) + + def SetDefaultPerfMode(self): + """Sets the performance mode for the device to its default mode.""" + if not self._device.HasRoot(): + return + mode_definitions = _PERFORMANCE_MODE_DEFINITIONS.get( + self._device.product_model) + if not mode_definitions: + self.SetScalingGovernor('ondemand') else: - self.SetScalingGovernor('performance') + default_mode_governor = mode_definitions.get('default_mode_governor') + assert default_mode_governor, ('Default mode governor must be provided ' + 'for all perf mode definitions.') + self.SetScalingGovernor(default_mode_governor) + default_mode = mode_definitions.get('default_mode') + if default_mode: + self._SetMaxFrequenciesFromMode(default_mode) + self._ForceAllCpusOnline(False) def SetPerfProfilingMode(self): """Enables all cores for reliable perf profiling.""" @@ -74,27 +193,6 @@ class PerfControl(object): raise RuntimeError('Need root to force CPUs online.') raise RuntimeError('Failed to force CPUs online.') - def SetDefaultPerfMode(self): - """Sets the performance mode for the device to its default mode.""" - if not self._device.HasRoot(): - return - product_model = self._device.product_model - if product_model == 'Nexus 5': - if self._AllCpusAreOnline(): - self._SetScalingMaxFreq(2265600) - self._SetMaxGpuClock(450000000) - - governor_mode = { - 'GT-I9300': 'pegasusq', - 'Galaxy Nexus': 'interactive', - 'Nexus 4': 'ondemand', - 'Nexus 5': 'ondemand', - 'Nexus 7': 'interactive', - 'Nexus 10': 'interactive' - }.get(product_model, 'ondemand') - self.SetScalingGovernor(governor_mode) - self._ForceAllCpusOnline(False) - def GetCpuInfo(self): online = (output.rstrip() == '1' and status == 0 for (_, output, status) in self._ForEachCpu('cat "$CPU/online"')) @@ -103,9 +201,24 @@ class PerfControl(object): in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"')) return zip(self._cpu_files, online, governor) - def _ForEachCpu(self, cmd): + def _ForEachCpu(self, cmd, cpu_list=None): + """Runs a command on the device for each of the CPUs. + + Args: + cmd: A string with a shell command, may may use shell expansion: "$CPU" to + refer to the current CPU in the string form (e.g. "cpu0", "cpu1", + and so on). + cpu_list: A space-separated string of CPU core names, like in the example + above + Returns: + A list of tuples in the form (cpu_string, command_output, exit_code), one + tuple per each command invocation. As usual, all lines of the output + command are joined into one line with spaces. + """ + if cpu_list is None: + cpu_list = self._cpu_file_list script = '; '.join([ - 'for CPU in %s' % self._cpu_file_list, + 'for CPU in %s' % cpu_list, 'do %s' % cmd, 'echo -n "%~%$?%~%"', 'done' @@ -115,20 +228,20 @@ class PerfControl(object): output = '\n'.join(output).split('%~%') return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2])) - def _WriteEachCpuFile(self, path, value): - self._ConditionallyWriteEachCpuFile(path, value, condition='true') - - def _ConditionallyWriteEachCpuFile(self, path, value, condition): + def _ConditionallyWriteCpuFiles(self, path, value, cpu_files, condition): template = ( '{condition} && test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"') results = self._ForEachCpu( - template.format(path=path, value=value, condition=condition)) + template.format(path=path, value=value, condition=condition), cpu_files) cpus = ' '.join(cpu for (cpu, _, status) in results if status == 0) if cpus: logger.info('Successfully set %s to %r on: %s', path, value, cpus) else: logger.warning('Failed to set %s to %r on any cpus', path, value) + def _WriteCpuFiles(self, path, value, cpu_files): + self._ConditionallyWriteCpuFiles(path, value, cpu_files, condition='true') + def _ReadEachCpuFile(self, path): return self._ForEachCpu( 'cat "$CPU/{path}"'.format(path=path)) @@ -145,8 +258,8 @@ class PerfControl(object): condition = 'test -e "{path}" && grep -q {value} {path}'.format( path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH), value=value) - self._ConditionallyWriteEachCpuFile( - 'cpufreq/scaling_governor', value, condition) + self._ConditionallyWriteCpuFiles( + 'cpufreq/scaling_governor', value, self._cpu_file_list, condition) def GetScalingGovernor(self): """Gets the currently set governor for each CPU. @@ -169,8 +282,8 @@ class PerfControl(object): """ return self._available_governors - def _SetScalingMaxFreq(self, value): - self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value) + def _SetScalingMaxFreqForCpus(self, value, cpu_files): + self._WriteCpuFiles('cpufreq/scaling_max_freq', '%d' % value, cpu_files) def _SetMaxGpuClock(self, value): self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk', @@ -179,8 +292,9 @@ class PerfControl(object): def _AllCpusAreOnline(self): results = self._ForEachCpu('cat "$CPU/online"') - # TODO(epenner): Investigate why file may be missing - # (http://crbug.com/397118) + # The file 'cpu0/online' is missing on some devices (example: Nexus 9). This + # is likely because on these devices it is impossible to bring the cpu0 + # offline. Assuming the same for all devices until proven otherwise. return all(output.rstrip() == '1' and status == 0 for (cpu, output, status) in results if cpu != 'cpu0') diff --git a/catapult/devil/devil/android/perf/perf_control_test.py b/catapult/devil/devil/android/perf/perf_control_test.py new file mode 100644 index 00000000..3832424c --- /dev/null +++ b/catapult/devil/devil/android/perf/perf_control_test.py @@ -0,0 +1,105 @@ +# Copyright 2018 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. + +import unittest + +from devil import devil_env +from devil.android import device_utils +from devil.android.perf import perf_control +from devil.android.sdk import adb_wrapper + +with devil_env.SysPath(devil_env.PYMOCK_PATH): + import mock + + +# pylint: disable=unused-argument +def _ShellCommandHandler(cmd, shell=False, check_return=False, + cwd=None, env=None, run_as=None, as_root=False, single_line=False, + large_output=False, raw_output=False, timeout=None, retries=None): + if cmd.startswith('for CPU in '): + if 'scaling_available_governors' in cmd: + contents = 'interactive ondemand userspace powersave performance' + return [contents + '\n%~%0%~%'] * 4 + if 'cat "$CPU/online"' in cmd: + return ['1\n%~%0%~%'] * 4 + assert False, 'Should not be called with cmd: {}'.format(cmd) + + +class PerfControlTest(unittest.TestCase): + @staticmethod + def _MockOutLowLevelPerfControlMethods(perf_control_object): + # pylint: disable=protected-access + perf_control_object.SetScalingGovernor = mock.Mock() + perf_control_object._ForceAllCpusOnline = mock.Mock() + perf_control_object._SetScalingMaxFreqForCpus = mock.Mock() + perf_control_object._SetMaxGpuClock = mock.Mock() + + # pylint: disable=no-self-use + def testNexus5HighPerfMode(self): + # Mock out the device state for PerfControl. + cpu_list = ['cpu%d' % cpu for cpu in xrange(4)] + mock_device = mock.Mock(spec=device_utils.DeviceUtils) + mock_device.product_model = 'Nexus 5' + mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper) + mock_device.ListDirectory.return_value = cpu_list + ['cpufreq'] + mock_device.FileExists.return_value = True + mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler) + pc = perf_control.PerfControl(mock_device) + self._MockOutLowLevelPerfControlMethods(pc) + + # Verify. + # pylint: disable=protected-access + # pylint: disable=no-member + pc.SetHighPerfMode() + mock_device.EnableRoot.assert_called_once_with() + pc._ForceAllCpusOnline.assert_called_once_with(True) + pc.SetScalingGovernor.assert_called_once_with('performance') + pc._SetScalingMaxFreqForCpus.assert_called_once_with( + 1190400, ' '.join(cpu_list)) + pc._SetMaxGpuClock.assert_called_once_with(200000000) + + def testNexus5XHighPerfMode(self): + # Mock out the device state for PerfControl. + cpu_list = ['cpu%d' % cpu for cpu in xrange(6)] + mock_device = mock.Mock(spec=device_utils.DeviceUtils) + mock_device.product_model = 'Nexus 5X' + mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper) + mock_device.ListDirectory.return_value = cpu_list + ['cpufreq'] + mock_device.FileExists.return_value = True + mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler) + pc = perf_control.PerfControl(mock_device) + self._MockOutLowLevelPerfControlMethods(pc) + + # Verify. + # pylint: disable=protected-access + # pylint: disable=no-member + pc.SetHighPerfMode() + mock_device.EnableRoot.assert_called_once_with() + pc._ForceAllCpusOnline.assert_called_once_with(True) + pc.SetScalingGovernor.assert_called_once_with('performance') + pc._SetScalingMaxFreqForCpus.assert_called_once_with( + 1248000, ' '.join(cpu_list)) + pc._SetMaxGpuClock.assert_called_once_with(300000000) + + def testNexus5XDefaultPerfMode(self): + # Mock out the device state for PerfControl. + cpu_list = ['cpu%d' % cpu for cpu in xrange(6)] + mock_device = mock.Mock(spec=device_utils.DeviceUtils) + mock_device.product_model = 'Nexus 5X' + mock_device.adb = mock.Mock(spec=adb_wrapper.AdbWrapper) + mock_device.ListDirectory.return_value = cpu_list + ['cpufreq'] + mock_device.FileExists.return_value = True + mock_device.RunShellCommand = mock.Mock(side_effect=_ShellCommandHandler) + pc = perf_control.PerfControl(mock_device) + self._MockOutLowLevelPerfControlMethods(pc) + + # Verify. + # pylint: disable=protected-access + # pylint: disable=no-member + pc.SetDefaultPerfMode() + pc.SetScalingGovernor.assert_called_once_with('ondemand') + pc._SetScalingMaxFreqForCpus.assert_any_call(1440000, 'cpu0 cpu1 cpu2 cpu3') + pc._SetScalingMaxFreqForCpus.assert_any_call(1824000, 'cpu4 cpu5') + pc._SetMaxGpuClock.assert_called_once_with(600000000) + pc._ForceAllCpusOnline.assert_called_once_with(False) diff --git a/catapult/devil/devil/android/perf/surface_stats_collector.py b/catapult/devil/devil/android/perf/surface_stats_collector.py index eab493df..ea46a398 100644 --- a/catapult/devil/devil/android/perf/surface_stats_collector.py +++ b/catapult/devil/devil/android/perf/surface_stats_collector.py @@ -116,6 +116,14 @@ class SurfaceStatsCollector(object): except StopIteration: raise Exception('Unable to get surface flinger process id') + def _GetSurfaceViewWindowName(self): + results = self._device.RunShellCommand( + ['dumpsys', 'SurfaceFlinger', '--list'], check_return=True) + for window_name in results: + if window_name.startswith('SurfaceView'): + return window_name + return None + def _GetSurfaceFlingerFrameData(self): """Returns collected SurfaceFlinger frame timing data. @@ -152,19 +160,21 @@ class SurfaceStatsCollector(object): # (each time the number above changes, we have a "jank"). # If this happens a lot during an animation, the animation appears # janky, even if it runs at 60 fps in average. - # - # We use the special "SurfaceView" window name because the statistics for - # the activity's main window are not updated when the main web content is - # composited into a SurfaceView. - results = self._device.RunShellCommand( - ['dumpsys', 'SurfaceFlinger', '--latency', 'SurfaceView'], - check_return=True) + window_name = self._GetSurfaceViewWindowName() + command = ['dumpsys', 'SurfaceFlinger', '--latency'] + # Even if we don't find the window name, run the command to get the refresh + # period. + if window_name: + command.append(window_name) + results = self._device.RunShellCommand(command, check_return=True) if not len(results): return (None, None) timestamps = [] nanoseconds_per_millisecond = 1e6 refresh_period = long(results[0]) / nanoseconds_per_millisecond + if not window_name: + return (refresh_period, timestamps) # If a fence associated with a frame is still pending when we query the # latency data, SurfaceFlinger gives the frame a timestamp of INT64_MAX. diff --git a/catapult/devil/devil/android/sdk/adb_wrapper.py b/catapult/devil/devil/android/sdk/adb_wrapper.py index 099a0f8a..2fbe9638 100644 --- a/catapult/devil/devil/android/sdk/adb_wrapper.py +++ b/catapult/devil/devil/android/sdk/adb_wrapper.py @@ -19,6 +19,7 @@ import posixpath import re import subprocess +from devil import base_error from devil import devil_env from devil.android import decorators from devil.android import device_errors @@ -96,7 +97,11 @@ def _GetVersion(): def _ShouldRetryAdbCmd(exc): - return not isinstance(exc, device_errors.NoAdbError) + # Errors are potentially transient and should be retried, with the exception + # of NoAdbError. Exceptions [e.g. generated from SIGTERM handler] should be + # raised. + return (isinstance(exc, base_error.BaseError) and + not isinstance(exc, device_errors.NoAdbError)) DeviceStat = collections.namedtuple('DeviceStat', @@ -257,13 +262,15 @@ class AdbWrapper(object): @classmethod @decorators.WithTimeoutAndConditionalRetries(_ShouldRetryAdbCmd) def _RunAdbCmd(cls, args, timeout=None, retries=None, device_serial=None, - check_error=True, cpu_affinity=None): - # pylint: disable=no-member + check_error=True, cpu_affinity=None, + ensure_logs_on_timeout=False): + timeout = timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime() + if ensure_logs_on_timeout: + timeout = 0.95 * timeout try: status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout( cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity), - timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime(), - env=cls._ADB_ENV) + timeout, env=cls._ADB_ENV) except OSError as e: if e.errno in (errno.ENOENT, errno.ENOEXEC): raise device_errors.NoAdbError(msg=str(e)) @@ -287,7 +294,9 @@ class AdbWrapper(object): return output # pylint: enable=unused-argument - def _RunDeviceAdbCmd(self, args, timeout, retries, check_error=True): + def _RunDeviceAdbCmd( + self, args, timeout, retries, check_error=True, + ensure_logs_on_timeout=False): """Runs an adb command on the device associated with this object. Args: @@ -302,7 +311,8 @@ class AdbWrapper(object): """ return self._RunAdbCmd(args, timeout=timeout, retries=retries, device_serial=self._device_serial, - check_error=check_error) + check_error=check_error, + ensure_logs_on_timeout=ensure_logs_on_timeout) def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout): """Runs an adb command and returns an iterator over its output lines. @@ -497,14 +507,17 @@ class AdbWrapper(object): return cmd_helper.StartCmd( self._BuildAdbCmd(['shell'] + cmd, self._device_serial)) - def Shell(self, command, expect_status=0, timeout=DEFAULT_TIMEOUT, - retries=DEFAULT_RETRIES): + def Shell(self, command, expect_status=0, ensure_logs_on_timeout=False, + timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES): """Runs a shell command on the device. Args: command: A string with the shell command to run. expect_status: (optional) Check that the command's exit status matches this value. Default is 0. If set to None the test is skipped. + ensure_logs_on_timeout: If True, will use a timeout that is 5% smaller + than the remaining time on the thread watchdog for the internal adb + command, which allows to retrive logs on timeout. timeout: (optional) Timeout per try in seconds. retries: (optional) Number of retries to attempt. @@ -519,7 +532,9 @@ class AdbWrapper(object): args = ['shell', command] else: args = ['shell', '( %s );echo %%$?' % command.rstrip()] - output = self._RunDeviceAdbCmd(args, timeout, retries, check_error=False) + output = self._RunDeviceAdbCmd( + args, timeout, retries, check_error=False, + ensure_logs_on_timeout=ensure_logs_on_timeout) if expect_status is not None: output_end = output.rfind('%') if output_end < 0: diff --git a/catapult/devil/devil/android/sdk/shared_prefs.py b/catapult/devil/devil/android/sdk/shared_prefs.py index c985cacc..c8c82b4e 100644 --- a/catapult/devil/devil/android/sdk/shared_prefs.py +++ b/catapult/devil/devil/android/sdk/shared_prefs.py @@ -278,12 +278,17 @@ class SharedPrefs(object): self._xml = None self._changed = True - def Commit(self): + def Commit(self, force_commit=False): """Save the current set of preferences to the device. - Only actually saves if some preferences have been modified. + Only actually saves if some preferences have been modified or force_commit + is set to True. + + Args: + force_commit: Commit even if no changes have been made to the SharedPrefs + instance. """ - if not self.changed: + if not (self.changed or force_commit): return self._device.RunShellCommand( ['mkdir', '-p', posixpath.dirname(self.path)], diff --git a/catapult/devil/devil/android/sdk/shared_prefs_test.py b/catapult/devil/devil/android/sdk/shared_prefs_test.py index 49587c89..08bbb467 100755 --- a/catapult/devil/devil/android/sdk/shared_prefs_test.py +++ b/catapult/devil/devil/android/sdk/shared_prefs_test.py @@ -19,6 +19,14 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH): import mock # pylint: disable=import-error +INITIAL_XML = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + '<map>\n' + ' <int name="databaseVersion" value="107" />\n' + ' <boolean name="featureEnabled" value="false" />\n' + ' <string name="someHashValue">249b3e5af13d4db2</string>\n' + '</map>') + + def MockDeviceWithFiles(files=None): if files is None: files = {} @@ -43,13 +51,7 @@ class SharedPrefsTest(unittest.TestCase): def setUp(self): self.device = MockDeviceWithFiles({ - '/data/data/com.some.package/shared_prefs/prefs.xml': - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - '<map>\n' - ' <int name="databaseVersion" value="107" />\n' - ' <boolean name="featureEnabled" value="false" />\n' - ' <string name="someHashValue">249b3e5af13d4db2</string>\n' - '</map>'}) + '/data/data/com.some.package/shared_prefs/prefs.xml': INITIAL_XML}) self.expected_data = {'databaseVersion': 107, 'featureEnabled': False, 'someHashValue': '249b3e5af13d4db2'} @@ -127,6 +129,22 @@ class SharedPrefsTest(unittest.TestCase): 'bigNumner': 6000000000, 'apps': ['gmail', 'chrome', 'music']}) # data survived roundtrip + def testForceCommit(self): + prefs = shared_prefs.SharedPrefs( + self.device, 'com.some.package', 'prefs.xml') + prefs.Load() + new_xml = 'Not valid XML' + self.device.WriteFile('/data/data/com.some.package/shared_prefs/prefs.xml', + new_xml) + prefs.Commit() + # Since we didn't change anything, Commit() should be a no-op. + self.assertEquals(self.device.ReadFile( + '/data/data/com.some.package/shared_prefs/prefs.xml'), new_xml) + prefs.Commit(force_commit=True) + # Forcing the commit should restore the originally read XML. + self.assertEquals(self.device.ReadFile( + '/data/data/com.some.package/shared_prefs/prefs.xml'), INITIAL_XML) + def testAsContextManager_onlyReads(self): with shared_prefs.SharedPrefs( self.device, 'com.some.package', 'prefs.xml') as prefs: diff --git a/catapult/devil/devil/android/sdk/version_codes.py b/catapult/devil/devil/android/sdk/version_codes.py index 1750f00d..29c7285e 100644 --- a/catapult/devil/devil/android/sdk/version_codes.py +++ b/catapult/devil/devil/android/sdk/version_codes.py @@ -19,3 +19,4 @@ NOUGAT = 24 NOUGAT_MR1 = 25 OREO = 26 OREO_MR1 = 27 +PIE = 28 diff --git a/catapult/devil/devil/android/tools/provision_devices.py b/catapult/devil/devil/android/tools/provision_devices.py index 3a726e59..47b1dc3f 100755 --- a/catapult/devil/devil/android/tools/provision_devices.py +++ b/catapult/devil/devil/android/tools/provision_devices.py @@ -39,6 +39,7 @@ from devil.android import settings from devil.android.sdk import adb_wrapper from devil.android.sdk import intent from devil.android.sdk import keyevent +from devil.android.sdk import shared_prefs from devil.android.sdk import version_codes from devil.android.tools import script_common from devil.constants import exit_codes @@ -51,6 +52,9 @@ _SYSTEM_APP_DIRECTORIES = ['/system/app/', '/system/priv-app/'] _SYSTEM_WEBVIEW_NAMES = ['webview', 'WebViewGoogle'] _CHROME_PACKAGE_REGEX = re.compile('.*chrom.*') _TOMBSTONE_REGEX = re.compile('tombstone.*') +_STANDALONE_VR_DEVICES = [ + 'vega', # Lenovo Mirage Solo +] class _DEFAULT_TIMEOUTS(object): @@ -134,6 +138,7 @@ def ProvisionDevices( steps.append(ProvisionStep(SetDate)) steps.append(ProvisionStep(CheckExternalStorage)) + steps.append(ProvisionStep(StandaloneVrDeviceSetup)) parallel_devices.pMap(ProvisionDevice, steps, blacklist, reboot_timeout) @@ -531,6 +536,30 @@ def CheckExternalStorage(device): device.WriteFile(f.name, 'test') +def StandaloneVrDeviceSetup(device): + """Performs any additional setup necessary for standalone Android VR devices. + + Arguments: + device: The device to check. + """ + if device.product_name not in _STANDALONE_VR_DEVICES: + return + + # Modify VrCore's settings so that any first time setup, etc. is skipped. + shared_pref = shared_prefs.SharedPrefs(device, 'com.google.vr.vrcore', + 'VrCoreSettings.xml', use_encrypted_path=True) + shared_pref.Load() + # Skip first time setup. + shared_pref.SetBoolean('DaydreamSetupComplete', True) + # Disable the automatic prompt that shows anytime the device detects that a + # controller isn't connected. + shared_pref.SetBoolean('gConfigFlags:controller_recovery_enabled', False) + # Use an automated controller instead of a real one so we get past the + # controller pairing screen that's shown on startup. + shared_pref.SetBoolean('UseAutomatedController', True) + shared_pref.Commit() + + def main(raw_args): # Recommended options on perf bots: # --disable-network diff --git a/catapult/devil/devil/android/tools/system_app.py b/catapult/devil/devil/android/tools/system_app.py index 00ea312a..4fe35e57 100755 --- a/catapult/devil/devil/android/tools/system_app.py +++ b/catapult/devil/devil/android/tools/system_app.py @@ -138,11 +138,11 @@ def _RelocateApp(device, package_name, relocate_to): @contextlib.contextmanager def _TemporarilyInstallApp(device, apk): """A context manager that installs an app while in scope.""" - device.adb.Install(apk, reinstall=True) + device.Install(apk, reinstall=True) try: yield finally: - device.adb.Uninstall(apk_helper.GetPackageName(apk)) + device.Uninstall(apk_helper.GetPackageName(apk)) def _MoveApp(device, relocation_map): diff --git a/catapult/devil/devil/android/tools/system_app_test.py b/catapult/devil/devil/android/tools/system_app_test.py index f72aa166..1400d7eb 100644 --- a/catapult/devil/devil/android/tools/system_app_test.py +++ b/catapult/devil/devil/android/tools/system_app_test.py @@ -47,7 +47,7 @@ class SystemAppTest(unittest.TestCase): mock_device.GetProp.side_effect = dict_getprop with system_app.EnableSystemAppModification(mock_device): - mock_device.EnableRoot.assert_called_once() + mock_device.EnableRoot.assert_called_once_with() mock_device.GetProp.assert_called_once_with( system_app._ENABLE_MODIFICATION_PROP) mock_device.SetProp.assert_called_once_with( @@ -55,10 +55,10 @@ class SystemAppTest(unittest.TestCase): mock_device.reset_mock() with system_app.EnableSystemAppModification(mock_device): - mock_device.EnableRoot.assert_not_called() + self.assertFalse(mock_device.EnableRoot.mock_calls) # assert not called mock_device.GetProp.assert_called_once_with( system_app._ENABLE_MODIFICATION_PROP) - mock_device.SetProp.assert_not_called() + self.assertFalse(mock_device.SetProp.mock_calls) # assert not called mock_device.reset_mock() mock_device.SetProp.assert_called_once_with( diff --git a/catapult/devil/devil/android/tools/unlock_bootloader.py b/catapult/devil/devil/android/tools/unlock_bootloader.py index 46fec9df..b38f6690 100644 --- a/catapult/devil/devil/android/tools/unlock_bootloader.py +++ b/catapult/devil/devil/android/tools/unlock_bootloader.py @@ -106,8 +106,10 @@ def unlock_bootloader(d): logging.info('Device %s already unlocked.', d) elif 'unlock is not allowed' in out: logging.error("Device %s is oem locked. Can't unlock bootloader.", d) + return 1 else: logging.error('Device %s in unknown state: "%s"', d, out) + return 1 break if leftover_pids: diff --git a/catapult/devil/devil/devil_dependencies.json b/catapult/devil/devil/devil_dependencies.json index 6884a36b..8a7943e2 100644 --- a/catapult/devil/devil/devil_dependencies.json +++ b/catapult/devil/devil/devil_dependencies.json @@ -58,6 +58,10 @@ "android_armeabi-v7a": { "cloud_storage_hash": "220ff3ba1a6c3c81877997e32784ffd008f293a5", "download_path": "../bin/deps/android/armeabi-v7a/apks/EmptySystemWebView.apk" + }, + "android_arm64-v8a": { + "cloud_storage_hash": "34e583c631a495afbba82ce8a1d4f9b5118a4411", + "download_path": "../bin/deps/android/arm64-v8a/apks/EmptySystemWebView.apk" } } }, @@ -134,4 +138,4 @@ } } } -}
\ No newline at end of file +} diff --git a/catapult/devil/devil/utils/cmd_helper.py b/catapult/devil/devil/utils/cmd_helper.py index 5ea85d85..b7b2f0dc 100644 --- a/catapult/devil/devil/utils/cmd_helper.py +++ b/catapult/devil/devil/utils/cmd_helper.py @@ -152,12 +152,12 @@ def _ValidateAndLogCommand(args, cwd, shell): else: if shell: raise Exception('array args must be run with shell=False') - args = ' '.join(SingleQuote(c) for c in args) + args = ' '.join(SingleQuote(str(c)) for c in args) if cwd is None: cwd = '' else: cwd = ':' + cwd - logger.info('[host]%s> %s', cwd, args) + logger.debug('[host]%s> %s', cwd, args) return args diff --git a/catapult/devil/devil/utils/lazy/weak_constant_test.py b/catapult/devil/devil/utils/lazy/weak_constant_test.py index 95191501..643351d8 100644 --- a/catapult/devil/devil/utils/lazy/weak_constant_test.py +++ b/catapult/devil/devil/utils/lazy/weak_constant_test.py @@ -38,7 +38,7 @@ class WeakConstantTest(unittest.TestCase): self.assertEquals( 'initializer called', test_constant.read()) - initializer.assert_called_once() + initializer.assert_called_once_with() def testInitialized(self): """Ensure that reading doesn't reinitialize the value.""" @@ -49,7 +49,7 @@ class WeakConstantTest(unittest.TestCase): self.assertEquals( 'initializer not called', test_constant.read()) - initializer.assert_not_called() + self.assertFalse(initializer.mock_calls) # assert not called def testFirstCallHangs(self): """Ensure that reading works even if the first initializer call hangs.""" diff --git a/catapult/systrace/systrace/systrace_trace_viewer.html b/catapult/systrace/systrace/systrace_trace_viewer.html index 29f8d59a..bdb0cd93 100644 --- a/catapult/systrace/systrace/systrace_trace_viewer.html +++ b/catapult/systrace/systrace/systrace_trace_viewer.html @@ -2297,7 +2297,7 @@ </style><style> .thread-track{flex-direction:column;display:flex;position:relative} </style><style> -.process-track-header{flex:0 0 auto;background-image:-webkit-gradient(linear,0 0,100% 0,from(#E5E5E5),to(#D1D1D1));border-bottom:1px solid #8e8e8e;border-top:1px solid white;font-size:75%}.process-track-name:before{content:'\25B8';padding:0 5px}.process-track-base.expanded .process-track-name:before{content:'\25BE'} +.process-track-header{display:flex;flex:0 0 auto;background-image:-webkit-gradient(linear,0 0,100% 0,from(#E5E5E5),to(#D1D1D1));border-bottom:1px solid #8e8e8e;border-top:1px solid white;font-size:75%}.process-track-name{flex-grow:1}.process-track-name:before{content:'\25B8';padding:0 5px}.process-track-base.expanded .process-track-name:before{content:'\25BE'}.process-track-close{color:black;border:1px solid transparent;padding:0px 2px}.process-track-close:hover{border:1px solid grey} </style><style> .model-track { flex-grow: 1; @@ -2841,7 +2841,7 @@ tr-ui-b-drag-handle { flex: 0 0 auto; } tr-ui-a-analysis-view { flex: 0 0 auto; } - #view_options_dropdown { + #view_options_dropdown, #process_filter_dropdown { --dropdown-button: { -webkit-appearance: none; align-items: normal; @@ -2862,6 +2862,7 @@ <div id="left_controls"></div> <div id="title">^_^</div> <div id="right_controls"> + <tr-ui-b-dropdown id="process_filter_dropdown" label="Processes"></tr-ui-b-dropdown> <tr-ui-b-toolbar-button id="view_metadata_button"> M </tr-ui-b-toolbar-button> @@ -3088,6 +3089,10 @@ margin-right: 20px; } + #show_visualization { + margin-right: 20px; + } + #export { margin-right: 20px; } @@ -3133,6 +3138,8 @@ <select id="statistic" value="{{displayStatisticName::change}}"> </select> + <button id="show_visualization" on-tap="loadVisualization_">Visualize</button> + <tr-ui-b-dropdown label="Export"> <tr-v-ui-histogram-set-controls-export> </tr-v-ui-histogram-set-controls-export> @@ -3358,6 +3365,90 @@ <tr-ui-b-table id="table"> </tr-ui-b-table></template> +</dom-module><dom-module id="tr-v-ui-metrics-visualization"> + <template> + <style> + button { + padding: 5px; + font-size: 14px; + } + + .text_input { + width: 50px; + padding: 4px; + font-size: 14px; + } + .error { + color: red; + display: none; + } + + .child_container { + position: relative; + display: inline-block; + } + + #title { + font-size: 20px; + font-weight: bold; + padding-bottom: 5px; + } + + #selectors { + display: none; + padding-bottom: 10px; + display: none; + } + + #search_page { + width: 200px; + margin-left: 30px; + } + + #close { + display: none; + vertical-align: top; + } + + #close svg{ + height: 1em; + } + + #close svg line { + stroke-width: 18; + stroke: black; + } + + #close:hover svg { + background: black; + } + + #close:hover svg line { + stroke: white; + } + </style> + <div id="title">Metrics Visualization</div> + <div class="error" id="data_error">Invalid data provided.</div> + <div id="selectors"> + <div id="percentile_label">Percentile Range:</div> + <input class="text_input" id="start" placeholder="0"/> + <input class="text_input" id="end" placeholder="100"/> + <button id="filter" on-tap="filterByPercentile_">Filter</button> + <input class="text_input" id="search_page" placeholder="Page Name"/> + <button id="search" on-tap="searchByPage_">Search</button> + <div class="error" id="search_error">Sorry, could not find that page!</div> + </div> + <div id="container"> + </div> + <div id="children"> + </div> + <span id="close"> + <svg viewBox="0 0 128 128"> + <line x1="28" x2="100" y1="28" y2="100"></line> + <line x1="28" x2="100" y1="100" y2="28"></line> + </svg> + </span> + </template> </dom-module><dom-module id="tr-v-ui-histogram-set-view"> <template> <style> @@ -3386,6 +3477,9 @@ <tr-v-ui-histogram-set-controls id="controls"> </tr-v-ui-histogram-set-controls> + <tr-v-ui-metrics-visualization id="metrics"> + </tr-v-ui-metrics-visualization> + <tr-v-ui-histogram-set-table id="table"></tr-v-ui-histogram-set-table> </div> </template> @@ -4229,7 +4323,7 @@ if(samples[-1]<1.0){locations.push(1.0);countLess.push(samples.length);countLess let maxDiff=0;let minDiff=0;for(let i=1;i<locations.length;i++){const length=locations[i]-locations[i-1];const countClosed=countLessEqual[i]-countLess[i-1];const countOpen=countLess[i]-countLessEqual[i-1];const countClosedIncrement=countLessEqual[i]-countLessEqual[i-1];const countOpenIncrement=countLess[i]-countLess[i-1];maxDiff=Math.max(countClosedIncrement*invSampleCount-length+maxDiff,countClosed*invSampleCount-length);minDiff=Math.min(countOpenIncrement*invSampleCount-length+minDiff,countOpen*invSampleCount-length);maxLocalDiscrepancy=Math.max(maxDiff,-minDiff,maxLocalDiscrepancy);} return maxLocalDiscrepancy;};Statistics.timestampsDiscrepancy=function(timestamps,opt_absolute,opt_locationCount){if(timestamps.length===0)return 0.0;if(opt_absolute===undefined)opt_absolute=true;if(Array.isArray(timestamps[0])){const rangeDiscrepancies=timestamps.map(function(r){return Statistics.timestampsDiscrepancy(r);});return Math.max.apply(null,rangeDiscrepancies);} const s=Statistics.normalizeSamples(timestamps);const samples=s.normalized_samples;const sampleScale=s.scale;let discrepancy=Statistics.discrepancy(samples,opt_locationCount);const invSampleCount=1.0/samples.length;if(opt_absolute===true){discrepancy/=sampleScale;}else{discrepancy=tr.b.math.clamp((discrepancy-invSampleCount)/(1.0-invSampleCount),0.0,1.0);} -return discrepancy;};Statistics.durationsDiscrepancy=function(durations,opt_absolute,opt_locationCount){if(durations.length===0)return 0.0;const timestamps=durations.reduce(function(prev,curr,index,array){prev.push(prev[prev.length-1]+curr);return prev;},[0]);return Statistics.timestampsDiscrepancy(timestamps,opt_absolute,opt_locationCount);};Statistics.uniformlySampleArray=function(samples,count){if(samples.length<=count){return samples;} +return discrepancy;};Statistics.uniformlySampleArray=function(samples,count){if(samples.length<=count){return samples;} while(samples.length>count){const i=parseInt(Math.random()*samples.length);samples.splice(i,1);} return samples;};Statistics.uniformlySampleStream=function(samples,streamLength,newElement,numSamples){if(streamLength<=numSamples){if(samples.length>=streamLength){samples[streamLength-1]=newElement;}else{samples.push(newElement);} return;} @@ -4250,19 +4344,24 @@ get significance(){return this.significance_;} compare(opt_alpha){const alpha=opt_alpha||Statistics.DEFAULT_ALPHA;if(this.p<alpha){this.significance_=Statistics.Significance.SIGNIFICANT;}else if(this.needMoreData_){this.significance_=Statistics.Significance.NEED_MORE_DATA;}else{this.significance_=Statistics.Significance.INSIGNIFICANT;} return this.significance_;} asDict(){return{p:this.p,U:this.U,significance:this.significance,};}} -Statistics.mwu=function(a,b,opt_alpha,opt_reqSampleSize){const result=mannwhitneyu.test(a,b);const needMoreData=opt_reqSampleSize&&Math.min(a.length,b.length)<opt_reqSampleSize;return new HypothesisTestResult(result.p,result.U,needMoreData,opt_alpha);};return{Statistics,};});'use strict';const GREEK_SMALL_LETTER_MU=String.fromCharCode(956);tr.exportTo('tr.b',function(){const SECONDS_IN_A_MINUTE=60;const SECONDS_IN_AN_HOUR=SECONDS_IN_A_MINUTE*60;const SECONDS_IN_A_DAY=SECONDS_IN_AN_HOUR*24;const SECONDS_IN_A_WEEK=SECONDS_IN_A_DAY*7;const SECONDS_IN_A_YEAR=SECONDS_IN_A_DAY*365.2422;const SECONDS_IN_A_MONTH=SECONDS_IN_A_YEAR/12;const UnitPrefixScale={};const UnitScale={};function defineUnitPrefixScale(name,prefixes){if(UnitPrefixScale[name]!==undefined){throw new Error('Unit prefix scale \''+name+'\' already exists');} +Statistics.mwu=function(a,b,opt_alpha,opt_reqSampleSize){const result=mannwhitneyu.test(a,b);const needMoreData=opt_reqSampleSize&&Math.min(a.length,b.length)<opt_reqSampleSize;return new HypothesisTestResult(result.p,result.U,needMoreData,opt_alpha);};return{Statistics,};});'use strict';tr.exportTo('tr.b',function(){const GREEK_SMALL_LETTER_MU=String.fromCharCode(956);const SECONDS_IN_A_MINUTE=60;const SECONDS_IN_AN_HOUR=SECONDS_IN_A_MINUTE*60;const SECONDS_IN_A_DAY=SECONDS_IN_AN_HOUR*24;const SECONDS_IN_A_WEEK=SECONDS_IN_A_DAY*7;const SECONDS_IN_A_YEAR=SECONDS_IN_A_DAY*365.2422;const SECONDS_IN_A_MONTH=SECONDS_IN_A_YEAR/12;const UnitPrefixScale={};const UnitScale={};function defineUnitPrefixScale(name,prefixes){if(UnitPrefixScale[name]!==undefined){throw new Error('Unit prefix scale \''+name+'\' already exists');} if(prefixes.AUTO!==undefined){throw new Error('The \'AUTO\' unit prefix is not supported for unit'+'prefix scales and cannot be added to scale \''+name+'\'');} UnitPrefixScale[name]=prefixes;} UnitScale.defineUnitScale=function(name,unitScale){if(UnitScale[name]!==undefined){throw new Error('Unit scale \''+name+'\' already exists');} if(unitScale.AUTO!==undefined){throw new Error('\'AUTO\' unit scale will be added automatically '+'for unit scale \''+name+'\'');} -unitScale.AUTO=Object.values(unitScale);unitScale.AUTO.sort((a,b)=>a.value-b.value);if(name)UnitScale[name]=unitScale;return unitScale;};UnitScale.defineUnitScaleFromPrefixScale=function(baseSymbol,baseName,prefixScale,opt_scaleName){if(baseSymbol===undefined){throw new Error('Cannot create UnitScale with undefined baseSymbol.');} +unitScale.AUTO=Object.values(unitScale);unitScale.AUTO.sort((a,b)=>a.value-b.value);if(name)UnitScale[name]=unitScale;return unitScale;};function definePrefixScaleFromUnitScale(prefixName,unitScale){if(!unitScale){throw new Error('Cannot create PrefixScale without a unit scale.');} +const prefixScale={};for(const[curPrefix,curScale]of Object.entries(unitScale)){if(curPrefix==='AUTO'){continue;} +if(curScale.symbol===undefined||!curScale.value){throw new Error(`Cannot create PrefixScale from malformed unit ${curScale}.`);} +prefixScale[curPrefix]={value:curScale.value,symbol:curScale.symbol};} +return defineUnitPrefixScale(prefixName,prefixScale);} +UnitScale.defineUnitScaleFromPrefixScale=function(baseSymbol,baseName,prefixScale,opt_scaleName){if(baseSymbol===undefined){throw new Error('Cannot create UnitScale with undefined baseSymbol.');} if(!baseName){throw new Error('Cannot create UnitScale without a baseName.');} if(!prefixScale){throw new Error('Cannot create UnitScale without a prefix scale.');} const unitScale={};for(const curPrefix of Object.keys(prefixScale)){const curScale=prefixScale[curPrefix];if(curScale.symbol===undefined||!curScale.value){throw new Error(`Cannot convert PrefixScale with malformed prefix ${curScale}.`);} const name=curPrefix==='NONE'?baseName:`${curPrefix}_${baseName}`;unitScale[name]={value:curScale.value,symbol:curScale.symbol+baseSymbol,baseSymbol};} return UnitScale.defineUnitScale(opt_scaleName,unitScale);};function convertUnit(value,fromScale,toScale){if(value===undefined)return undefined;const fromScaleBase=fromScale.baseSymbol;const toScaleBase=toScale.baseSymbol;if(fromScaleBase!==undefined&&toScaleBase!==undefined&&fromScaleBase!==toScaleBase){throw new Error('Cannot convert between units with different base symbols.');} return value*(fromScale.value/toScale.value);} -defineUnitPrefixScale('BINARY',{NONE:{value:Math.pow(1024,0),symbol:''},KIBI:{value:Math.pow(1024,1),symbol:'Ki'},MEBI:{value:Math.pow(1024,2),symbol:'Mi'},GIBI:{value:Math.pow(1024,3),symbol:'Gi'},TEBI:{value:Math.pow(1024,4),symbol:'Ti'}});defineUnitPrefixScale('METRIC',{NANO:{value:1e-9,symbol:'n'},MICRO:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU},MILLI:{value:1e-3,symbol:'m'},NONE:{value:1,symbol:''},KILO:{value:1e3,symbol:'k'},MEGA:{value:1e6,symbol:'M'},GIGA:{value:1e9,symbol:'G'}});UnitScale.defineUnitScale('TIME',{NANO_SEC:{value:1e-9,symbol:'ns',baseSymbol:'s'},MICRO_SEC:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU+'s',baseSymbol:'s'},MILLI_SEC:{value:1e-3,symbol:'ms',baseSymbol:'s'},SEC:{value:1,symbol:'s',baseSymbol:'s'},MINUTE:{value:SECONDS_IN_A_MINUTE,symbol:'min',baseSymbol:'s'},HOUR:{value:SECONDS_IN_AN_HOUR,symbol:'hr',baseSymbol:'s'},DAY:{value:SECONDS_IN_A_DAY,symbol:'days',baseSymbol:'s'},WEEK:{value:SECONDS_IN_A_WEEK,symbol:'weeks',baseSymbol:'s'},MONTH:{value:SECONDS_IN_A_MONTH,symbol:'months',baseSymbol:'s'},YEAR:{value:SECONDS_IN_A_YEAR,symbol:'years',baseSymbol:'s'}});UnitScale.defineUnitScaleFromPrefixScale('B','BYTE',UnitPrefixScale.BINARY,'MEMORY');return{UnitPrefixScale,UnitScale,convertUnit,};});'use strict';tr.exportTo('tr.b',function(){const msDisplayMode={scale:1e-3,suffix:'ms',roundedLess(a,b){return Math.round(a*1000)<Math.round(b*1000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.MILLI_SEC],minimumFractionDigits:3,}};const nsDisplayMode={scale:1e-9,suffix:'ns',roundedLess(a,b){return Math.round(a*1000000)<Math.round(b*1000000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.NANO_SEC],maximumFractionDigits:0}};const TimeDisplayModes={ns:nsDisplayMode,ms:msDisplayMode};return{TimeDisplayModes,};});'use strict';tr.exportTo('tr.ui.b',function(){function iterateElementDeeplyImpl(element,cb,thisArg,includeElement){if(includeElement&&cb.call(thisArg,element))return true;if(element.root&&element.root!==element&&iterateElementDeeplyImpl(element.root,cb,thisArg,false)){return true;} +defineUnitPrefixScale('BINARY',{NONE:{value:Math.pow(1024,0),symbol:''},KIBI:{value:Math.pow(1024,1),symbol:'Ki'},MEBI:{value:Math.pow(1024,2),symbol:'Mi'},GIBI:{value:Math.pow(1024,3),symbol:'Gi'},TEBI:{value:Math.pow(1024,4),symbol:'Ti'}});defineUnitPrefixScale('METRIC',{NANO:{value:1e-9,symbol:'n'},MICRO:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU},MILLI:{value:1e-3,symbol:'m'},NONE:{value:1,symbol:''},KILO:{value:1e3,symbol:'k'},MEGA:{value:1e6,symbol:'M'},GIGA:{value:1e9,symbol:'G'}});UnitScale.defineUnitScale('TIME',{NANO_SEC:{value:1e-9,symbol:'ns',baseSymbol:'s'},MICRO_SEC:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU+'s',baseSymbol:'s'},MILLI_SEC:{value:1e-3,symbol:'ms',baseSymbol:'s'},SEC:{value:1,symbol:'s',baseSymbol:'s'},MINUTE:{value:SECONDS_IN_A_MINUTE,symbol:'min',baseSymbol:'s'},HOUR:{value:SECONDS_IN_AN_HOUR,symbol:'hr',baseSymbol:'s'},DAY:{value:SECONDS_IN_A_DAY,symbol:'days',baseSymbol:'s'},WEEK:{value:SECONDS_IN_A_WEEK,symbol:'weeks',baseSymbol:'s'},MONTH:{value:SECONDS_IN_A_MONTH,symbol:'months',baseSymbol:'s'},YEAR:{value:SECONDS_IN_A_YEAR,symbol:'years',baseSymbol:'s'}});UnitScale.defineUnitScaleFromPrefixScale('B','BYTE',UnitPrefixScale.BINARY,'MEMORY');definePrefixScaleFromUnitScale('DATA_SIZE',UnitScale.MEMORY);UnitScale.defineUnitScaleFromPrefixScale('/s','SECONDS',UnitPrefixScale.DATA_SIZE,'BANDWIDTH_BYTES');return{UnitPrefixScale,UnitScale,convertUnit,GREEK_SMALL_LETTER_MU,};});'use strict';tr.exportTo('tr.b',function(){const msDisplayMode={scale:1e-3,suffix:'ms',roundedLess(a,b){return Math.round(a*1000)<Math.round(b*1000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.MILLI_SEC],minimumFractionDigits:3,}};const nsDisplayMode={scale:1e-9,suffix:'ns',roundedLess(a,b){return Math.round(a*1000000)<Math.round(b*1000000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.NANO_SEC],maximumFractionDigits:0}};const TimeDisplayModes={ns:nsDisplayMode,ms:msDisplayMode};return{TimeDisplayModes,};});'use strict';tr.exportTo('tr.ui.b',function(){function iterateElementDeeplyImpl(element,cb,thisArg,includeElement){if(includeElement&&cb.call(thisArg,element))return true;if(element.root&&element.root!==element&&iterateElementDeeplyImpl(element.root,cb,thisArg,false)){return true;} const children=Polymer.dom(element).children;for(let i=0;i<children.length;i++){if(iterateElementDeeplyImpl(children[i],cb,thisArg,true)){return true;}} return false;} function iterateElementDeeply(element,cb,thisArg){iterateElementDeeplyImpl(element,cb,thisArg,false);} @@ -4285,14 +4384,15 @@ const context=opt_context||{};let scale=undefined;if(context.unitScale){scale=co if(!(scale instanceof Array)){throw new Error('Unit has a malformed unit scale.');} return scale;},get unitString(){const scale=this.getUnitScale_();if(!scale){throw new Error('A UnitScale could not be found for Unit '+this.unitName);} return scale[0].symbol;},format(value,opt_context){let signString='';if(value<0){signString='-';value=-value;}else if(this.isDelta){signString=value===0?PLUS_MINUS_SIGN:'+';} -const context=opt_context||{};const scale=this.getUnitScale_(context);let deltaValue=context.deltaValue===undefined?value:context.deltaValue;deltaValue=Math.abs(deltaValue)*this.scaleBaseUnit.value;let i=0;while(i<scale.length-1&&deltaValue/scale[i+1].value>=1){i++;} +const context=opt_context||{};const scale=this.getUnitScale_(context);let deltaValue=context.deltaValue===undefined?value:context.deltaValue;deltaValue=Math.abs(deltaValue)*this.scaleBaseUnit.value;if(deltaValue===0){deltaValue=1;} +let i=0;while(i<scale.length-1&&deltaValue/scale[i+1].value>=1){i++;} const selectedSubUnit=scale[i];let formatSpec=this.formatSpec_;if(typeof formatSpec==='function')formatSpec=formatSpec();let unitString='';if(selectedSubUnit.symbol){if(!formatSpec.avoidSpacePrecedingUnit)unitString=' ';unitString+=selectedSubUnit.symbol;} value=tr.b.convertUnit(value,this.scaleBaseUnit,selectedSubUnit);const numberString=getNumberFormatter(formatSpec.minimumFractionDigits,formatSpec.maximumFractionDigits,context.minimumFractionDigits,context.maximumFractionDigits).format(value);return signString+numberString+unitString;}};Unit.reset=function(){Unit.currentTimeDisplayMode=TimeDisplayModes.ms;};Unit.timestampFromUs=function(us){return tr.b.convertUnit(us,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);};Object.defineProperty(Unit,'currentTimeDisplayMode',{get(){return Unit.currentTimeDisplayMode_;},set(value){if(Unit.currentTimeDisplayMode_===value)return;Unit.currentTimeDisplayMode_=value;Unit.dispatchEvent(new tr.b.Event('display-mode-changed'));}});Unit.didPreferredTimeDisplayUnitChange=function(){let largest=undefined;const els=tr.ui.b.findDeepElementsMatching(document.body,'tr-v-ui-preferred-display-unit');els.forEach(function(el){largest=max(largest,el.preferredTimeDisplayMode);});Unit.currentTimeDisplayMode=largest===undefined?TimeDisplayModes.ms:largest;};Unit.byName={};Unit.byJSONName={};Unit.fromJSON=function(object){const u=Unit.byJSONName[object];if(u){return u;} throw new Error(`Unrecognized unit "${object}"`);};Unit.define=function(params){const definedUnits=[];for(const improvementDirection of Object.values(ImprovementDirection)){const regularUnit=Unit.defineUnitVariant_(params,false,improvementDirection);const deltaUnit=Unit.defineUnitVariant_(params,true,improvementDirection);regularUnit.correspondingDeltaUnit=deltaUnit;deltaUnit.correspondingDeltaUnit=deltaUnit;definedUnits.push(regularUnit,deltaUnit);} const baseUnit=Unit.byName[params.baseUnitName];definedUnits.forEach(u=>u.baseUnit=baseUnit);};Unit.nameSuffixForImprovementDirection=function(improvementDirection){switch(improvementDirection){case ImprovementDirection.DONT_CARE:return'';case ImprovementDirection.BIGGER_IS_BETTER:return'_biggerIsBetter';case ImprovementDirection.SMALLER_IS_BETTER:return'_smallerIsBetter';default:throw new Error('Unknown improvement direction: '+improvementDirection);}};Unit.defineUnitVariant_=function(params,isDelta,improvementDirection){let nameSuffix=isDelta?'Delta':'';nameSuffix+=Unit.nameSuffixForImprovementDirection(improvementDirection);const unitName=params.baseUnitName+nameSuffix;const jsonName=params.baseJsonName+nameSuffix;if(Unit.byName[unitName]!==undefined){throw new Error('Unit \''+unitName+'\' already exists');} if(Unit.byJSONName[jsonName]!==undefined){throw new Error('JSON unit \''+jsonName+'\' alread exists');} let scaleBaseUnit=params.scaleBaseUnit;if(!scaleBaseUnit){let formatSpec=params.formatSpec;if(typeof formatSpec==='function')formatSpec=formatSpec();const baseSymbol=formatSpec.unitScale?formatSpec.unitScale[0].baseSymbol:(formatSpec.baseSymbol||'');scaleBaseUnit={value:1,symbol:baseSymbol,baseSymbol};} -const unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{baseSymbol:'J',minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{baseSymbol:'W',minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');} +const unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'bandwidthInBytesPerSecond',baseJsonName:'bytesPerSecond',formatSpec:{unitScale:tr.b.UnitScale.BANDWIDTH_BYTES.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('J','JOULE',tr.b.UnitPrefixScale.METRIC,'JOULE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('W','WATT',tr.b.UnitPrefixScale.METRIC,'WATT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricCurrentInAmperes',baseJsonName:'A',formatSpec:{baseSymbol:'A',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('A','AMPERE',tr.b.UnitPrefixScale.METRIC,'AMPERE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricPotentialInVolts',baseJsonName:'V',formatSpec:{baseSymbol:'V',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('V','VOLT',tr.b.UnitPrefixScale.METRIC,'VOLT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'frequencyInHertz',baseJsonName:'Hz',formatSpec:{baseSymbol:'Hz',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('Hz','HERTZ',tr.b.UnitPrefixScale.METRIC,'HERTZ').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');} if(!(typeof(value)==='number')){throw new Error('Expected value to be number');} this.unit=unit;this.value=value;} asDict(){return{unit:this.unit.asJSON(),value:tr.b.numberToJson(this.value),};} @@ -4528,17 +4628,30 @@ return process.findAllThreadsNamed('CrGpuMain').length>0;};ChromeGpuHelper.proto getNetworkEvents(){const networkEvents=[];for(const slice of this.thread.asyncSliceGroup.slices){const categories=tr.b.getCategoryParts(slice.category);const isNetEvent=category=>NET_CATEGORIES.has(category);if(categories.filter(isNetEvent).length===0)continue;networkEvents.push(slice);} return networkEvents;}} return{ChromeThreadHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){const ChromeThreadHelper=tr.model.helpers.ChromeThreadHelper;function ChromeRendererHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrRendererMain')||process.findAtMostOneThreadNamed('Chrome_InProcRendererThread');this.compositorThread_=process.findAtMostOneThreadNamed('Compositor');this.rasterWorkerThreads_=process.findAllThreadsMatching(function(t){if(t.name===undefined)return false;if(t.name.indexOf('CompositorTileWorker')===0)return true;if(t.name.indexOf('CompositorRasterWorker')===0)return true;return false;});if(!process.name){process.name=ChromeRendererHelper.PROCESS_NAME;}} -ChromeRendererHelper.PROCESS_NAME='Renderer';ChromeRendererHelper.isRenderProcess=function(process){if(process.findAtMostOneThreadNamed('CrRendererMain'))return true;if(process.findAtMostOneThreadNamed('Compositor'))return true;return false;};ChromeRendererHelper.isTracingProcess=function(process){return process.labels!==undefined&&process.labels.length===1&&process.labels[0]==='chrome://tracing';};ChromeRendererHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;},get compositorThread(){return this.compositorThread_;},get rasterWorkerThreads(){return this.rasterWorkerThreads_;},get isChromeTracingUI(){return ChromeRendererHelper.isTracingProcess(this.process);},};return{ChromeRendererHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function findChromeBrowserProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeBrowserHelper.isBrowserProcess);} +ChromeRendererHelper.PROCESS_NAME='Renderer';ChromeRendererHelper.isRenderProcess=function(process){if(process.findAtMostOneThreadNamed('CrRendererMain'))return true;if(process.findAtMostOneThreadNamed('Compositor'))return true;return false;};ChromeRendererHelper.isTracingProcess=function(process){return process.labels!==undefined&&process.labels.length===1&&process.labels[0]==='chrome://tracing';};ChromeRendererHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;},get compositorThread(){return this.compositorThread_;},get rasterWorkerThreads(){return this.rasterWorkerThreads_;},get isChromeTracingUI(){return ChromeRendererHelper.isTracingProcess(this.process);},};return{ChromeRendererHelper,};});'use strict';tr.exportTo('tr.model.um',function(){class Segment extends tr.model.TimedEvent{constructor(start,duration){super(start);this.duration=duration;this.expectations_=[];} +get expectations(){return this.expectations_;} +clone(){const clone=new Segment(this.start,this.duration);clone.expectations.push(...this.expectations);return clone;} +addSegment(other){this.duration+=other.duration;this.expectations.push(...other.expectations);}} +return{Segment,};});'use strict';tr.exportTo('tr.model.helpers',function(){const GESTURE_EVENT='SyntheticGestureController::running';const IR_REG_EXP=/Interaction\.([^/]+)(\/[^/]*)?$/;const ChromeRendererHelper=tr.model.helpers.ChromeRendererHelper;class TelemetryHelper{constructor(modelHelper){this.modelHelper=modelHelper;this.renderersWithIR_=undefined;this.segments_=undefined;this.uiSegments_=undefined;} +get renderersWithIR(){this.findIRs_();return this.renderersWithIR_;} +get segments(){this.findIRs_();return this.segments_;} +get uiSegments(){this.findIRs_();return this.uiSegments_;} +findIRs_(){if(this.segments_!==undefined)return;this.renderersWithIR_=[];const gestureEvents=[];const interactionRecords=[];const processes=Object.values(this.modelHelper.rendererHelpers).concat(this.modelHelper.browserHelpers).map(processHelper=>processHelper.process);for(const process of processes){let foundIR=false;for(const thread of Object.values(process.threads)){for(const slice of thread.asyncSliceGroup.slices){if(slice.title===GESTURE_EVENT){gestureEvents.push(slice);}else if(IR_REG_EXP.test(slice.title)){interactionRecords.push(slice);foundIR=true;}}} +if(foundIR&&ChromeRendererHelper.isRenderProcess(process)&&!ChromeRendererHelper.isTracingProcess(process)){this.renderersWithIR_.push(new ChromeRendererHelper(this.modelHelper,process));}} +this.segments_=[];this.uiSegments_=[];for(const ir of interactionRecords){const parts=IR_REG_EXP.exec(ir.title);let gestureEventFound=false;if(parts[1].startsWith('Gesture_')){for(const gestureEvent of gestureEvents){if(ir.boundsRange.intersectsRangeInclusive(gestureEvent.boundsRange)){this.segments_.push(new tr.model.um.Segment(gestureEvent.start,gestureEvent.duration));gestureEventFound=true;break;}}}else if(parts[1].startsWith('ui_')){this.uiSegments_.push(new tr.model.um.Segment(ir.start,ir.duration));} +if(!gestureEventFound){this.segments_.push(new tr.model.um.Segment(ir.start,ir.duration));}} +this.segments_.sort((x,y)=>x.start-y.start);this.uiSegments_.sort((x,y)=>x.start-y.start);}} +return{TelemetryHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function findChromeBrowserProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeBrowserHelper.isBrowserProcess);} function findChromeRenderProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeRendererHelper.isRenderProcess);} function findChromeGpuProcess(model){const gpuProcesses=model.getAllProcesses(tr.model.helpers.ChromeGpuHelper.isGpuProcess);if(gpuProcesses.length!==1)return undefined;return gpuProcesses[0];} +function findTelemetrySurfaceFlingerProcess(model){const surfaceFlingerProcesses=model.getAllProcesses(process=>(process.name==='SurfaceFlinger'));if(surfaceFlingerProcesses.length!==1)return undefined;return surfaceFlingerProcesses[0];} function ChromeModelHelper(model){this.model_=model;const browserProcesses=findChromeBrowserProcesses(model);this.browserHelpers_=browserProcesses.map(p=>new tr.model.helpers.ChromeBrowserHelper(this,p));const gpuProcess=findChromeGpuProcess(model);if(gpuProcess){this.gpuHelper_=new tr.model.helpers.ChromeGpuHelper(this,gpuProcess);}else{this.gpuHelper_=undefined;} -const rendererProcesses_=findChromeRenderProcesses(model);this.rendererHelpers_={};rendererProcesses_.forEach(function(renderProcess){const rendererHelper=new tr.model.helpers.ChromeRendererHelper(this,renderProcess);this.rendererHelpers_[rendererHelper.pid]=rendererHelper;},this);this.chromeBounds_=undefined;} -ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsModel=function(model){if(findChromeBrowserProcesses(model).length)return true;if(findChromeRenderProcesses(model).length)return true;return false;};ChromeModelHelper.prototype={get pid(){throw new Error('woah');},get process(){throw new Error('woah');},get model(){return this.model_;},get browserProcess(){if(this.browserHelper===undefined)return undefined;return this.browserHelper.process;},get browserHelper(){return this.browserHelpers_[0];},get browserHelpers(){return this.browserHelpers_;},get gpuHelper(){return this.gpuHelper_;},get rendererHelpers(){return this.rendererHelpers_;},get rendererWithLargestPid(){let largestPid=-1;for(const pid in this.rendererHelpers){const rendererHelper=this.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;if(pid>largestPid)largestPid=pid;} -if(largestPid===-1)return undefined;return this.rendererHelpers[largestPid];},get chromeBounds(){if(!this.chromeBounds_){this.chromeBounds_=new tr.b.math.Range();for(const browserHelper of Object.values(this.browserHelpers)){this.chromeBounds_.addRange(browserHelper.process.bounds);} +const rendererProcesses_=findChromeRenderProcesses(model);this.rendererHelpers_={};rendererProcesses_.forEach(function(renderProcess){const rendererHelper=new tr.model.helpers.ChromeRendererHelper(this,renderProcess);this.rendererHelpers_[rendererHelper.pid]=rendererHelper;},this);this.surfaceFlingerProcess_=findTelemetrySurfaceFlingerProcess(model);this.chromeBounds_=undefined;this.telemetryHelper_=new tr.model.helpers.TelemetryHelper(this);} +ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsModel=function(model){if(findChromeBrowserProcesses(model).length)return true;if(findChromeRenderProcesses(model).length)return true;return false;};ChromeModelHelper.prototype={get pid(){throw new Error('woah');},get process(){throw new Error('woah');},get model(){return this.model_;},get browserProcess(){if(this.browserHelper===undefined)return undefined;return this.browserHelper.process;},get browserHelper(){return this.browserHelpers_[0];},get browserHelpers(){return this.browserHelpers_;},get gpuHelper(){return this.gpuHelper_;},get rendererHelpers(){return this.rendererHelpers_;},get surfaceFlingerProcess(){return this.surfaceFlingerProcess_;},get chromeBounds(){if(!this.chromeBounds_){this.chromeBounds_=new tr.b.math.Range();for(const browserHelper of Object.values(this.browserHelpers)){this.chromeBounds_.addRange(browserHelper.process.bounds);} for(const rendererHelper of Object.values(this.rendererHelpers)){this.chromeBounds_.addRange(rendererHelper.process.bounds);} if(this.gpuHelper){this.chromeBounds_.addRange(this.gpuHelper.process.bounds);}} if(this.chromeBounds_.isEmpty){return undefined;} -return this.chromeBounds_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){const AsyncSlice=tr.model.AsyncSlice;const EventSet=tr.model.EventSet;const UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';const ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';const BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';const END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';const MAIN_RENDERER_THREAD_NAME='CrRendererMain';const COMPOSITOR_THREAD_NAME='Compositor';const POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';const IPC_FLOW_EVENT='disabled-by-default-ipc.flow';const INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}} +return this.chromeBounds_;},get telemetryHelper(){return this.telemetryHelper_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){const AsyncSlice=tr.model.AsyncSlice;const EventSet=tr.model.EventSet;const UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';const ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';const BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';const LEGACY_END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';const MAIN_RENDERER_THREAD_NAME='CrRendererMain';const COMPOSITOR_THREAD_NAME='Compositor';const POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';const IPC_FLOW_EVENT='disabled-by-default-ipc.flow';const INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}} InputLatencyAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get isLegacyEvent(){return this.title==='InputLatency';},get typeName(){if(!this.typeName_){this.determineLegacyTypeName_();} return this.typeName_;},checkTypeName_(){if(!this.typeName_){throw new Error('Unable to determine typeName');} let found=false;for(const typeName in INPUT_EVENT_TYPE_NAMES){if(this.typeName===INPUT_EVENT_TYPE_NAMES[typeName]){found=true;break;}} @@ -4567,8 +4680,8 @@ while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.add const COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame){this.backtraceFromDraw(beginImplFrame,visitedEvents);}} const INPUT_GSU='InputLatency::GestureScrollUpdate';if(this.title===INPUT_GSU){this.addScrollUpdateEvents(rendererHelper);}},get associatedEvents(){if(this.associatedEvents_.length!==0){return this.associatedEvents_;} const modelIndices=this.startThread.parent.model.modelIndices;const flowEvents=modelIndices.getFlowEventsWithId(this.id);if(flowEvents.length===0){return this.associatedEvents_;} -const sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);const rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))return undefined;const data=this.args.data;if(!(END_COMP_NAME in data))return undefined;let latency=0;const endTime=data[END_COMP_NAME].time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');} -return latency;}};const eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];const allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES,};});'use strict';tr.exportTo('tr.e.chrome',function(){const SAME_AS_PARENT='same-as-parent';const TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::UpdateLayers::CalcDrawProps','UpdateLayerTree',],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers',],iframe_creation:['WebLocalFrameImpl::createChildframe',],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale','ImageResourceContent::updateImage',],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent',],layout:['DisplayItemList::Finalize','IntersectionObserverController::computeTrackedIntersectionObservations','LocalFrameView::invalidateTree','LocalFrameView::layout','LocalFrameView::performLayout','LocalFrameView::performPostLayoutTasks','LocalFrameView::performPreLayoutTasks','FrameView::invalidateTree','FrameView::layout','FrameView::performLayout','FrameView::performPostLayoutTasks','FrameView::performPreLayoutTasks','Layer::updateLayerPositionsAfterLayout','LayerTreeHostInProcess::UpdateLayers::BuildPropertyTrees','Layout','LayoutView::hitTest','PaintLayer::updateLayerPositionsAfterLayout','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::layout',],parseHTML:['BackgroundHTMLParser::pumpTokenizer','BackgroundHTMLParser::sendTokensToMainThread','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::documentElementAvailable','HTMLDocumentParser::notifyPendingTokenizedChunks','HTMLDocumentParser::processParsedChunkFromBackgroundParser','HTMLDocumentParser::processTokenizedChunkFromBackgroundParser','ParseHTML',],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory',],record:['Canvas2DLayerBridge::flushRecordingOnly','CompositingRequirementsUpdater::updateRecursive','ContentLayerDelegate::paintContents','DeprecatedPaintLayerCompositor::updateIfNeededRecursive','DeprecatedPaintLayerCompositor::updateLayerPositionsAfterLayout','LocalFrameView::paintTree','LocalFrameView::prePaint','Paint','PaintController::commitNewDisplayItems','PaintLayerCompositor::updateIfNeededRecursive','Picture::Record','PictureLayer::Update','RenderLayer::updateLayerPositionsAfterLayout',],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::rebuildLayoutTree','Document::recalcStyle','Document::updateActiveStyle','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','LocalFrameView::updateStyleAndLayoutIfNeededRecursive','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleEngine::updateActiveStyleSheets','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree',],script_parse_and_compile:['V8.CompileFullCode','V8.NewContext','V8.Parse','V8.ParseLazy','V8.RecompileSynchronous','V8.ScriptCompiler','v8.compile','v8.parseOnBackground',],script_execute:['EvaluateScript','FunctionCall','HTMLParserScriptRunner ExecuteScript','V8.Execute','V8.RunMicrotasks','V8.Task','WindowProxy::initialize','v8.callFunction','v8.run',],resource_loading:['RenderFrameImpl::didFinishDocumentLoad','RenderFrameImpl::didFinishLoad','Resource::appendData','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnReceivedResponse','ResourceDispatcher::OnRequestComplete','ResourceFetcher::requestResource','WebURLLoaderImpl::Context::Cancel','WebURLLoaderImpl::Context::OnCompletedRequest','WebURLLoaderImpl::Context::OnReceivedData','WebURLLoaderImpl::Context::OnReceivedRedirect','WebURLLoaderImpl::Context::OnReceivedResponse','WebURLLoaderImpl::Context::Start','WebURLLoaderImpl::loadAsynchronously','WebURLLoaderImpl::loadSynchronously','content::mojom::URLLoaderClient',],renderer_misc:['DecodeFont','ThreadState::completeSweep',],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send',]};const COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();const USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(const category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});} +const sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);const rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))return undefined;const data=this.args.data;const endTimeComp=data[END_COMP_NAME]||data[LEGACY_END_COMP_NAME];if(endTimeComp===undefined)return undefined;let latency=0;const endTime=endTimeComp.time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');} +return latency;}};const eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];const allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES,};});'use strict';tr.exportTo('tr.e.chrome',function(){const SAME_AS_PARENT='same-as-parent';const TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::DoUpdateLayers','LayerTreeHost::UpdateLayers::BuildPropertyTrees','LocalFrameView::pushPaintArtifactToCompositor','LocalFrameView::updateCompositedSelectionIfNeeded','LocalFrameView::RunCompositingLifecyclePhase','UpdateLayerTree',],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers',],iframe_creation:['WebLocalFrameImpl::createChildframe',],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale','ImageResourceContent::updateImage',],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent',],layout:['IntersectionObserverController::computeTrackedIntersectionObservations','LocalFrameView::invalidateTree','LocalFrameView::layout','LocalFrameView::performLayout','LocalFrameView::performPostLayoutTasks','LocalFrameView::performPreLayoutTasks','LocalFrameView::RunStyleAndLayoutCompositingPhases','Layout','PaintLayer::updateLayerPositionsAfterLayout','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::updateAllLifecyclePhases','WebViewImpl::beginFrame',],parseHTML:['BackgroundHTMLParser::pumpTokenizer','BackgroundHTMLParser::sendTokensToMainThread','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::documentElementAvailable','HTMLDocumentParser::notifyPendingTokenizedChunks','HTMLDocumentParser::processParsedChunkFromBackgroundParser','HTMLDocumentParser::processTokenizedChunkFromBackgroundParser','ParseHTML',],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory',],record:['Canvas2DLayerBridge::flushRecordingOnly','CompositingInputsUpdater::update','CompositingRequirementsUpdater::updateRecursive','ContentLayerDelegate::paintContents','DisplayItemList::Finalize','LocalFrameView::RunPaintLifecyclePhase','LocalFrameView::RunPrePaintLifecyclePhase','Paint','PaintController::commitNewDisplayItems','PaintLayerCompositor::updateIfNeededRecursive','Picture::Record','PictureLayer::Update',],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::rebuildLayoutTree','Document::recalcStyle','Document::updateActiveStyle','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','LocalFrameView::updateStyleAndLayoutIfNeededRecursive','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleEngine::updateActiveStyleSheets','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree',],script_parse_and_compile:['V8.CompileFullCode','V8.NewContext','V8.Parse','V8.ParseLazy','V8.RecompileSynchronous','V8.ScriptCompiler','v8.compile','v8.parseOnBackground',],script_execute:['EvaluateScript','FunctionCall','HTMLParserScriptRunner ExecuteScript','V8.Execute','V8.RunMicrotasks','V8.Task','WindowProxy::initialize','v8.callFunction','v8.run',],resource_loading:['RenderFrameImpl::didFinishDocumentLoad','RenderFrameImpl::didFinishLoad','Resource::appendData','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnReceivedResponse','ResourceDispatcher::OnRequestComplete','ResourceFetcher::requestResource','WebURLLoaderImpl::Context::Cancel','WebURLLoaderImpl::Context::OnCompletedRequest','WebURLLoaderImpl::Context::OnReceivedData','WebURLLoaderImpl::Context::OnReceivedRedirect','WebURLLoaderImpl::Context::OnReceivedResponse','WebURLLoaderImpl::Context::Start','WebURLLoaderImpl::loadAsynchronously','WebURLLoaderImpl::loadSynchronously','content::mojom::URLLoaderClient',],renderer_misc:['DecodeFont','ThreadState::completeSweep',],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send',]};const COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();const USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(const category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});} const USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY={netlog:'net',overhead:'overhead',startup:'startup',gpu:'gpu',};function ChromeUserFriendlyCategoryDriver(){} ChromeUserFriendlyCategoryDriver.fromEvent=function(event){let userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_TITLE.get(event.title);if(userFriendlyCategory){if(userFriendlyCategory===SAME_AS_PARENT){if(event.parentSlice){return ChromeUserFriendlyCategoryDriver.fromEvent(event.parentSlice);}}else{return userFriendlyCategory;}} const eventCategoryParts=tr.b.getCategoryParts(event.category);for(let i=0;i<eventCategoryParts.length;++i){const eventCategory=eventCategoryParts[i];userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY[eventCategory];if(userFriendlyCategory){return userFriendlyCategory;}} @@ -4586,7 +4699,8 @@ LayoutObject.prototype={get snapshot(){return this.snapshot_;},get id(){return t return names;},getProperty(name){return this.otherProperties_[name];},get previousSnapshotLayoutObject(){if(!this.snapshot.previousSnapshot)return undefined;return this.snapshot.previousSnapshot.getLayoutObjectById(this.id);},get nextSnapshotLayoutObject(){if(!this.snapshot.nextSnapshot)return undefined;return this.snapshot.nextSnapshot.getLayoutObjectById(this.id);}};return{LayoutObject,};});'use strict';tr.exportTo('tr.e.chrome',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;const ObjectInstance=tr.model.ObjectInstance;function LayoutTreeInstance(){ObjectInstance.apply(this,arguments);} LayoutTreeInstance.prototype={__proto__:ObjectInstance.prototype,};ObjectInstance.subTypes.register(LayoutTreeInstance,{typeName:'LayoutTree'});function LayoutTreeSnapshot(){ObjectSnapshot.apply(this,arguments);this.rootLayoutObject=new tr.e.chrome.LayoutObject(this,this.args);} LayoutTreeSnapshot.prototype={__proto__:ObjectSnapshot.prototype,};ObjectSnapshot.subTypes.register(LayoutTreeSnapshot,{typeName:'LayoutTree'});return{LayoutTreeInstance,LayoutTreeSnapshot,};});'use strict';tr.exportTo('tr.model',function(){function EventContainer(){this.guid_=tr.b.GUID.allocateSimple();this.important=true;this.bounds_=new tr.b.math.Range();} -EventContainer.prototype={get guid(){return this.guid_;},get stableId(){throw new Error('Not implemented');},get bounds(){return this.bounds_;},updateBounds(){throw new Error('Not implemented');},shiftTimestampsForward(amount){throw new Error('Not implemented');},*childEvents(){},*getDescendantEvents(){yield*this.childEvents();for(const container of this.childEventContainers()){yield*container.getDescendantEvents();}},*childEventContainers(){},*getDescendantEventContainers(){yield this;for(const container of this.childEventContainers()){yield*container.getDescendantEventContainers();}},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){},*findTopmostSlices(eventPredicate){for(const ec of this.getDescendantEventContainers()){yield*ec.findTopmostSlicesInThisContainer(eventPredicate);}},*findTopmostSlicesNamed(name){yield*this.findTopmostSlices(e=>e.title===name);}};return{EventContainer,};});'use strict';tr.exportTo('tr.model',function(){const Event=tr.model.Event;const EventRegistry=tr.model.EventRegistry;class ResourceUsageSample extends Event{constructor(series,start,usage){super();this.series_=series;this.start_=start;this.usage_=usage;} +EventContainer.prototype={get guid(){return this.guid_;},get stableId(){throw new Error('Not implemented');},get bounds(){return this.bounds_;},updateBounds(){throw new Error('Not implemented');},shiftTimestampsForward(amount){throw new Error('Not implemented');},*childEvents(){},*getDescendantEvents(){yield*this.childEvents();for(const container of this.childEventContainers()){yield*container.getDescendantEvents();}},*childEventContainers(){},*getDescendantEventContainers(){yield this;for(const container of this.childEventContainers()){yield*container.getDescendantEventContainers();}},*getDescendantEventsInSortedRanges(ranges,opt_containerPredicate){if(opt_containerPredicate===undefined||opt_containerPredicate(this)){for(const event of this.childEvents()){const i=tr.b.findFirstTrueIndexInSortedArray(ranges,range=>event.start<=range.max);if(i<ranges.length&&event.end>=ranges[i].min)yield event;}} +for(const container of this.childEventContainers()){yield*container.getDescendantEventsInSortedRanges(ranges,opt_containerPredicate);}},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){},*findTopmostSlices(eventPredicate){for(const ec of this.getDescendantEventContainers()){yield*ec.findTopmostSlicesInThisContainer(eventPredicate);}},*findTopmostSlicesNamed(name){yield*this.findTopmostSlices(e=>e.title===name);}};return{EventContainer,};});'use strict';tr.exportTo('tr.model',function(){const Event=tr.model.Event;const EventRegistry=tr.model.EventRegistry;class ResourceUsageSample extends Event{constructor(series,start,usage){super();this.series_=series;this.start_=start;this.usage_=usage;} get series(){return this.series_;} get start(){return this.start_;} set start(value){this.start_=value;} @@ -4638,10 +4752,12 @@ let exitTime;if(header.version===5&&header.opcode===kProcessDefunctOpcode){exitT return{pageDirectoryBase,uniqueProcessKey,processId,parentId,sessionId,exitStatus,directoryTableBase,flags,userSID,imageFileName,commandLine,packageFullName,applicationId,exitTime};},decodeStart(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');} process.name=fields.imageFileName;return true;},decodeEnd(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDCStart(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');} process.name=fields.imageFileName;return true;},decodeDCEnd(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDefunct(header,decoder){const fields=this.decodeFields(header,decoder);return true;}};Parser.register(ProcessParser);return{ProcessParser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){const Parser=tr.e.importer.etw.Parser;const guid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';const kThreadStartOpcode=1;const kThreadEndOpcode=2;const kThreadDCStartOpcode=3;const kThreadDCEndOpcode=4;const kThreadCSwitchOpcode=36;function ThreadParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kThreadStartOpcode,ThreadParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kThreadEndOpcode,ThreadParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kThreadDCStartOpcode,ThreadParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kThreadDCEndOpcode,ThreadParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kThreadCSwitchOpcode,ThreadParser.prototype.decodeCSwitch.bind(this));} -ThreadParser.prototype={__proto__:Parser.prototype,decodeFields(header,decoder){if(header.version>3){throw new Error('Incompatible Thread event version.');} +ThreadParser.prototype={__proto__:Parser.prototype,decodeFields(header,decoder){if(header.version>3){throw new Error('Incompatible Thread event version '+ +header.version+'.');} const processId=decoder.decodeUInt32();const threadId=decoder.decodeUInt32();let stackBase;let stackLimit;let userStackBase;let userStackLimit;let affinity;let startAddr;let win32StartAddr;let tebBase;let subProcessTag;let basePriority;let pagePriority;let ioPriority;let threadFlags;let waitMode;if(header.version===1){if(header.opcode===kThreadStartOpcode||header.opcode===kThreadDCStartOpcode){stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);startAddr=decoder.decodeUInteger(header.is64);win32StartAddr=decoder.decodeUInteger(header.is64);waitMode=decoder.decodeInt8();decoder.skip(3);}}else{stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);if(header.version===2){startAddr=decoder.decodeUInteger(header.is64);}else{affinity=decoder.decodeUInteger(header.is64);} win32StartAddr=decoder.decodeUInteger(header.is64);tebBase=decoder.decodeUInteger(header.is64);subProcessTag=decoder.decodeUInt32();if(header.version===3){basePriority=decoder.decodeUInt8();pagePriority=decoder.decodeUInt8();ioPriority=decoder.decodeUInt8();threadFlags=decoder.decodeUInt8();}} -return{processId,threadId,stackBase,stackLimit,userStackBase,userStackLimit,affinity,startAddr,win32StartAddr,tebBase,subProcessTag,waitMode,basePriority,pagePriority,ioPriority,threadFlags};},decodeCSwitchFields(header,decoder){if(header.version!==2){throw new Error('Incompatible Thread event version.');} +return{processId,threadId,stackBase,stackLimit,userStackBase,userStackLimit,affinity,startAddr,win32StartAddr,tebBase,subProcessTag,waitMode,basePriority,pagePriority,ioPriority,threadFlags};},decodeCSwitchFields(header,decoder){if(header.version<2||header.version>4){throw new Error('Incompatible cswitch event version '+ +header.version+'.');} const newThreadId=decoder.decodeUInt32();const oldThreadId=decoder.decodeUInt32();const newThreadPriority=decoder.decodeInt8();const oldThreadPriority=decoder.decodeInt8();const previousCState=decoder.decodeUInt8();const spareByte=decoder.decodeInt8();const oldThreadWaitReason=decoder.decodeInt8();const oldThreadWaitMode=decoder.decodeInt8();const oldThreadState=decoder.decodeInt8();const oldThreadWaitIdealProcessor=decoder.decodeInt8();const newThreadWaitTime=decoder.decodeUInt32();const reserved=decoder.decodeUInt32();return{newThreadId,oldThreadId,newThreadPriority,oldThreadPriority,previousCState,spareByte,oldThreadWaitReason,oldThreadWaitMode,oldThreadState,oldThreadWaitIdealProcessor,newThreadWaitTime,reserved};},decodeStart(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeEnd(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeDCStart(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeDCEnd(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeCSwitch(header,decoder){const fields=this.decodeCSwitchFields(header,decoder);const cpu=this.importer.getOrCreateCpu(header.cpu);const newThread=this.importer.getThreadFromWindowsTid(fields.newThreadId);let newThreadName;if(newThread&&newThread.userFriendlyName){newThreadName=newThread.userFriendlyName;}else{const newProcessId=this.importer.getPidFromWindowsTid(fields.newThreadId);const newProcess=this.model.getProcess(newProcessId);let newProcessName;if(newProcess){newProcessName=newProcess.name;}else{newProcessName='Unknown process';} newThreadName=newProcessName+' (tid '+fields.newThreadId+')';} cpu.switchActiveThread(header.timestamp,{},fields.newThreadId,newThreadName,fields);return true;}};Parser.register(ThreadParser);return{ThreadParser,};});'use strict';tr.exportTo('tr.b',function(){function max(a,b){if(a===undefined)return b;if(b===undefined)return a;return Math.max(a,b);} @@ -4822,13 +4938,14 @@ this.parentContainer.samples.forEach(function(sample){if(this.start<=sample.star function getSliceHi(s){return s.end;} function SliceGroup(parentContainer,opt_sliceConstructor,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;const sliceConstructor=opt_sliceConstructor||ThreadSlice;this.sliceConstructor=sliceConstructor;this.sliceConstructorSubTypes=this.sliceConstructor.subTypes;if(!this.sliceConstructorSubTypes){throw new Error('opt_sliceConstructor must have a subtype registry.');} this.openPartialSlices_=[];this.slices=[];this.topLevelSlices=[];this.haveTopLevelSlicesBeenBuilt=false;this.name_=opt_name;if(this.model===undefined){throw new Error('SliceGroup must have model defined.');}} -SliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.model;},get stableId(){return this.parentContainer_.stableId+'.SliceGroup';},getSettingsKey(){if(!this.name_)return undefined;const parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},get length(){return this.slices.length;},pushSlice(slice){this.haveTopLevelSlicesBeenBuilt=false;slice.parentContainer=this.parentContainer_;this.slices.push(slice);return slice;},pushSlices(slices){this.haveTopLevelSlicesBeenBuilt=false;slices.forEach(function(slice){slice.parentContainer=this.parentContainer_;this.slices.push(slice);},this);},beginSlice(category,title,ts,opt_args,opt_tts,opt_argsStripped,opt_colorId){const colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(category,title);const slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},null,opt_tts,undefined,opt_argsStripped);this.openPartialSlices_.push(slice);slice.didNotFinish=true;this.pushSlice(slice);return slice;},isTimestampValidForBeginOrEnd(ts){if(!this.openPartialSlices_.length)return true;const top=this.openPartialSlices_[this.openPartialSlices_.length-1];return ts>=top.start;},get openSliceCount(){return this.openPartialSlices_.length;},get mostRecentlyOpenedPartialSlice(){if(!this.openPartialSlices_.length)return undefined;return this.openPartialSlices_[this.openPartialSlices_.length-1];},endSlice(ts,opt_tts,opt_colorId){if(!this.openSliceCount){throw new Error('endSlice called without an open slice');} +SliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.model;},get stableId(){return this.parentContainer_.stableId+'.SliceGroup';},getSettingsKey(){if(!this.name_)return undefined;const parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},get length(){return this.slices.length;},pushSlice(slice){this.haveTopLevelSlicesBeenBuilt=false;slice.parentContainer=this.parentContainer_;this.slices.push(slice);return slice;},pushSlices(slices){this.haveTopLevelSlicesBeenBuilt=false;slices.forEach(function(slice){slice.parentContainer=this.parentContainer_;this.slices.push(slice);},this);},beginSlice(category,title,ts,opt_args,opt_tts,opt_argsStripped,opt_colorId,opt_bindId){const colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(category,title);const slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},null,opt_tts,undefined,opt_argsStripped,opt_bindId);this.openPartialSlices_.push(slice);slice.didNotFinish=true;this.pushSlice(slice);return slice;},isTimestampValidForBeginOrEnd(ts){if(!this.openPartialSlices_.length)return true;const top=this.openPartialSlices_[this.openPartialSlices_.length-1];return ts>=top.start;},get openSliceCount(){return this.openPartialSlices_.length;},get mostRecentlyOpenedPartialSlice(){if(!this.openPartialSlices_.length)return undefined;return this.openPartialSlices_[this.openPartialSlices_.length-1];},endSlice(ts,opt_tts,opt_colorId){if(!this.openSliceCount){throw new Error('endSlice called without an open slice');} const slice=this.openPartialSlices_[this.openSliceCount-1];this.openPartialSlices_.splice(this.openSliceCount-1,1);if(ts<slice.start){throw new Error('Slice '+slice.title+' end time is before its start.');} slice.duration=ts-slice.start;slice.didNotFinish=false;slice.colorId=opt_colorId||slice.colorId;if(opt_tts&&slice.cpuStart!==undefined){slice.cpuDuration=opt_tts-slice.cpuStart;} return slice;},pushCompleteSlice(category,title,ts,duration,tts,cpuDuration,opt_args,opt_argsStripped,opt_colorId,opt_bindId){const colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(category,title);const slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},duration,tts,cpuDuration,opt_argsStripped,opt_bindId);if(duration===undefined){slice.didNotFinish=true;} this.pushSlice(slice);return slice;},autoCloseOpenSlices(){this.updateBounds();const maxTimestamp=this.bounds.max;for(let sI=0;sI<this.slices.length;sI++){const slice=this.slices[sI];if(slice.didNotFinish){slice.duration=maxTimestamp-slice.start;}} this.openPartialSlices_=[];},shiftTimestampsForward(amount){for(let sI=0;sI<this.slices.length;sI++){const slice=this.slices[sI];slice.start=(slice.start+amount);}},updateBounds(){this.bounds.reset();for(let i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},copySlice(slice){const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(slice.category,slice.title);const newSlice=new sliceType(slice.category,slice.title,slice.colorId,slice.start,slice.args,slice.duration,slice.cpuStart,slice.cpuDuration);newSlice.didNotFinish=slice.didNotFinish;return newSlice;},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');} -for(const s of this.topLevelSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},*childEvents(){yield*this.slices;},*childEventContainers(){},getSlicesOfName(title){const slices=[];for(let i=0;i<this.slices.length;i++){if(this.slices[i].title===title){slices.push(this.slices[i]);}} +for(const s of this.topLevelSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},*childEvents(){yield*this.slices;},*childEventContainers(){},*getDescendantEventsInSortedRanges(ranges,opt_containerPredicate){if(opt_containerPredicate===undefined||opt_containerPredicate(this)){let rangeIndex=0;let range=ranges[rangeIndex];for(const event of this.childEvents()){while(event.start>range.max){rangeIndex++;if(rangeIndex>=ranges.length)return;range=ranges[rangeIndex];} +if(event.end>=range.min)yield event;}}},getSlicesOfName(title){const slices=[];for(let i=0;i<this.slices.length;i++){if(this.slices[i].title===title){slices.push(this.slices[i]);}} return slices;},iterSlicesInTimeRange(callback,start,end){const ret=[];tr.b.iterateOverIntersectingIntervals(this.topLevelSlices,function(s){return s.start;},function(s){return s.duration;},start,end,function(topLevelSlice){callback(topLevelSlice);for(const slice of topLevelSlice.enumerateAllDescendents()){callback(slice);}});return ret;},findFirstSlice(){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');} if(0===this.slices.length)return undefined;return this.slices[0];},findSliceAtTs(ts){if(!this.haveTopLevelSlicesBeenBuilt)throw new Error('Nope');let i=tr.b.findIndexInSortedClosedIntervals(this.topLevelSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===this.topLevelSlices.length){return undefined;} let curSlice=this.topLevelSlices[i];while(true){i=tr.b.findIndexInSortedClosedIntervals(curSlice.subSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===curSlice.subSlices.length){return curSlice;} @@ -5007,6 +5124,12 @@ if(this.regions_!==undefined&&this.regions_.length!==0){throw new Error('Interna if(this.isLeafNode){return;} this.regions_=undefined;this.children_=this.rule_.children.map(function(childRule){const child=new VMRegionClassificationNode(childRule);child.buildChildNodesRecursively_();return child;});},addStatsFromRegion_(region){this.hasRegions=true;const regionSizeInBytes=region.sizeInBytes;if(regionSizeInBytes!==undefined){this.sizeInBytes=(this.sizeInBytes||0)+regionSizeInBytes;} const thisByteStats=this.byteStats;const regionByteStats=region.byteStats;for(const byteStatName in regionByteStats){const regionByteStatValue=regionByteStats[byteStatName];if(regionByteStatValue===undefined)continue;thisByteStats[byteStatName]=(thisByteStats[byteStatName]||0)+regionByteStatValue;} +if(region.mappedFile.includes('/base.odex')||region.mappedFile.includes('/base.vdex')){if(region.byteStats.proportionalResident!==undefined){thisByteStats.javaBasePss=(thisByteStats.javaBasePss||0)+ +region.byteStats.proportionalResident;} +if(region.byteStats.privateCleanResident!==undefined){thisByteStats.javaBaseCleanResident=(thisByteStats.javaBaseCleanResident||0)+ +region.byteStats.privateCleanResident;} +if(region.byteStats.sharedCleanResident!==undefined){thisByteStats.javaBaseCleanResident=(thisByteStats.javaBaseCleanResident||0)+ +region.byteStats.sharedCleanResident;}} const textProtectionFlags=(VMRegion.PROTECTION_FLAG_READ|VMRegion.PROTECTION_FLAG_EXECUTE);if((region.protectionFlags===textProtectionFlags)&&(region.mappedFile.includes('/base.apk')||region.mappedFile.includes('/libchrome.so'))){if(regionSizeInBytes!==undefined){this.nativeLibrarySizeInBytes=(this.nativeLibrarySizeInBytes||0)+regionSizeInBytes;} if(region.byteStats.privateCleanResident!==undefined){thisByteStats.nativeLibraryPrivateCleanResident=(thisByteStats.nativeLibraryPrivateCleanResident||0)+ region.byteStats.privateCleanResident;} @@ -5054,11 +5177,7 @@ return result;},get sourceInfo(){return this.sourceInfo_;},set parentFrame(paren this.parentFrame_=parentFrame;if(this.parentFrame_){this.parentFrame_.addChild(this);}},addChild(child){this.children.push(child);},removeChild(child){const i=this.children.indexOf(child.id);if(i===-1){throw new Error('omg');} this.children.splice(i,1);},removeAllChildren(){for(let i=0;i<this.children.length;i++){this.children[i].parentFrame_=undefined;} this.children.splice(0,this.children.length);},get stackTrace(){const stack=[this];let cur=this.parentFrame;while(cur){stack.push(cur);cur=cur.parentFrame;} -return stack;},getUserFriendlyStackTrace(){return this.stackTrace.map(function(x){return x.title;});}};return{StackFrame,};});'use strict';tr.exportTo('tr.model.um',function(){class Segment extends tr.model.TimedEvent{constructor(start,duration){super(start);this.duration=duration;this.expectations_=[];} -get expectations(){return this.expectations_;} -clone(){const clone=new Segment(this.start,this.duration);clone.expectations.push(...this.expectations);return clone;} -addSegment(other){this.duration+=other.duration;this.expectations.push(...other.expectations);}} -return{Segment,};});'use strict';tr.exportTo('tr.model.um',function(){class UserModel extends tr.model.EventContainer{constructor(parentModel){super();this.parentModel_=parentModel;this.expectations_=new tr.model.EventSet();this.segments_=[];} +return stack;},getUserFriendlyStackTrace(){return this.stackTrace.map(function(x){return x.title;});}};return{StackFrame,};});'use strict';tr.exportTo('tr.model.um',function(){class UserModel extends tr.model.EventContainer{constructor(parentModel){super();this.parentModel_=parentModel;this.expectations_=new tr.model.EventSet();this.segments_=[];} get stableId(){return'UserModel';} get parentModel(){return this.parentModel_;} sortExpectations(){this.expectations_.sortEvents((x,y)=>(x.start-y.start));} @@ -5569,7 +5688,7 @@ this.scope+delim+this.id;}};return{ScopedId,};});'use strict';tr.exportTo('tr.ui XMarkerAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw(ctx){const dt=this.viewport_.currentDisplayTransform;const viewX=dt.xWorldToView(this.annotation_.timestamp);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,ctx.canvas.height);ctx.strokeStyle=this.annotation_.strokeStyle;ctx.stroke();}};return{XMarkerAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function XMarkerAnnotation(timestamp){tr.model.Annotation.apply(this,arguments);this.timestamp=timestamp;this.strokeStyle='rgba(0, 0, 255, 0.5)';} XMarkerAnnotation.fromDict=function(dict){return new XMarkerAnnotation(dict.args.timestamp);};XMarkerAnnotation.prototype={__proto__:tr.model.Annotation.prototype,toDict(){return{typeName:'xmarker',args:{timestamp:this.timestamp}};},createView_(viewport){return new tr.ui.annotations.XMarkerAnnotationView(viewport,this);}};tr.model.Annotation.register(XMarkerAnnotation,{typeName:'xmarker'});return{XMarkerAnnotation,};});'use strict';tr.exportTo('tr.e.importer',function(){const Base64=tr.b.Base64;const deepCopy=tr.b.deepCopy;const ColorScheme=tr.b.ColorScheme;const HeapDumpTraceEventImporter=tr.e.importer.HeapDumpTraceEventImporter;const LegacyHeapDumpTraceEventImporter=tr.e.importer.LegacyHeapDumpTraceEventImporter;const StreamingEventExpander=tr.e.importer.StreamingEventExpander;const ProfilingDictionaryReader=tr.e.importer.ProfilingDictionaryReader;function getEventColor(event,opt_customName){if(event.cname){return ColorScheme.getColorIdForReservedName(event.cname);}else if(opt_customName||event.name){return ColorScheme.getColorIdForGeneralPurposeString(opt_customName||event.name);}} function isLegacyChromeClockSyncEvent(event){return event.name!==undefined&&event.name.startsWith(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX)&&((event.ph==='S')||(event.ph==='F'));} -const PRODUCER='producer';const CONSUMER='consumer';const STEP='step';const BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;const LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;const DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;const MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER=[undefined,BACKGROUND,LIGHT,DETAILED];const GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX='global/';const LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX='ClockSyncEvent.';const BYTE_STAT_NAME_MAP={'pc':'privateCleanResident','pd':'privateDirtyResident','sc':'sharedCleanResident','sd':'sharedDirtyResident','pss':'proportionalResident','sw':'swapped'};const WEAK_MEMORY_ALLOCATOR_DUMP_FLAG=1<<0;const OBJECT_TYPE_NAME_PATTERNS=[{prefix:'const char *WTF::getStringWithTypeName() [T = ',suffix:']'},{prefix:'const char* WTF::getStringWithTypeName() [with T = ',suffix:']'},{prefix:'const char *__cdecl WTF::getStringWithTypeName<',suffix:'>(void)'}];const SUBTRACE_FIELDS=new Set(['powerTraceAsString','systemTraceEvents',]);const NON_METADATA_FIELDS=new Set(['displayTimeUnit','samples','stackFrames','traceAnnotations','traceEvents',...SUBTRACE_FIELDS]);function TraceEventImporter(model,eventData){this.hasEvents_=undefined;this.importPriority=1;this.model_=model;this.events_=undefined;this.sampleEvents_=undefined;this.stackFrameEvents_=undefined;this.stackFrameTree_=new tr.model.ProfileTree();this.subtraces_=[];this.eventsWereFromString_=false;this.softwareMeasuredCpuCount_=undefined;this.allAsyncEvents_=[];this.allFlowEvents_=[];this.allObjectEvents_=[];this.contextProcessorPerThread={};this.traceEventSampleStackFramesByName_={};this.v8ProcessCodeMaps_={};this.v8ProcessRootStackFrame_={};this.v8SamplingData_=[];this.profileTrees_=new Map();this.profileInfo_=new Map();this.legacyChromeClockSyncStartEvent_=undefined;this.legacyChromeClockSyncFinishEvent_=undefined;this.allMemoryDumpEvents_={};this.heapProfileExpander=new ProfilingDictionaryReader();this.objectTypeNameMap_={};this.clockDomainId_=tr.model.ClockDomainId.UNKNOWN_CHROME_LEGACY;this.toModelTime_=undefined;if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();if(eventData[0]==='['){eventData=eventData.replace(/\s*,\s*$/,'');if(eventData[eventData.length-1]!==']'){eventData=eventData+']';}} +const PRODUCER='producer';const CONSUMER='consumer';const STEP='step';const BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;const LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;const DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;const MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER=[undefined,BACKGROUND,LIGHT,DETAILED];const GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX='global/';const LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX='ClockSyncEvent.';const BYTE_STAT_NAME_MAP={'pc':'privateCleanResident','pd':'privateDirtyResident','sc':'sharedCleanResident','sd':'sharedDirtyResident','pss':'proportionalResident','sw':'swapped'};const WEAK_MEMORY_ALLOCATOR_DUMP_FLAG=1<<0;const OBJECT_TYPE_NAME_PATTERNS=[{prefix:'const char *WTF::getStringWithTypeName() [T = ',suffix:']'},{prefix:'const char* WTF::getStringWithTypeName() [with T = ',suffix:']'},{prefix:'const char *__cdecl WTF::getStringWithTypeName<',suffix:'>(void)'}];const SUBTRACE_FIELDS=new Set(['powerTraceAsString','systemTraceEvents','androidProcessDump',]);const NON_METADATA_FIELDS=new Set(['displayTimeUnit','samples','stackFrames','traceAnnotations','traceEvents',...SUBTRACE_FIELDS]);function TraceEventImporter(model,eventData){this.hasEvents_=undefined;this.importPriority=1;this.model_=model;this.events_=undefined;this.sampleEvents_=undefined;this.stackFrameEvents_=undefined;this.stackFrameTree_=new tr.model.ProfileTree();this.subtraces_=[];this.eventsWereFromString_=false;this.softwareMeasuredCpuCount_=undefined;this.allAsyncEvents_=[];this.allFlowEvents_=[];this.allObjectEvents_=[];this.contextProcessorPerThread={};this.traceEventSampleStackFramesByName_={};this.v8ProcessCodeMaps_={};this.v8ProcessRootStackFrame_={};this.v8SamplingData_=[];this.profileTrees_=new Map();this.profileInfo_=new Map();this.legacyChromeClockSyncStartEvent_=undefined;this.legacyChromeClockSyncFinishEvent_=undefined;this.allMemoryDumpEvents_={};this.heapProfileExpander=new ProfilingDictionaryReader();this.objectTypeNameMap_={};this.clockDomainId_=tr.model.ClockDomainId.UNKNOWN_CHROME_LEGACY;this.toModelTime_=undefined;if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();if(eventData[0]==='['){eventData=eventData.replace(/\s*,\s*$/,'');if(eventData[eventData.length-1]!==']'){eventData=eventData+']';}} this.events_=JSON.parse(eventData);this.eventsWereFromString_=true;}else{this.events_=eventData;} if(this.events_.traceEvents){const container=this.events_;this.events_=this.events_.traceEvents;for(const subtraceField of SUBTRACE_FIELDS){if(container[subtraceField]){this.storeSubtrace_(container[subtraceField]);}} this.storeSamples_(container.samples);this.storeStackFrames_(container.stackFrames);this.storeDisplayTimeUnit_(container.displayTimeUnit);this.storeTraceAnnotations_(container.traceAnnotations);this.storeMetadata_(container);}else if(this.events_ instanceof tr.b.TraceStream){const parser=oboe().node('{cat ph}',function(e){return oboe.drop;}).node('!.powerTraceAsString',this.storeSubtrace_.bind(this)).node('!.systemTraceEvents',this.storeSubtrace_.bind(this)).node('!.samples',this.storeSamples_.bind(this)).node('!.stackFrames',this.storeStackFrames_.bind(this)).node('!.displayTimeUnit',this.storeDisplayTimeUnit_.bind(this)).node('!.traceAnnotations',this.storeTraceAnnotations_.bind(this)).done(this.storeMetadata_.bind(this));this.events_.rewind();while(this.events_.hasData){parser.write(this.events_.readNumBytes());} @@ -5588,8 +5707,8 @@ if(ctr.numSeries===0){this.model_.importWarning({type:'counter_parse_error',mess const ts=this.toModelTimeFromUs_(event.ts);ctr.series.forEach(function(series){const val=event.args[series.name]?event.args[series.name]:0;series.addCounterSample(ts,val);});},processObjectEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allObjectEvents_.push({sequenceNumber:this.allObjectEvents_.length,event,thread});if(thread.guid in this.contextProcessorPerThread){const processor=this.contextProcessorPerThread[thread.guid];const scopedId=TraceEventImporter.scopedIdForEvent_(event);if(event.ph==='D'){processor.destroyContext(scopedId);} processor.invalidateContextCacheForSnapshot(scopedId);}},processContextEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(!(thread.guid in this.contextProcessorPerThread)){this.contextProcessorPerThread[thread.guid]=new tr.importer.ContextProcessor(this.model_);} const scopedId=TraceEventImporter.scopedIdForEvent_(event);const contextType=event.name;const processor=this.contextProcessorPerThread[thread.guid];if(event.ph==='('){processor.enterContext(contextType,scopedId);}else if(event.ph===')'){processor.leaveContext(contextType,scopedId);}else{this.model_.importWarning({type:'unknown_context_phase',message:'Unknown context event phase: '+event.ph+'.'});}},setContextsFromThread_(thread,slice){if(thread.guid in this.contextProcessorPerThread){slice.contexts=this.contextProcessorPerThread[thread.guid].activeContexts;}},processDurationEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);const ts=this.toModelTimeFromUs_(event.ts);if(event.dur===0&&!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'duration_parse_error',message:'Timestamps are moving backward.'});return;} -if(event.ph==='B'){const slice=thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));slice.startStackFrame=this.getStackFrameForEvent_(event);this.setContextsFromThread_(thread,slice);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){if(event.s!==undefined&&event.s!=='t'){throw new Error('This should never happen');} -thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event));const slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts));slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=undefined;}else{if(!thread.sliceGroup.openSliceCount){this.model_.importWarning({type:'duration_parse_error',message:'E phase event without a matching B phase event.'});return;} +if(event.ph==='B'){const slice=thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);this.setContextsFromThread_(thread,slice);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){if(event.s!==undefined&&event.s!=='t'){throw new Error('This should never happen');} +thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event),event.bind_id);const slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts));slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=undefined;}else{if(!thread.sliceGroup.openSliceCount){this.model_.importWarning({type:'duration_parse_error',message:'E phase event without a matching B phase event.'});return;} const slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts),getEventColor(event));if(event.name&&slice.title!==event.name){this.model_.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+ slice.title+' in openSlice, and is '+ event.name+' in endSlice'});} @@ -5670,7 +5789,7 @@ const leafNode=this.stackFrameTree_.getNode('g'+event.sf);const sample=new tr.mo if(event.cat===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'cat parameter.'});continue;} if(event.name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'name parameter.'});continue;} const id=TraceEventImporter.scopedIdForEvent_(event);if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require an '+'id parameter.'});continue;} -if(event.cat==='blink.user_timing'){const matched=/([^\/:]+):([^\/:]+)\/?(.*)/.exec(event.name);if(matched!==null){const key=matched[1]+':'+event.cat;event.args=JSON.parse(Base64.atob(matched[3])||'{}');if(nestableMeasureAsyncEventsByKey[key]===undefined){nestableMeasureAsyncEventsByKey[key]=[];} +if(event.cat==='blink.user_timing'){const matched=/([^\/:]+):([^\/]+)\/?(.*)/.exec(event.name);if(matched!==null){const key=matched[1]+':'+event.cat;event.args=JSON.parse(Base64.atob(matched[3])||'{}');if(nestableMeasureAsyncEventsByKey[key]===undefined){nestableMeasureAsyncEventsByKey[key]=[];} nestableMeasureAsyncEventsByKey[key].push(asyncEventState);continue;}} const key=event.cat+':'+id.toStringWithDelimiter(':');if(nestableAsyncEventsByKey[key]===undefined){nestableAsyncEventsByKey[key]=[];} nestableAsyncEventsByKey[key].push(asyncEventState);} @@ -5997,7 +6116,7 @@ powerCounter.series.forEach(function(series){if(series.name==='Min Frequency'){s if(series.name==='Max Frequency'){series.addCounterSample(ts,maxFreq);}});},powerStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/type=(\d+) state=(\d) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const cpuState=parseInt(event[2]);this.cpuStateSlice(ts,targetCpuNumber,event[1],cpuState);return true;},powerFrequencyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/type=(\d+) state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const powerState=parseInt(event[2]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[2]);const powerState=parseInt(event[1]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyLimitsEvent(eventName,cpu,pid,ts,eventBase){const event=/min=(\d+) max=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const minFreq=parseInt(event[1]);const maxFreq=parseInt(event[2]);this.cpuFrequencyLimitsSlice(ts,targetCpuNumber,minFreq,maxFreq);return true;},cpuIdleEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[2]);const cpuState=parseInt(event[1]);this.cpuIdleSlice(ts,targetCpuNumber,cpuState);return true;}};Parser.register(PowerParser);return{PowerParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function RegulatorParser(importer){Parser.call(this,importer);importer.registerEventHandler('regulator_enable',RegulatorParser.prototype.regulatorEnableEvent.bind(this));importer.registerEventHandler('regulator_enable_delay',RegulatorParser.prototype.regulatorEnableDelayEvent.bind(this));importer.registerEventHandler('regulator_enable_complete',RegulatorParser.prototype.regulatorEnableCompleteEvent.bind(this));importer.registerEventHandler('regulator_disable',RegulatorParser.prototype.regulatorDisableEvent.bind(this));importer.registerEventHandler('regulator_disable_complete',RegulatorParser.prototype.regulatorDisableCompleteEvent.bind(this));importer.registerEventHandler('regulator_set_voltage',RegulatorParser.prototype.regulatorSetVoltageEvent.bind(this));importer.registerEventHandler('regulator_set_voltage_complete',RegulatorParser.prototype.regulatorSetVoltageCompleteEvent.bind(this));this.model_=importer.model_;} const regulatorEnableRE=/name=(.+)/;const regulatorDisableRE=/name=(.+)/;const regulatorSetVoltageCompleteRE=/name=(\S+), val=(\d+)/;RegulatorParser.prototype={__proto__:Parser.prototype,getCtr_(ctrName,valueName){const ctr=this.model_.kernel.getOrCreateCounter(null,'vreg '+ctrName+' '+valueName);if(ctr.series[0]===undefined){ctr.addSeries(new tr.model.CounterSeries(valueName,ColorScheme.getColorIdForGeneralPurposeString(ctrName+'.'+valueName)));} return ctr;},regulatorEnableEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorEnableRE.exec(eventBase.details);if(!event)return false;const name=event[1];const ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,1);return true;},regulatorEnableDelayEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorEnableCompleteEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorDisableEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorDisableRE.exec(eventBase.details);if(!event)return false;const name=event[1];const ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,0);return true;},regulatorDisableCompleteEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageCompleteEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorSetVoltageCompleteRE.exec(eventBase.details);if(!event)return false;const name=event[1];const voltage=parseInt(event[2]);const ctr=this.getCtr_(name,'voltage');ctr.series[0].addCounterSample(ts,voltage);return true;}};Parser.register(RegulatorParser);return{RegulatorParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function SchedParser(importer){Parser.call(this,importer);importer.registerEventHandler('sched_switch',SchedParser.prototype.schedSwitchEvent.bind(this));importer.registerEventHandler('sched_wakeup',SchedParser.prototype.schedWakeupEvent.bind(this));importer.registerEventHandler('sched_blocked_reason',SchedParser.prototype.schedBlockedEvent.bind(this));importer.registerEventHandler('sched_cpu_hotplug',SchedParser.prototype.schedCpuHotplugEvent.bind(this));} -const TestExports={};const schedSwitchRE=new RegExp('prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) '+'prev_state=(\\S\\+?|\\S\\|\\S) ==> '+'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');const schedBlockedRE=new RegExp('pid=(\\d+) iowait=(\\d) caller=(.+)');TestExports.schedSwitchRE=schedSwitchRE;const schedWakeupRE=/comm=(.+) pid=(\d+) prio=(\d+)(?: success=\d+)? target_cpu=(\d+)/;TestExports.schedWakeupRE=schedWakeupRE;SchedParser.prototype={__proto__:Parser.prototype,schedSwitchEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedSwitchRE.exec(eventBase.details);if(!event)return false;const prevState=event[4];const nextComm=event[5];const nextPid=parseInt(event[6]);const nextPrio=parseInt(event[7]);if(eventBase.tgid!==undefined){const tgid=parseInt(eventBase.tgid);const process=this.importer.model_.getOrCreateProcess(tgid);if(!process.getThread(pid)){const thread=process.getOrCreateThread(pid);thread.name=eventBase.threadName;}} +const TestExports={};const schedSwitchRE=new RegExp('prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) '+'prev_state=(\\S\\+?|\\S\\|\\S) ==> '+'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');const schedBlockedRE=new RegExp('pid=(\\d+) iowait=(\\d) caller=(.+)');TestExports.schedSwitchRE=schedSwitchRE;const schedWakeupRE=/comm=(.+) pid=(\d+) prio=(\d+)(?: success=\d+)? target_cpu=(\d+)/;TestExports.schedWakeupRE=schedWakeupRE;const unknownThreadName='<...>';SchedParser.prototype={__proto__:Parser.prototype,schedSwitchEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedSwitchRE.exec(eventBase.details);if(!event)return false;const prevState=event[4];const nextComm=event[5];const nextPid=parseInt(event[6]);const nextPrio=parseInt(event[7]);if(eventBase.tgid!==undefined){const tgid=parseInt(eventBase.tgid);const process=this.importer.model_.getOrCreateProcess(tgid);const storedThread=process.getThread(pid);if(!storedThread){const thread=process.getOrCreateThread(pid);thread.name=eventBase.threadName;}else if(storedThread.name===unknownThreadName){storedThread.name=eventBase.threadName;}} const nextThread=this.importer.threadsByLinuxPid[nextPid];let nextName;if(nextThread){nextName=nextThread.userFriendlyName;}else{nextName=nextComm;} const cpu=this.importer.getOrCreateCpu(cpuNumber);cpu.switchActiveThread(ts,{stateWhenDescheduled:prevState},nextPid,nextName,{comm:nextComm,tid:nextPid,prio:nextPrio});return true;},schedWakeupEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedWakeupRE.exec(eventBase.details);if(!event)return false;const fromPid=pid;const comm=event[1];pid=parseInt(event[2]);const prio=parseInt(event[3]);this.importer.markPidRunnable(ts,pid,comm,prio,fromPid);return true;},schedCpuHotplugEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/cpu (\d+) (.+) error=(\d+)/.exec(eventBase.details);if(!event)return false;cpuNumber=event[1];const state=event[2];const targetCpu=this.importer.getOrCreateCpu(cpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','Cpu Hotplug');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('State',tr.b.ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'State')));} powerCounter.series.forEach(function(series){if(series.name==='State'){series.addCounterSample(ts,state.localeCompare('offline')?0:1);}});return true;},schedBlockedEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedBlockedRE.exec(eventBase.details);if(!event)return false;pid=parseInt(event[1]);const iowait=parseInt(event[2]);const caller=event[3];this.importer.addPidBlockedReason(ts,pid,iowait,caller);return true;}};Parser.register(SchedParser);return{SchedParser,_SchedParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function SyncParser(importer){Parser.call(this,importer);importer.registerEventHandler('sync_timeline',SyncParser.prototype.timelineEvent.bind(this));importer.registerEventHandler('sync_wait',SyncParser.prototype.syncWaitEvent.bind(this));importer.registerEventHandler('sync_pt',SyncParser.prototype.syncPtEvent.bind(this));this.model_=importer.model_;} @@ -6140,7 +6259,7 @@ if(x.end!==y.end){return x.end-y.end;} if(x.guid&&y.guid){return x.guid-y.guid;} return 0;} function forEventTypesIn(events,typeNames,cb,opt_this){events.forEach(function(event){if(typeNames.indexOf(event.typeName)>=0){cb.call(opt_this,event);}});} -function causedFrame(event){return event.associatedEvents.some(x=>x.title===tr.model.helpers.IMPL_RENDERING_STATS);} +function causedFrame(event){return event.associatedEvents.some(isImplFrameEvent);} function getSortedFrameEventsByProcess(modelHelper){const frameEventsByPid={};for(const[pid,rendererHelper]of Object.entries(modelHelper.rendererHelpers)){frameEventsByPid[pid]=rendererHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds);} return frameEventsByPid;} @@ -6223,7 +6342,8 @@ function fixResponseAnimationStarts(protoExpectations){protoExpectations.forEach protoExpectations.forEach(function(rpe){if(rpe.type!==ProtoExpectation.RESPONSE_TYPE){return;} if(!ape.containsTimestampInclusive(rpe.end)){return;} if(ape.containsTimestampInclusive(rpe.start)){return;} -ape.start=rpe.end;});});return protoExpectations;} +ape.start=rpe.end;if(ape.associatedEvents!==undefined){ape.associatedEvents=ape.associatedEvents.filter(e=>(!isImplFrameEvent(e)||e.start>=ape.start));}});});return protoExpectations;} +function isImplFrameEvent(event){return event.title===tr.model.helpers.IMPL_RENDERING_STATS;} function fixTapResponseTouchAnimations(protoExpectations){function isTapResponse(pe){return(pe.type===ProtoExpectation.RESPONSE_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TAP]);} function isTouchAnimation(pe){return(pe.type===ProtoExpectation.ANIMATION_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TOUCH_MOVE])&&!pe.containsTypeNames([INPUT_TYPE.SCROLL_UPDATE,INPUT_TYPE.PINCH_UPDATE]);} const newPEs=[];while(protoExpectations.length){const pe=protoExpectations.shift();newPEs.push(pe);const peIsTapResponse=isTapResponse(pe);const peIsTouchAnimation=isTouchAnimation(pe);if(!peIsTapResponse&&!peIsTouchAnimation){continue;} @@ -6235,7 +6355,7 @@ if(frameEvents.length===0&&!(pe.initiatorType===INITIATOR_TYPE.WEBGL||pe.initiat pe.associatedEvents.addEventSet(frameEvents);newPEs.push(pe);} return newPEs;} function checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations,warn){const handledEvents=[];protoExpectations.forEach(function(protoExpectation){protoExpectation.associatedEvents.forEach(function(event){if((event.title===CSS_ANIMATION_TITLE)&&(event.subSlices.length>0)){return;} -if((handledEvents.indexOf(event)>=0)&&(event.title!==tr.model.helpers.IMPL_RENDERING_STATS)){warn({type:'UserModelBuilder',message:`double-handled event: ${event.typeName} @ ${event.start}`,showToUser:false,});return;} +if((handledEvents.indexOf(event)>=0)&&(!isImplFrameEvent(event))){warn({type:'UserModelBuilder',message:`double-handled event: ${event.typeName} @ ${event.start}`,showToUser:false,});return;} handledEvents.push(event);});});sortedInputEvents.forEach(function(event){if(handledEvents.indexOf(event)<0){warn({type:'UserModelBuilder',message:`double-handled event: ${event.typeName} @ ${event.start}`,showToUser:false,});}});} function findInputExpectations(modelHelper){let warning;function warn(w){if(warning)return;warning=w;} const sortedInputEvents=getSortedInputEvents(modelHelper);let protoExpectations=findProtoExpectations(modelHelper,sortedInputEvents,warn);protoExpectations=postProcessProtoExpectations(modelHelper,protoExpectations);checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations,warn);if(warning)modelHelper.model.importWarning(warning);const expectations=[];protoExpectations.forEach(function(protoExpectation){const ir=protoExpectation.createInteractionRecord(modelHelper.model);if(ir){expectations.push(ir);}});return expectations;} @@ -6258,19 +6378,22 @@ function makeKeyUniqueAndSet(map,key,value){let uniqueKey=key;let nextIndex=2;wh 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));} function hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);} -return{hasCategoryAndName,filterExpectationsByRange,perceptualBlend,splitGlobalDumpsByBrowserName};});'use strict';tr.exportTo('tr.e.chrome',function(){const CHROME_INTERNAL_URLS=['','about:blank','data:text/html,pluginplaceholderdata','chrome-error://chromewebdata/'];const TOP_LEVEL_TASK_TITLES=['TaskQueueManager::ProcessTaskFromWorkQueue','ThreadControllerImpl::DoWork',];class EventFinderUtils{static hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);} -static getSortedMainThreadEventsByFrame(rendererHelper,eventTitle,eventCategory){const eventsByFrame=new Map();for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(rendererHelper.isTelemetryInternalEvent(ev))continue;if(!this.hasCategoryAndName(ev,eventCategory,eventTitle)){continue;} -const frameIdRef=ev.args.frame;if(frameIdRef===undefined)continue;if(!eventsByFrame.has(frameIdRef)){eventsByFrame.set(frameIdRef,[]);} +return{hasCategoryAndName,filterExpectationsByRange,perceptualBlend,splitGlobalDumpsByBrowserName};});'use strict';tr.exportTo('tr.e.chrome',function(){const CHROME_INTERNAL_URLS=['','about:blank','data:text/html,pluginplaceholderdata','chrome-error://chromewebdata/'];const SCHEDULER_TOP_LEVEL_TASK_TITLE='ThreadControllerImpl::RunTask';const SCHEDULOER_TOP_LEVEL_TASKS=new Set([SCHEDULER_TOP_LEVEL_TASK_TITLE,'ThreadControllerImpl::DoWork','TaskQueueManager::ProcessTaskFromWorkQueue']);class EventFinderUtils{static hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);} +static*getMainThreadEvents(rendererHelper,eventTitle,eventCategory){if(!rendererHelper.mainThread)return;for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(rendererHelper.isTelemetryInternalEvent(ev))continue;if(!this.hasCategoryAndName(ev,eventCategory,eventTitle)){continue;} +yield ev;}} +static getSortedMainThreadEventsByFrame(rendererHelper,eventTitle,eventCategory){const eventsByFrame=new Map();const events=this.getMainThreadEvents(rendererHelper,eventTitle,eventCategory);for(const ev of events){const frameIdRef=ev.args.frame;if(frameIdRef===undefined)continue;if(!eventsByFrame.has(frameIdRef)){eventsByFrame.set(frameIdRef,[]);} eventsByFrame.get(frameIdRef).push(ev);} return eventsByFrame;} +static getSortedMainThreadEventsByNavId(rendererHelper,eventTitle,eventCategory){const eventsByNavId=new Map();const events=this.getMainThreadEvents(rendererHelper,eventTitle,eventCategory);for(const ev of events){if(ev.args.data===undefined)continue;const navIdRef=ev.args.data.navigationId;if(navIdRef===undefined)continue;eventsByNavId.set(navIdRef,ev);} +return eventsByNavId;} static findLastEventStartingOnOrBeforeTimestamp(sortedEvents,timestamp){const firstIndexAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>timestamp);if(firstIndexAfterTimestamp===0)return undefined;return sortedEvents[firstIndexAfterTimestamp-1];} static findLastEventStartingBeforeTimestamp(sortedEvents,timestamp){const firstIndexAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>=timestamp);if(firstIndexAfterTimestamp===0)return undefined;return sortedEvents[firstIndexAfterTimestamp-1];} static findNextEventStartingOnOrAfterTimestamp(sortedEvents,timestamp){const firstIndexOnOrAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>=timestamp);if(firstIndexOnOrAfterTimestamp===sortedEvents.length){return undefined;} return sortedEvents[firstIndexOnOrAfterTimestamp];} static findNextEventStartingAfterTimestamp(sortedEvents,timestamp){const firstIndexOnOrAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>timestamp);if(firstIndexOnOrAfterTimestamp===sortedEvents.length){return undefined;} return sortedEvents[firstIndexOnOrAfterTimestamp];} -static findToplevelSchedulerTasks(mainThread){const titles=new Set(TOP_LEVEL_TASK_TITLES);const tasks=[];tasks.push(...mainThread.findTopmostSlices(slice=>titles.has(slice.title)));return tasks;}} -return{EventFinderUtils,CHROME_INTERNAL_URLS,};});'use strict';tr.exportTo('tr.e.chrome',function(){const TIME_TO_INTERACTIVE_WINDOW_SIZE_MS=5000;const ACTIVE_REQUEST_TOLERANCE=2;const FCI_MIN_CLUSTER_SEPARATION_MS=1000;const TASK_CLUSTER_HEAVINESS_THRESHOLD_MS=250;const ENDPOINT_TYPES={LONG_TASK_START:'LONG_TASK_START',LONG_TASK_END:'LONG_TASK_END',REQUEST_START:'REQUEST_START',REQUEST_END:'REQUEST_END'};function getEndpoints_(events,startType,endType){const endpoints=[];for(const event of events){endpoints.push({time:event.start,type:startType});endpoints.push({time:event.end,type:endType});} +static findToplevelSchedulerTasks(mainThread){const tasks=[];tasks.push(...mainThread.findTopmostSlices(slice=>slice.category==='toplevel'&&SCHEDULOER_TOP_LEVEL_TASKS.has(slice.title)));return tasks;}} +return{EventFinderUtils,CHROME_INTERNAL_URLS,SCHEDULER_TOP_LEVEL_TASK_TITLE,};});'use strict';tr.exportTo('tr.e.chrome',function(){const TIME_TO_INTERACTIVE_WINDOW_SIZE_MS=5000;const ACTIVE_REQUEST_TOLERANCE=2;const FCI_MIN_CLUSTER_SEPARATION_MS=1000;const TASK_CLUSTER_HEAVINESS_THRESHOLD_MS=250;const ENDPOINT_TYPES={LONG_TASK_START:'LONG_TASK_START',LONG_TASK_END:'LONG_TASK_END',REQUEST_START:'REQUEST_START',REQUEST_END:'REQUEST_END'};function getEndpoints_(events,startType,endType){const endpoints=[];for(const event of events){endpoints.push({time:event.start,type:startType});endpoints.push({time:event.end,type:endType});} return endpoints;} function reachedTTIQuiscence_(timestamp,networkQuietWindowStart,mainThreadQuietWindowStart){if(networkQuietWindowStart===undefined||mainThreadQuietWindowStart===undefined){return false;} const mainThreadQuietForLongEnough=timestamp-mainThreadQuietWindowStart>=TIME_TO_INTERACTIVE_WINDOW_SIZE_MS;const networkQuietForLongEnough=timestamp-networkQuietWindowStart>=TIME_TO_INTERACTIVE_WINDOW_SIZE_MS;return mainThreadQuietForLongEnough&&networkQuietForLongEnough;} @@ -7018,7 +7141,7 @@ subDiagnostic.mergeRelationships(otherDiagnostic,parentHist,otherParentHist);}} get length(){return this._diagnostics.length;}*[Symbol.iterator](){for(const diagnostic of this._diagnostics)yield diagnostic;} asDictInto_(d){d.diagnostics=this._diagnostics.map(d=>d.asDictOrReference());} static fromDict(d){return new UnmergeableDiagnosticSet(d.diagnostics.map(d=>((typeof d==='string')?new tr.v.d.DiagnosticRef(d):tr.v.d.Diagnostic.fromDict(d))));}} -tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const RESERVED_INFOS={ANGLE_REVISIONS:{name:'angleRevisions',type:tr.v.d.GenericSet},ARCHITECTURES:{name:'architectures',type:tr.v.d.GenericSet},BENCHMARKS:{name:'benchmarks',type:tr.v.d.GenericSet},BENCHMARK_START:{name:'benchmarkStart',type:tr.v.d.DateRange},BENCHMARK_DESCRIPTIONS:{name:'benchmarkDescriptions',type:tr.v.d.GenericSet},BOTS:{name:'bots',type:tr.v.d.GenericSet},BUG_COMPONENTS:{name:'bugComponents',type:tr.v.d.GenericSet},BUILDS:{name:'builds',type:tr.v.d.GenericSet},CATAPULT_REVISIONS:{name:'catapultRevisions',type:tr.v.d.GenericSet},CHROMIUM_COMMIT_POSITIONS:{name:'chromiumCommitPositions',type:tr.v.d.GenericSet},CHROMIUM_REVISIONS:{name:'chromiumRevisions',type:tr.v.d.GenericSet},DEVICE_IDS:{name:'deviceIds',type:tr.v.d.GenericSet},DOCUMENTATION_URLS:{name:'documentationUrls',type:tr.v.d.GenericSet},FUCHSIA_GARNET_REVISIONS:{name:'fuchsiaGarnetRevisions',type:tr.v.d.GenericSet},FUCHSIA_PERIDOT_REVISIONS:{name:'fuchsiaPeridotRevisions',type:tr.v.d.GenericSet},FUCHSIA_TOPAZ_REVISIONS:{name:'fuchsiaTopazRevisions',type:tr.v.d.GenericSet},FUCHSIA_ZIRCON_REVISIONS:{name:'fuchsiaZirconRevisions',type:tr.v.d.GenericSet},GPUS:{name:'gpus',type:tr.v.d.GenericSet},GROUPING_PATH:{name:'groupingPath',type:tr.v.d.GroupingPath},IS_REFERENCE_BUILD:{name:'isReferenceBuild',type:tr.v.d.GenericSet},LABELS:{name:'labels',type:tr.v.d.GenericSet},LOG_URLS:{name:'logUrls',type:tr.v.d.GenericSet},MASTERS:{name:'masters',type:tr.v.d.GenericSet},MEMORY_AMOUNTS:{name:'memoryAmounts',type:tr.v.d.GenericSet},MERGED_FROM:{name:'mergedFrom',type:tr.v.d.RelatedHistogramMap},MERGED_TO:{name:'mergedTo',type:tr.v.d.RelatedHistogramMap},OS_NAMES:{name:'osNames',type:tr.v.d.GenericSet},OS_VERSIONS:{name:'osVersions',type:tr.v.d.GenericSet},OWNERS:{name:'owners',type:tr.v.d.GenericSet},POINT_ID:{name:'pointId',type:tr.v.d.GenericSet},PRODUCT_VERSIONS:{name:'productVersions',type:tr.v.d.GenericSet},RELATED_NAMES:{name:'relatedNames',type:tr.v.d.GenericSet},SKIA_REVISIONS:{name:'skiaRevisions',type:tr.v.d.GenericSet},STORIES:{name:'stories',type:tr.v.d.GenericSet},STORYSET_REPEATS:{name:'storysetRepeats',type:tr.v.d.GenericSet},STORY_TAGS:{name:'storyTags',type:tr.v.d.GenericSet},SUMMARY_KEYS:{name:'summaryKeys',type:tr.v.d.GenericSet},TAG_MAP:{name:'tagmap',type:tr.v.d.TagMap},TEST_PATH:{name:'testPath',type:tr.v.d.GenericSet},TRACE_START:{name:'traceStart',type:tr.v.d.DateRange},TRACE_URLS:{name:'traceUrls',type:tr.v.d.GenericSet},V8_COMMIT_POSITIONS:{name:'v8CommitPositions',type:tr.v.d.DateRange},V8_REVISIONS:{name:'v8Revisions',type:tr.v.d.GenericSet},WEBRTC_REVISIONS:{name:'webrtcRevisions',type:tr.v.d.GenericSet},};const RESERVED_NAMES={};const RESERVED_NAMES_TO_TYPES=new Map();for(const[codename,info]of Object.entries(RESERVED_INFOS)){RESERVED_NAMES[codename]=info.name;if(RESERVED_NAMES_TO_TYPES.has(info.name)){throw new Error(`Duplicate reserved name "${info.name}"`);} +tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const RESERVED_INFOS={ANGLE_REVISIONS:{name:'angleRevisions',type:tr.v.d.GenericSet},ARCHITECTURES:{name:'architectures',type:tr.v.d.GenericSet},BENCHMARKS:{name:'benchmarks',type:tr.v.d.GenericSet},BENCHMARK_START:{name:'benchmarkStart',type:tr.v.d.DateRange},BENCHMARK_DESCRIPTIONS:{name:'benchmarkDescriptions',type:tr.v.d.GenericSet},BOTS:{name:'bots',type:tr.v.d.GenericSet},BUG_COMPONENTS:{name:'bugComponents',type:tr.v.d.GenericSet},BUILDS:{name:'builds',type:tr.v.d.GenericSet},CATAPULT_REVISIONS:{name:'catapultRevisions',type:tr.v.d.GenericSet},CHROMIUM_COMMIT_POSITIONS:{name:'chromiumCommitPositions',type:tr.v.d.GenericSet},CHROMIUM_REVISIONS:{name:'chromiumRevisions',type:tr.v.d.GenericSet},DEVICE_IDS:{name:'deviceIds',type:tr.v.d.GenericSet},DOCUMENTATION_URLS:{name:'documentationUrls',type:tr.v.d.GenericSet},FUCHSIA_GARNET_REVISIONS:{name:'fuchsiaGarnetRevisions',type:tr.v.d.GenericSet},FUCHSIA_PERIDOT_REVISIONS:{name:'fuchsiaPeridotRevisions',type:tr.v.d.GenericSet},FUCHSIA_TOPAZ_REVISIONS:{name:'fuchsiaTopazRevisions',type:tr.v.d.GenericSet},FUCHSIA_ZIRCON_REVISIONS:{name:'fuchsiaZirconRevisions',type:tr.v.d.GenericSet},GPUS:{name:'gpus',type:tr.v.d.GenericSet},GROUPING_PATH:{name:'groupingPath',type:tr.v.d.GroupingPath},IS_REFERENCE_BUILD:{name:'isReferenceBuild',type:tr.v.d.GenericSet},LABELS:{name:'labels',type:tr.v.d.GenericSet},LOG_URLS:{name:'logUrls',type:tr.v.d.GenericSet},MASTERS:{name:'masters',type:tr.v.d.GenericSet},MEMORY_AMOUNTS:{name:'memoryAmounts',type:tr.v.d.GenericSet},MERGED_FROM:{name:'mergedFrom',type:tr.v.d.RelatedHistogramMap},MERGED_TO:{name:'mergedTo',type:tr.v.d.RelatedHistogramMap},OS_NAMES:{name:'osNames',type:tr.v.d.GenericSet},OS_VERSIONS:{name:'osVersions',type:tr.v.d.GenericSet},OWNERS:{name:'owners',type:tr.v.d.GenericSet},POINT_ID:{name:'pointId',type:tr.v.d.GenericSet},PRODUCT_VERSIONS:{name:'productVersions',type:tr.v.d.GenericSet},RELATED_NAMES:{name:'relatedNames',type:tr.v.d.GenericSet},REVISION_TIMESTAMPS:{name:'revisionTimestamps',type:tr.v.d.DateRange},SKIA_REVISIONS:{name:'skiaRevisions',type:tr.v.d.GenericSet},STORIES:{name:'stories',type:tr.v.d.GenericSet},STORYSET_REPEATS:{name:'storysetRepeats',type:tr.v.d.GenericSet},STORY_TAGS:{name:'storyTags',type:tr.v.d.GenericSet},SUMMARY_KEYS:{name:'summaryKeys',type:tr.v.d.GenericSet},TAG_MAP:{name:'tagmap',type:tr.v.d.TagMap},TEST_PATH:{name:'testPath',type:tr.v.d.GenericSet},TRACE_START:{name:'traceStart',type:tr.v.d.DateRange},TRACE_URLS:{name:'traceUrls',type:tr.v.d.GenericSet},V8_COMMIT_POSITIONS:{name:'v8CommitPositions',type:tr.v.d.DateRange},V8_REVISIONS:{name:'v8Revisions',type:tr.v.d.GenericSet},WEBRTC_REVISIONS:{name:'webrtcRevisions',type:tr.v.d.GenericSet},};const RESERVED_NAMES={};const RESERVED_NAMES_TO_TYPES=new Map();for(const[codename,info]of Object.entries(RESERVED_INFOS)){RESERVED_NAMES[codename]=info.name;if(RESERVED_NAMES_TO_TYPES.has(info.name)){throw new Error(`Duplicate reserved name "${info.name}"`);} RESERVED_NAMES_TO_TYPES.set(info.name,info.type);} const RESERVED_NAMES_SET=new Set(Object.values(RESERVED_NAMES));return{RESERVED_INFOS,RESERVED_NAMES,RESERVED_NAMES_SET,RESERVED_NAMES_TO_TYPES,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticMap extends Map{constructor(opt_allowReservedNames){super();if(opt_allowReservedNames===undefined){opt_allowReservedNames=true;} this.allowReservedNames_=opt_allowReservedNames;} @@ -7670,10 +7793,12 @@ const width=textNode.getComputedTextLength();const height=textNode.getBBox().hei function DataSeries(key){this.key_=key;this.target_=undefined;this.title_='';this.optional_=false;this.enabled_=true;this.color_=getColorOfKey(key,false);this.highlightedColor_=getColorOfKey(key,true);} DataSeries.prototype={get key(){return this.key_;},get title(){return this.title_;},set title(t){this.title_=t;},get color(){return this.color_;},set color(c){this.color_=c;},get highlightedColor(){return this.highlightedColor_;},set highlightedColor(c){this.highlightedColor_=c;},get optional(){return this.optional_;},set optional(optional){this.optional_=optional;},get enabled(){return this.enabled_;},set enabled(enabled){if(!this.optional&&!enabled){this.optional=true;} this.enabled_=enabled;},get target(){return this.target_;},set target(t){this.target_=t;}};const ChartBase=tr.ui.b.define('svg',undefined,svgNS);ChartBase.prototype={__proto__:HTMLUnknownElement.prototype,getDataSeries(key){if(!this.seriesByKey_.has(key)){this.seriesByKey_.set(key,new DataSeries(key));} -return this.seriesByKey_.get(key);},decorate(){Polymer.dom(this).classList.add('chart-base');this.setAttribute('style','cursor: default; user-select: none;');this.chartTitle_=undefined;this.seriesByKey_=new Map();this.graphWidth_=undefined;this.graphHeight_=undefined;this.margin={top:0,right:0,bottom:0,left:0,};this.hideLegend_=false;const template=Polymer.dom(THIS_DOC).querySelector('#chart-base-template');const svgEl=Polymer.dom(template.content).querySelector('svg');for(let i=0;i<Polymer.dom(svgEl).children.length;i++){Polymer.dom(this).appendChild(Polymer.dom(svgEl.children[i]).cloneNode(true));} -this.addEventListener(DataSeriesEnableChangeEventType,this.onDataSeriesEnableChange_.bind(this));},get hideLegend(){return this.hideLegend_;},set hideLegend(h){this.hideLegend_=h;this.updateContents_();},isSeriesEnabled(key){return this.getDataSeries(key).enabled;},onDataSeriesEnableChange_(event){this.getDataSeries(event.key).enabled=event.enabled;this.updateContents_();},get chartTitle(){return this.chartTitle_;},set chartTitle(chartTitle){this.chartTitle_=chartTitle;this.updateContents_();},get chartAreaElement(){return Polymer.dom(this).querySelector('#chart-area');},get graphWidth(){if(this.graphWidth_===undefined)return this.defaultGraphWidth;return this.graphWidth_;},set graphWidth(width){this.graphWidth_=width;this.updateContents_();},get defaultGraphWidth(){return 0;},get graphHeight(){if(this.graphHeight_===undefined)return this.defaultGraphHeight;return this.graphHeight_;},set graphHeight(height){this.graphHeight_=height;this.updateContents_();},get defaultGraphHeight(){return 0;},get totalWidth(){return this.margin.left+this.graphWidth+this.margin.right;},get totalHeight(){return this.margin.top+this.graphHeight+this.margin.bottom;},updateMargins_(){const legendSize=this.computeLegendSize_();this.margin.right=Math.max(this.margin.right,legendSize.width);this.margin.bottom=Math.max(this.margin.bottom,legendSize.height-this.graphHeight);if(this.chartTitle_){const titleSize=getSVGTextSize(this,this.chartTitle_,textNode=>{textNode.style.fontSize='16pt';});this.margin.top=Math.max(this.margin.top,titleSize.height+15);const horizontalOverhangPx=(titleSize.width-this.graphWidth)/2;this.margin.left=Math.max(this.margin.left,horizontalOverhangPx);this.margin.right=Math.max(this.margin.right,horizontalOverhangPx);}},computeLegendSize_(){let width=0;let height=0;if(this.hideLegend)return{width,height};for(const series of this.seriesByKey_.values()){const textSize=getSVGTextSize(this,series.key);width=Math.max(width,textSize.width+20);height+=textSize.height;} +return this.seriesByKey_.get(key);},decorate(){Polymer.dom(this).classList.add('chart-base');this.setAttribute('style','cursor: default; user-select: none;');this.chartTitle_=undefined;this.seriesByKey_=new Map();this.graphWidth_=undefined;this.graphHeight_=undefined;this.margin={top:0,right:0,bottom:0,left:0,};this.hideLegend_=false;this.showTitleInLegend_=false;this.titleHeight_='16pt';const template=Polymer.dom(THIS_DOC).querySelector('#chart-base-template');const svgEl=Polymer.dom(template.content).querySelector('svg');for(let i=0;i<Polymer.dom(svgEl).children.length;i++){Polymer.dom(this).appendChild(Polymer.dom(svgEl.children[i]).cloneNode(true));} +this.addEventListener(DataSeriesEnableChangeEventType,this.onDataSeriesEnableChange_.bind(this));},get hideLegend(){return this.hideLegend_;},set hideLegend(h){this.hideLegend_=h;this.updateContents_();},get showTitleInLegend(){return this.showTitleInLegend_;},set showTitleInLegend(s){this.showTitleInLegend_=s;this.updateContents_();},isSeriesEnabled(key){return this.getDataSeries(key).enabled;},onDataSeriesEnableChange_(event){this.getDataSeries(event.key).enabled=event.enabled;this.updateContents_();},get chartTitle(){return this.chartTitle_;},set chartTitle(chartTitle){this.chartTitle_=chartTitle;this.updateContents_();},get chartAreaElement(){return Polymer.dom(this).querySelector('#chart-area');},get graphWidth(){if(this.graphWidth_===undefined)return this.defaultGraphWidth;return this.graphWidth_;},set graphWidth(width){this.graphWidth_=width;this.updateContents_();},get defaultGraphWidth(){return 0;},get graphHeight(){if(this.graphHeight_===undefined)return this.defaultGraphHeight;return this.graphHeight_;},set graphHeight(height){this.graphHeight_=height;this.updateContents_();},get titleHeight(){return this.titleHeight_;},set titleHeight(height){this.titleHeight_=height;this.updateContents_();},get defaultGraphHeight(){return 0;},get totalWidth(){return this.margin.left+this.graphWidth+this.margin.right;},get totalHeight(){return this.margin.top+this.graphHeight+this.margin.bottom;},updateMargins_(){const legendSize=this.computeLegendSize_();this.margin.right=Math.max(this.margin.right,legendSize.width);this.margin.bottom=Math.max(this.margin.bottom,legendSize.height-this.graphHeight);if(this.chartTitle_){const titleSize=getSVGTextSize(this,this.chartTitle_,textNode=>{textNode.style.fontSize='16pt';});this.margin.top=Math.max(this.margin.top,titleSize.height+15);const horizontalOverhangPx=(titleSize.width-this.graphWidth)/2;this.margin.left=Math.max(this.margin.left,horizontalOverhangPx);this.margin.right=Math.max(this.margin.right,horizontalOverhangPx);}},computeLegendSize_(){let width=0;let height=0;if(this.hideLegend)return{width,height};let series=[...this.seriesByKey_.values()];if(this.showTitleInLegend){series=series.filter(series=>series.title!=='');} +for(const seriesEntry of series){const legendText=this.showTitleInLegend?seriesEntry.title:seriesEntry.key;const textSize=getSVGTextSize(this,legendText);width=Math.max(width,textSize.width+30);height+=textSize.height;} return{width,height};},updateDimensions_(){const thisSel=d3.select(this);thisSel.attr('width',this.totalWidth);thisSel.attr('height',this.totalHeight);d3.select(this.chartAreaElement).attr('transform','translate('+this.margin.left+', '+this.margin.top+')');},updateContents_(){this.updateMargins_();this.updateDimensions_();this.updateTitle_();this.updateLegend_();},updateTitle_(){const titleSel=d3.select(this.chartAreaElement).select('#title');if(!this.chartTitle_){titleSel.style('display','none');return;} -titleSel.attr('transform','translate('+this.graphWidth*0.5+',-15)').style('display',undefined).style('text-anchor','middle').style('font-size','16pt').attr('class','title').attr('width',this.graphWidth).text(this.chartTitle_);},updateLegend_(){const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.legend').remove();if(this.hideLegend)return;const series=[...this.seriesByKey_.values()].reverse();const legendEntriesSel=chartAreaSel.selectAll('.legend').data(series);legendEntriesSel.enter().append('foreignObject').attr('class','legend').attr('x',this.graphWidth+2).attr('width',this.margin.right).attr('height',18).attr('transform',(series,i)=>'translate(0,'+i*18+')').append('xhtml:body').style('margin',0).append('tr-ui-b-chart-legend-key').property('color',series=>((this.currentHighlightedLegendKey===series.key)?series.highlightedColor:series.color)).property('width',this.margin.right).property('target',series=>series.target).property('title',series=>series.title).property('optional',series=>series.optional).property('enabled',series=>series.enabled).text(series=>series.key);legendEntriesSel.exit().remove();},get highlightedLegendKey(){return this.highlightedLegendKey_;},set highlightedLegendKey(highlightedLegendKey){this.highlightedLegendKey_=highlightedLegendKey;this.updateHighlight_();},get currentHighlightedLegendKey(){if(this.tempHighlightedLegendKey_){return this.tempHighlightedLegendKey_;} +titleSel.attr('transform','translate('+this.graphWidth*0.5+',-15)').style('display',undefined).style('text-anchor','middle').style('font-size',this.titleHeight).attr('class','title').attr('width',this.graphWidth).text(this.chartTitle_);},updateLegend_(){const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.legend').remove();if(this.hideLegend)return;let series;let seriesText;if(this.showTitleInLegend){series=[...this.seriesByKey_.values()].filter(series=>series.title!=='').reverse();seriesText=series=>series.title;}else{series=[...this.seriesByKey_.values()].reverse();seriesText=series=>series.key;} +const legendEntriesSel=chartAreaSel.selectAll('.legend').data(series);legendEntriesSel.enter().append('foreignObject').attr('class','legend').attr('x',this.graphWidth+2).attr('width',this.margin.right).attr('height',18).attr('transform',(series,i)=>'translate(0,'+i*18+')').append('xhtml:body').style('margin',0).append('tr-ui-b-chart-legend-key').property('color',series=>((this.currentHighlightedLegendKey===series.key)?series.highlightedColor:series.color)).property('width',this.margin.right).property('target',series=>series.target).property('title',series=>series.title).property('optional',series=>series.optional).property('enabled',series=>series.enabled).text(seriesText);legendEntriesSel.exit().remove();},get highlightedLegendKey(){return this.highlightedLegendKey_;},set highlightedLegendKey(highlightedLegendKey){this.highlightedLegendKey_=highlightedLegendKey;this.updateHighlight_();},get currentHighlightedLegendKey(){if(this.tempHighlightedLegendKey_){return this.tempHighlightedLegendKey_;} return this.highlightedLegendKey_;},pushTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_){throw new Error('push cannot nest');} this.tempHighlightedLegendKey_=key;this.updateHighlight_();},popTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_!==key){throw new Error('pop cannot happen');} this.tempHighlightedLegendKey_=undefined;this.updateHighlight_();},updateHighlight_(){const chartAreaSel=d3.select(this.chartAreaElement);const legendEntriesSel=chartAreaSel.selectAll('.legend');const getDataSeries=chart.getDataSeries.bind(chart);const currentHighlightedLegendKey=chart.currentHighlightedLegendKey;legendEntriesSel.each(function(key){const dataSeries=getDataSeries(key);if(key===currentHighlightedLegendKey){this.style.fill=dataSeries.highlightedColor;this.style.fontWeight='bold';}else{this.style.fill=dataSeries.color;this.style.fontWeight='';}});}};return{ChartBase,DataSeriesEnableChangeEventType,getColorOfKey,getSVGTextSize,};});'use strict';tr.exportTo('tr.ui.b',function(){const D3_Y_AXIS_WIDTH_PX=9;const D3_X_AXIS_HEIGHT_PX=23;function sanitizePower(x,defaultValue){if(!isNaN(x)&&isFinite(x)&&(x!==0))return x;return defaultValue;} @@ -7705,21 +7830,27 @@ this.updateBrushContents_(chartAreaSel.select('#brushes'));this.updateDataConten this.data_.forEach(function(multiSeriesDatum,index){const x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')return;if(multiSeriesDatum[seriesKey]===undefined)return;if(!this.isDatumFieldSeries_(seriesKey))return;const singleSeriesDatum={x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_(clientPoint){const rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_(chartPoint){return{x:tr.b.math.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.math.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_(clientX,clientY){const chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_(mouseEvent,dataEvent){const dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseMove_(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();} const dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseUp_(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(213, 236, 229)');}}};return{ChartBase2D,};});'use strict';tr.exportTo('tr.ui.b',function(){const ChartBase2D=tr.ui.b.ChartBase2D;const ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate(){super.decorate();this.brushedRange_=new tr.b.math.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},get brushedRange(){return tr.b.math.Range.fromDict(this.brushedRange_.toJSON());},computeBrushRangeFromIndices(indexA,indexB){indexA=tr.b.math.clamp(indexA,0,this.data_.length-1);indexB=tr.b.math.clamp(indexB,0,this.data_.length-1);const leftIndex=Math.min(indexA,indexB);const rightIndex=Math.max(indexA,indexB);const brushRange=new tr.b.math.Range();brushRange.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)- this.getSampleWidth_(this.data_,leftIndex,true));brushRange.addValue(this.getXForDatum_(this.data_[rightIndex],rightIndex)+ -this.getSampleWidth_(this.data_,rightIndex,false));return brushRange;},getDataIndex_(dataX){if(this.data.length===0)return undefined;const bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined){dataEvent.data=this.data_[dataEvent.index];}},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();const brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];const brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_(brushRectsSel){brushRectsSel.attr('x',d=>this.xScale_(d.min)).attr('y',0).attr('width',d=>this.xScale_(d.max)-this.xScale_(d.min)).attr('height',this.graphHeight).attr('fill','rgb(213, 236, 229)');}};return{ChartBase2DBrushX,};});'use strict';tr.exportTo('tr.ui.b',function(){const ColumnChart=tr.ui.b.define('column-chart',tr.ui.b.ChartBase2DBrushX);ColumnChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.xCushion_=1;this.isStacked_=false;this.enableHoverBox=true;this.displayXInHover=false;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},get defaultGraphHeight(){return 100;},get defaultGraphWidth(){return 10*this.data_.length;},updateScales_(){if(this.data_.length===0)return;let xDifferences=0;let currentX=undefined;let previousX=undefined;this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}},this);this.xScale_.range([0,this.graphWidth]);const domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1){this.xCushion_=xDifferences/(this.data_.length-1);} +this.getSampleWidth_(this.data_,rightIndex,false));return brushRange;},getDataIndex_(dataX){if(this.data.length===0)return undefined;const bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined){dataEvent.data=this.data_[dataEvent.index];}},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();const brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];const brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_(brushRectsSel){brushRectsSel.attr('x',d=>this.xScale_(d.min)).attr('y',0).attr('width',d=>this.xScale_(d.max)-this.xScale_(d.min)).attr('height',this.graphHeight).attr('fill','rgb(213, 236, 229)');}};return{ChartBase2DBrushX,};});'use strict';tr.exportTo('tr.ui.b',function(){const ColumnChart=tr.ui.b.define('column-chart',tr.ui.b.ChartBase2DBrushX);ColumnChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.xCushion_=1;this.isStacked_=false;this.isGrouped_=false;this.enableHoverBox=true;this.displayXInHover=false;this.enableToolTip=false;this.toolTipCallBack_=()=>{};},set toolTipCallBack(callback){this.toolTipCallBack_=callback;},get toolTipCallBack(){return this.toolTipCallBack_;},set isGrouped(grouped){this.isGrouped_=grouped;if(grouped){this.getDataSeries('group').color='transparent';} +this.updateContents_();},get isGrouped(){return this.isGrouped_;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},get defaultGraphHeight(){return 100;},get defaultGraphWidth(){return 10*this.data_.length;},updateScales_(){if(this.data_.length===0)return;let xDifferences=0;let currentX=undefined;let previousX=undefined;this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}},this);this.xScale_.range([0,this.graphWidth]);const domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1){this.xCushion_=xDifferences/(this.data_.length-1);} this.xScale_.domain([domain[0],domain[1]+this.xCushion_]);this.yScale_.range([this.graphHeight,0]);this.yScale_.domain(this.getYScaleDomain_(this.dataRange.min,this.dataRange.max));},updateDataRange_(){if(!this.isStacked){super.updateDataRange_();return;} -this.autoDataRange_.reset();this.autoDataRange_.addValue(0);for(const datum of this.data_){let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined){continue;} +this.autoDataRange_.reset();this.autoDataRange_.addValue(0);for(const datum of this.data_){let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined){continue;}else if(this.isGrouped&&key==='group'){continue;} sum+=datum[key];} -this.autoDataRange_.addValue(sum);}},getStackedRectsForDatum_(datum,index){const stacks=[];let bottom=this.yScale_.range()[0];let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;} +this.autoDataRange_.addValue(sum);}},getStackedRectsForDatum_(datum,index){const stacks=[];let bottom=this.yScale_.range()[0];let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;}else if(this.isGrouped&&key==='group'){continue;} sum+=this.dataRange.clamp(datum[key]);const heightPx=bottom-this.yScale_(sum);bottom-=heightPx;stacks.push({key,value:datum[key],color:this.getDataSeries(key).color,heightPx,topPx:bottom,underflow:sum<this.dataRange.min,overflow:sum>this.dataRange.max,});} return stacks;},getRectsForDatum_(datum,index){if(this.isStacked){return this.getStackedRectsForDatum_(datum,index);} const stacks=[];for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;} const clampedValue=this.dataRange.clamp(datum[key]);const topPx=this.yScale_(Math.max(clampedValue,this.getYScaleMin_()));stacks.push({key,value:datum[key],topPx,heightPx:this.yScale_.range()[0]-topPx,color:this.getDataSeries(key).color,underflow:datum[key]<this.dataRange.min,overflow:datum[key]>this.dataRange.max,});} -stacks.sort(function(a,b){return b.topPx-a.topPx;});return stacks;},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;if(seriesKeys.length>1){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width+5;keyHeightPx=this.textHeightPx_;} +stacks.sort(function(a,b){return b.topPx-a.topPx;});return stacks;},drawToolTip_(rect){if(!this.enableToolTip)return;const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.tooltip').remove();const labelText='View Breakdown';const labelWidth=tr.ui.b.getSVGTextSize(this.chartAreaElement,labelText).width+5;const labelHeight=this.textHeightPx_;const toolTipLeftPx=rect.leftPx+(rect.widthPx/2);const toolTipTopPx=rect.topPx;chartAreaSel.append('rect').attr('class','tooltip').attr('fill','white').attr('opacity',0.8).attr('stroke','black').attr('x',toolTipLeftPx).attr('y',toolTipTopPx).attr('width',labelWidth+5).attr('height',labelHeight+10);chartAreaSel.append('text').style('cursor','pointer').attr('class','tooltip').on('mousedown',()=>this.toolTipCallBack_(rect)).attr('fill','blue').attr('x',toolTipLeftPx+4).attr('y',toolTipTopPx+labelHeight).attr('text-decoration','underline').text(labelText);},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;if(seriesKeys.length>1&&!this.isGrouped){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width+5;keyHeightPx=this.textHeightPx_;} let xLabelWidthPx=0;let xLabelHeightPx=0;if(this.displayXInHover){xLabelWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.x).width+5;xLabelHeightPx=this.textHeightPx_;} -let value=rect.value;if(this.unit)value=this.unit.format(value);const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,value).width+5;const valueHeightPx=this.textHeightPx_;const hoverWidthPx=Math.max(keyWidthPx,valueWidthPx,xLabelWidthPx);let hoverLeftPx=rect.leftPx+(rect.widthPx/2);hoverLeftPx=Math.max(hoverLeftPx-hoverWidthPx,-this.margin.left);const hoverHeightPx=keyHeightPx+valueHeightPx+xLabelHeightPx+2;let hoverTopPx=rect.topPx;hoverTopPx=Math.min(hoverTopPx,this.getBoundingClientRect().height-hoverHeightPx-12);chartAreaSel.append('rect').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('fill','white').attr('stroke','black').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',hoverHeightPx);if(seriesKeys.length>1){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-2).text(rect.key);} -if(this.displayXInHover){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+xLabelHeightPx-2).text(rect.datum.x);} -chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+hoverHeightPx-2).text(value);},clearHoverValueBox_(rect){const event=window.event;if(event.relatedTarget&&Array.from(event.relatedTarget.classList).includes('hover')){return;} -const rectHoverEvent=new tr.b.Event('rect-mouseleave');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);d3.select(this.chartAreaElement).selectAll('.hover').remove();},drawRect_(rect,sel){sel=sel.data([rect]);sel.enter().append('rect').attr('fill',rect.color).attr('x',rect.leftPx).attr('y',rect.topPx).attr('width',rect.widthPx).attr('height',rect.heightPx).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawUnderflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',this.graphHeight).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',0);sel.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const chartAreaSel=d3.select(this.chartAreaElement);const seriesKeys=[...this.seriesByKey_.keys()];const rectsSel=dataSel.selectAll('path');this.data_.forEach(function(datum,index){const currentX=this.getXForDatum_(datum,index);let width=undefined;if(index<(this.data_.length-1)){const nextX=this.getXForDatum_(this.data_[index+1],index+1);width=nextX-currentX;}else{width=this.xCushion_;} +let groupWidthPx=0;let groupHeightPx=0;if(this.isGrouped&&rect.datum.group!==undefined){groupWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.group).width+5;groupHeightPx=this.textHeightPx_;} +let value=rect.value;if(this.unit)value=this.unit.format(value);const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,value).width+5;const valueHeightPx=this.textHeightPx_;const hoverWidthPx=Math.max(keyWidthPx,valueWidthPx,xLabelWidthPx,groupWidthPx);let hoverLeftPx=rect.leftPx+(rect.widthPx/2);hoverLeftPx=Math.max(hoverLeftPx-hoverWidthPx,-this.margin.left);const hoverHeightPx=keyHeightPx+valueHeightPx+ +xLabelHeightPx+groupHeightPx+2;const topOffSetPx=this.isGrouped?36:12;let hoverTopPx=rect.topPx;hoverTopPx=Math.min(hoverTopPx,this.getBoundingClientRect().height- +hoverHeightPx-topOffSetPx);chartAreaSel.append('rect').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill','white').attr('stroke','black').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',hoverHeightPx);if(seriesKeys.length>1&&!this.isGrouped){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-2).text(rect.key);} +if(this.displayXInHover){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+xLabelHeightPx-2).text(rect.datum.x);} +if(this.isGrouped&&rect.datum.group!==undefined){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+ +xLabelHeightPx+groupHeightPx-2).text(rect.datum.group);} +chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+hoverHeightPx-2).text(value);},clearHoverValueBox_(rect){const event=window.event;if(event.relatedTarget&&Array.from(event.relatedTarget.classList).includes('hover')){return;} +const rectHoverEvent=new tr.b.Event('rect-mouseleave');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);d3.select(this.chartAreaElement).selectAll('.hover').remove();},drawRect_(rect,sel){sel=sel.data([rect]);sel.enter().append('rect').attr('fill',rect.color).attr('x',rect.leftPx).attr('y',rect.topPx).attr('width',rect.widthPx).attr('height',rect.heightPx).on('mousedown',this.drawToolTip_.bind(this,rect)).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawUnderflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',this.graphHeight).on('mousedown',this.drawToolTip_.bind(this,rect)).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',0);sel.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const chartAreaSel=d3.select(this.chartAreaElement);const seriesKeys=[...this.seriesByKey_.keys()];const rectsSel=dataSel.selectAll('path');this.data_.forEach(function(datum,index){const currentX=this.getXForDatum_(datum,index);let width=undefined;if(index<(this.data_.length-1)){const nextX=this.getXForDatum_(this.data_[index+1],index+1);width=nextX-currentX;}else{width=this.xCushion_;} for(const rect of this.getRectsForDatum_(datum,index)){rect.datum=datum;rect.index=index;rect.leftPx=this.xScale_(currentX);rect.rightPx=this.xScale_(currentX+width);rect.widthPx=rect.rightPx-rect.leftPx;this.drawRect_(rect,rectsSel);if(rect.underflow){this.drawUnderflow_(rect,rectsSel);} if(rect.overflow){this.drawOverflow_(rect,rectsSel);}}},this);}};return{ColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const LineChart=tr.ui.b.define('line-chart',tr.ui.b.ChartBase2DBrushX);LineChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.enableHoverBox=true;this.displayXInHover=false;},get defaultGraphWidth(){return 20*this.data_.length;},get defaultGraphHeight(){return 100;},drawHoverValueBox_(circle){tr.ui.b.ColumnChart.prototype.drawHoverValueBox_.call(this,circle);},clearHoverValueBox_(circle){tr.ui.b.ColumnChart.prototype.clearHoverValueBox_.call(this,circle);},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const dataBySeriesKey=this.getDataBySeriesKey_();const seriesKeys=[...this.seriesByKey_.keys()];const pathsSel=dataSel.selectAll('path').data(seriesKeys);pathsSel.enter().append('path').style('fill','none').style('stroke-width','1.5px').style('stroke',key=>this.getDataSeries(key).color).attr('d',key=>{const line=d3.svg.line().x(d=>this.xScale_(d.x)).y(d=>this.yScale_(this.dataRange.clamp(d[key])));return line(dataBySeriesKey[key]);});pathsSel.exit().remove();if(this.enableHoverBox){for(let index=0;index<this.data_.length;++index){const datum=this.data_[index];const x=this.getXForDatum_(datum,index);for(const[key,value]of Object.entries(datum)){if(key==='x')continue;if(value===undefined)continue;const color=this.getDataSeries(key).color;const circle=document.createElementNS('http://www.w3.org/2000/svg','circle');circle.setAttribute('cx',this.xScale_(x));circle.setAttribute('cy',this.yScale_(this.dataRange.clamp(value)));circle.setAttribute('r',5);circle.style.fill=color;circle.datum=datum;circle.key=key;circle.value=datum[key];circle.leftPx=this.xScale_(x);circle.widthPx=0;circle.color=color;circle.topPx=this.yScale_(this.dataRange.clamp(value));circle.heightPx=0;circle.addEventListener('mouseenter',()=>this.drawHoverValueBox_(circle));circle.addEventListener('mouseleave',()=>this.clearHoverValueBox_(circle));dataSel[0][0].appendChild(circle);}}}}};return{LineChart,};});'use strict';Polymer({is:'tr-ui-e-s-input-latency-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.rangeOfInterest_=new tr.b.math.Range();this.frametimeType_=tr.model.helpers.IMPL_FRAMETIME_TYPE;this.latencyChart_=undefined;this.frametimeChart_=undefined;this.selectedProcessId_=undefined;this.mouseDownIndex_=undefined;this.curMouseIndex_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;if(this.model_){this.modelHelper_=this.model_.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}else{this.modelHelper_=undefined;} this.updateToolbar_();this.updateContents_();},get frametimeType(){return this.frametimeType_;},set frametimeType(type){if(this.frametimeType_===type)return;this.frametimeType_=type;this.updateContents_();},get selectedProcessId(){return this.selectedProcessId_;},set selectedProcessId(process){if(this.selectedProcessId_===process)return;this.selectedProcessId_=process;this.updateContents_();},set selection(selection){if(this.latencyChart_===undefined)return;this.latencyChart_.brushedRange=selection.bounds;},setBrushedIndices(mouseDownIndex,curIndex){this.mouseDownIndex_=mouseDownIndex;this.curMouseIndex_=curIndex;this.updateBrushedRange_();},updateBrushedRange_(){if(this.latencyChart_===undefined)return;let r=new tr.b.math.Range();if(this.mouseDownIndex_===undefined){this.latencyChart_.brushedRange=r;return;} @@ -7734,7 +7865,7 @@ const frameEvents=chromeProcess.getFrameEventsInRange(this.frametimeType,rangeOf if(frametimeData.length!==0){this.frametimeChart_=this.createLatencyLineChart(frametimeData,'Frame Times',resultArea);}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},supportsModel(m){if(m===undefined){return{supported:false,reason:'Unknown tracing model'};} if(!tr.model.helpers.ChromeModelHelper.supportsModel(m)){return{supported:false,reason:'No Chrome browser or renderer process found'};} const modelHelper=m.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper.browserHelper&&modelHelper.browserHelper.hasLatencyEvents){return{supported:true};} -return{supported:false,reason:'No InputLatency events trace. Consider enabling '+'benchmark" and "input" category when recording the trace'};},get textLabel(){return'Input Latency';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-input-latency-side-panel');});'use strict';tr.exportTo('tr.e.system_stats',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function SystemStatsSnapshot(objectInstance,ts,args){ObjectSnapshot.apply(this,arguments);this.objectInstance=objectInstance;this.ts=ts;this.args=args;this.stats=args;} +return{supported:false,reason:'No InputLatency events trace. Consider enabling '+'benchmark" and "input" category when recording the trace'};},get textLabel(){return'Input Latency';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-input-latency-side-panel');});'use strict';tr.exportTo('tr.e.system_stats',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function SystemStatsSnapshot(objectInstance,ts,args){ObjectSnapshot.apply(this,arguments);this.objectInstance=objectInstance;this.ts=ts;this.args=args;this.stats_=args;} SystemStatsSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize(){if(this.args.length===0){throw new Error('No system stats snapshot data.');} this.stats_=this.args;},getStats(){return this.stats_;},setStats(stats){this.stats_=stats;}};ObjectSnapshot.subTypes.register(SystemStatsSnapshot,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){const constants={HEADING_WIDTH:250};return{constants,};});'use strict';Polymer({is:'tr-ui-b-heading',DOWN_ARROW:String.fromCharCode(0x25BE),RIGHT_ARROW:String.fromCharCode(0x25B8),ready(viewport){this.style.width=(tr.ui.b.constants.HEADING_WIDTH-6)+'px';this.heading_='';this.expanded_=true;this.arrowVisible_=false;this.selectionGenerator_=undefined;this.updateContents_();},get heading(){return this.heading_;},set heading(text){if(this.heading_===text)return;this.heading_=text;this.updateContents_();},set arrowVisible(val){if(this.arrowVisible_===val)return;this.arrowVisible_=!!val;this.updateContents_();},set tooltip(text){this.$.heading.title=text;},set selectionGenerator(generator){if(this.selectionGenerator_===generator)return;this.selectionGenerator_=generator;this.updateContents_();},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=!!expanded;this.updateContents_();},onHeadingDivClicked_(){this.dispatchEvent(new tr.b.Event('heading-clicked',true));},updateContents_(){if(this.arrowVisible_){this.$.arrow.style.display='';}else{this.$.arrow.style.display='none';this.$.heading.style.display=this.expanded_?'':'none';} if(this.arrowVisible_){Polymer.dom(this.$.arrow).textContent=this.expanded_?this.DOWN_ARROW:this.RIGHT_ARROW;} @@ -7754,7 +7885,7 @@ const snapshots=this.objectInstance_.snapshots;const maxBounds=this.objectInstan return snapshots[i].ts-snapshots[i-1].ts;} return snapshots[i+1].ts-snapshots[i].ts;},loWX,hiWX,onSnapshot);},addEventNearToProvidedEventToSelection(event,offset,selection){if(!(event instanceof tr.model.ObjectSnapshot)){throw new Error('Unrecognized event');} const objectSnapshots=this.objectInstance_.snapshots;const index=objectSnapshots.indexOf(event);const newIndex=index+offset;if(newIndex>=0&&newIndex<objectSnapshots.length){selection.push(objectSnapshots[newIndex]);return true;} -return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const snapshot=tr.b.findClosestElementInSortedArray(this.objectInstance_.snapshots,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};return{StackedBarsTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){const EventPresenter=tr.ui.b.EventPresenter;let statCount;const excludedStats={'meminfo':{'pswpin':0,'pswpout':0,'pgmajfault':0},'diskinfo':{'io':0,'io_time':0,'read_time':0,'reads':0,'reads_merged':0,'sectors_read':0,'sectors_written':0,'weighted_io_time':0,'write_time':0,'writes':0,'writes_merged':0},'swapinfo':{}};const SystemStatsInstanceTrack=tr.ui.b.define('tr-ui-e-system-stats-instance-track',tr.ui.tracks.StackedBarsTrack);SystemStatsInstanceTrack.prototype={__proto__:tr.ui.tracks.StackedBarsTrack.prototype,decorate(viewport){tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('tr-ui-e-system-stats-instance-track');this.objectInstance_=null;},set objectInstances(objectInstances){if(!objectInstances){this.objectInstance_=[];return;} +return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const snapshot=tr.b.findClosestElementInSortedArray(this.objectInstance_.snapshots,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};return{StackedBarsTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){const EventPresenter=tr.ui.b.EventPresenter;let statCount;const excludedStats={'meminfo':{'pswpin':0,'pswpout':0,'pgmajfault':0},'diskinfo':{'io':0,'io_time':0,'read_time':0,'reads':0,'reads_merged':0,'sectors_read':0,'sectors_written':0,'weighted_io_time':0,'write_time':0,'writes':0,'writes_merged':0},'swapinfo':{},'perfinfo':{'idle_time':0,'read_transfer_count':0,'write_transfer_count':0,'other_transfer_count':0,'read_operation_count':0,'write_operation_count':0,'other_operation_count':0,'pagefile_pages_written':0,'pagefile_pages_write_ios':0,'available_pages':0,'pages_read':0,'page_read_ios':0}};const SystemStatsInstanceTrack=tr.ui.b.define('tr-ui-e-system-stats-instance-track',tr.ui.tracks.StackedBarsTrack);const kPageSizeWindows=4096;SystemStatsInstanceTrack.prototype={__proto__:tr.ui.tracks.StackedBarsTrack.prototype,decorate(viewport){tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('tr-ui-e-system-stats-instance-track');this.objectInstance_=null;},set objectInstances(objectInstances){if(!objectInstances){this.objectInstance_=[];return;} if(objectInstances.length!==1){throw new Error('Bad object instance count.');} this.objectInstance_=objectInstances[0];if(this.objectInstance_!==null){this.computeRates_(this.objectInstance_.snapshots);this.maxStats_=this.computeMaxStats_(this.objectInstance_.snapshots);}},computeRates_(snapshots){for(let i=0;i<snapshots.length;i++){const snapshot=snapshots[i];const stats=snapshot.getStats();let prevSnapshot;if(i===0){prevSnapshot=snapshots[0];}else{prevSnapshot=snapshots[i-1];} const prevStats=prevSnapshot.getStats();let timeIntervalSeconds=(snapshot.ts-prevSnapshot.ts)/1000;if(timeIntervalSeconds===0){timeIntervalSeconds=1;} @@ -7767,7 +7898,27 @@ prevStats.pgmajfault)/timeIntervalSeconds;} if(statName==='pswpin'){stats.bytes_swpin_per_sec=(stats.pswpin- prevStats.pswpin)*1000/timeIntervalSeconds;} if(statName==='pswpout'){stats.bytes_swpout_per_sec=(stats.pswpout- -prevStats.pswpout)*1000/timeIntervalSeconds;}}}},computeMaxStats_(snapshots){const maxStats={};statCount=0;for(let i=0;i<snapshots.length;i++){const snapshot=snapshots[i];const stats=snapshot.getStats();this.computeMaxStatsRecursive_(stats,maxStats,excludedStats);} +prevStats.pswpout)*1000/timeIntervalSeconds;} +if(statName==='idle_time'){const units=tr.b.convertUnit(100.,tr.b.UnitScale.TIME.NANO_SEC,tr.b.UnitScale.TIME.SEC);const idleTile=(stats.idle_time-prevStats.idle_time)*units;stats.idle_time_per_sec=idleTile/timeIntervalSeconds;} +if(statName==='read_transfer_count'){const bytesRead=stats.read_transfer_count- +prevStats.read_transfer_count;stats.bytes_read_per_sec=bytesRead/timeIntervalSeconds;} +if(statName==='write_transfer_count'){const bytesWritten=stats.write_transfer_count- +prevStats.write_transfer_count;stats.bytes_written_per_sec=bytesWritten/timeIntervalSeconds;} +if(statName==='other_transfer_count'){const bytesTransfer=stats.other_transfer_count- +prevStats.other_transfer_count;stats.bytes_other_per_sec=bytesTransfer/timeIntervalSeconds;} +if(statName==='read_operation_count'){const readOperation=stats.read_operation_count- +prevStats.read_operation_count;stats.read_operation_per_sec=readOperation/timeIntervalSeconds;} +if(statName==='write_operation_count'){const writeOperation=stats.write_operation_count- +prevStats.write_operation_count;stats.write_operation_per_sec=writeOperation/timeIntervalSeconds;} +if(statName==='other_operation_count'){const otherOperation=stats.other_operation_count- +prevStats.other_operation_count;stats.other_operation_per_sec=otherOperation/timeIntervalSeconds;} +if(statName==='pagefile_pages_written'){const pageFileBytesWritten=(stats.pagefile_pages_written- +prevStats.pagefile_pages_written)*kPageSizeWindows;stats.pagefile_bytes_written_per_sec=pageFileBytesWritten/timeIntervalSeconds;} +if(statName==='pagefile_pages_write_ios'){const pagefileWriteOperation=stats.pagefile_pages_write_ios- +prevStats.pagefile_pages_write_ios;stats.pagefile_write_operation_per_sec=pagefileWriteOperation/timeIntervalSeconds;} +if(statName==='available_pages'){stats.available_pages_in_bytes=stats.available_pages*kPageSizeWindows;} +if(statName==='pages_read'){const pagesBytesRead=(stats.pages_read-prevStats.pages_read)*kPageSizeWindows;stats.bytes_read_per_sec=pagesBytesRead/timeIntervalSeconds;} +if(statName==='page_read_ios'){const pagesBytesReadOperations=stats.page_read_ios-prevStats.page_read_ios;stats.pagefile_write_operation_per_sec=pagesBytesReadOperations/timeIntervalSeconds;}}}},computeMaxStats_(snapshots){const maxStats={};statCount=0;for(let i=0;i<snapshots.length;i++){const snapshot=snapshots[i];const stats=snapshot.getStats();this.computeMaxStatsRecursive_(stats,maxStats,excludedStats);} return maxStats;},computeMaxStatsRecursive_(stats,maxStats,excludedStats){for(const statName in stats){if(stats[statName]instanceof Object){if(!(statName in maxStats)){maxStats[statName]={};} let excludedNested;if(excludedStats&&statName in excludedStats){excludedNested=excludedStats[statName];}else{excludedNested=null;} this.computeMaxStatsRecursive_(stats[statName],maxStats[statName],excludedNested);}else{if(excludedStats&&statName in excludedStats){continue;} @@ -7843,7 +7994,7 @@ const e=objects[instanceType];for(const name of Object.getOwnPropertyNames(INSTA this.updateTable_();if(slices.length>1){this.buildOptions_();this.constructDiffTable_();}},updateTable_(){this.constructTable_();this.$.table.tableRows=this.isolateEntries_;this.$.table.rebuild();},});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.gcObjectsStats.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',tr.e.v8.V8GCStatsThreadSlice,{multi:true,title:'V8 GC Stats slices'});'use strict';tr.exportTo('tr.e.v8',function(){const IC_STATS_PROPERTIES=['type','category','scriptName','filePosition','state','isNative','map','propertiesMode','numberOfOwnProperties','instanceType'];class ICStatsEntry{constructor(obj){this.type_=obj.type;if(this.type_.includes('Store')){this.category_='Store';}else if(this.type_.includes('Load')){this.category_='Load';} this.state_=obj.state;if(obj.functionName){this.functionName_=obj.optimized?'*':'~';this.functionName_+=obj.functionName.length===0?'(anonymous function)':obj.functionName;} this.offset_=obj.offset;this.scriptName_=obj.scriptName?obj.scriptName:'unknown';this.isNative_=obj.scriptName&&obj.scriptName.includes('native');this.lineNum_=obj.lineNum?obj.lineNum:'unknown';this.filePosition_=this.scriptName_+':'+this.lineNum_;if(this.functionName_){this.filePosition_+=' '+this.functionName_+'+'+this.offset_;} -this.constructor_=obj.constructor?false:true;this.map_=obj.map;if(this.map_){this.propertiesMode_=obj.dict===0?'slow':'fast';}else{this.propertiesMode_='unknown';} +this.constructor_=obj.constructor?false:true;this.map_=obj.map;if(this.map_){this.propertiesMode_=obj.dict===1?'slow':'fast';}else{this.propertiesMode_='unknown';} this.numberOfOwnProperties_=obj.own;this.instanceType_=obj.instanceType;this.key_=obj.key;} get type(){return this.type_;} get category(){return this.category_;} @@ -7890,7 +8041,7 @@ match(name){return this.regex_&&name.match(this.regex_);} add(entry){const value=this.entries_.get(entry.name);if(value!==undefined){value.addSample(entry.count,entry.time);}else{this.entries_.set(entry.name,entry);} this.count_+=entry.count;this.time_+=entry.time;} get values(){return Array.from(this.entries_.values());}} -class RuntimeStatsGroupCollection{constructor(){this.blink_cpp_group_=new RuntimeStatsGroup('Blink C++',/.*Callback.*/);this.api_group_=new RuntimeStatsGroup('API',/.*API.*/);this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),this.blink_cpp_group_,this.api_group_,new RuntimeStatsGroup('GC-Background-Marking',/.*GC.MC.BACKGROUND.*MARKING.*/),new RuntimeStatsGroup('GC-Background-Sweeping',/.*GC.MC.BACKGROUND.*SWEEPING.*/),new RuntimeStatsGroup('GC-Background-Scavenger',/.*GC.SCAVENGER.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MinorMC',/.*GC.MINOR_MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MajorMC',/.*GC.MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-Other',/.*GC.*BACKGROUND.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];this.blink_group_collection_=null;} +class RuntimeStatsGroupCollection{constructor(){this.blink_cpp_group_=new RuntimeStatsGroup('Blink C++',/.*Callback.*/);this.api_group_=new RuntimeStatsGroup('API',/.*API.*/);this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize-Background',/(.*OptimizeConcurrent.*)|RecompileConcurrent.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),this.blink_cpp_group_,this.api_group_,new RuntimeStatsGroup('GC-Background-Marking',/.*GC.MC.BACKGROUND.*MARKING.*/),new RuntimeStatsGroup('GC-Background-Sweeping',/.*GC.MC.BACKGROUND.*SWEEPING.*/),new RuntimeStatsGroup('GC-Background-Scavenger',/.*GC.SCAVENGER.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MinorMC',/.*GC.MINOR_MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MajorMC',/.*GC.MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-Other',/.*GC.*BACKGROUND.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];this.blink_group_collection_=null;} addSlices(slices){const blinkEntries=[];for(const slice of slices){if(!(slice instanceof tr.e.v8.V8ThreadSlice))return;let runtimeCallStats;try{runtimeCallStats=JSON.parse(slice.runtimeCallStats);}catch(e){runtimeCallStats=slice.runtimeCallStats;} if(runtimeCallStats===undefined)continue;for(const[name,stat]of Object.entries(runtimeCallStats)){if(name.match(/Blink_.*/)){if(name==='Blink_V8')continue;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);blinkEntries.push(entry);continue;} for(let i=1;i<this.groups_.length;++i){if(this.groups_[i].match(name)){if(stat.length!==2)break;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);this.groups_[0].addSample(stat[0],stat[1]);this.groups_[i].add(entry);break;}}}} @@ -7934,12 +8085,14 @@ MetricRegistry.checkFilename(metric.name);});return{MetricRegistry,};});'use str if(slice.title==='RenderAccessibilityImpl::SendLocationChanges'){renderAccessibilityLocationsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}} for(const browserHelper of Object.values(chromeHelper.browserHelpers)){const mainThread=browserHelper.mainThread;if(mainThread===undefined)continue;for(const slice of mainThread.getDescendantEvents()){if(slice.title==='BrowserAccessibilityManager::OnAccessibilityEvents'){browserAccessibilityEventsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}} histograms.addHistogram(browserAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityLocationsHist);} -tr.metrics.MetricRegistry.register(accessibilityMetric);return{accessibilityMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MESSAGE_LOOP_EVENT_NAME='Startup.BrowserMessageLoopStartTimeFromMainEntry3';const NAVIGATION_TIME_TO_NETWORK_STACK_EVENT_NAME='Navigation timeToNetworkStack';const FIRST_CONTENTFUL_PAINT_EVENT_NAME='firstContentfulPaint';function androidStartupMetric(histograms,model){const browserStartTimestamps=[];const requestStartTimestampsAndEvents=[];const firstContentfulPaintTimestampsAndEvents=[];const messageLoopStartHistogram=histograms.createHistogram('messageloop_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const requestStartHistogram=histograms.createHistogram('request_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;for(const helper of chromeHelper.browserHelpers){for(const ev of helper.mainThread.asyncSliceGroup.childEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartHistogram.addSample(ev.duration,{events:new tr.v.d.RelatedEventSet([ev])});browserStartTimestamps.push(ev.start);} -if(ev.title===NAVIGATION_TIME_TO_NETWORK_STACK_EVENT_NAME){requestStartTimestampsAndEvents.push({timestamp:ev.end,event:new tr.v.d.RelatedEventSet([ev])});}}} -const firstContentfulPaintHistogram=histograms.createHistogram('first_contentful_paint_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const rendererHelpers=chromeHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintTimestampsAndEvents.push({timestamp:ev.end,event:new tr.v.d.RelatedEventSet([ev])});break;}}} -const totalRequestStarts=requestStartTimestampsAndEvents.length;const totalBrowserStarts=browserStartTimestamps.length;if(totalRequestStarts!==totalBrowserStarts){throw new Error('Number of request starts ('+totalRequestStarts+') differs from number of browser starts ('+totalBrowserStarts+')');} -const totalFcpTimestamps=firstContentfulPaintTimestampsAndEvents.length;if(totalFcpTimestamps!==totalBrowserStarts){throw new Error('Number of FCP events ('+totalFcpTimestamps+') differs from number of browser starts ('+totalBrowserStarts+')');} -for(let i=0;i<totalBrowserStarts;i++){const browserStart=browserStartTimestamps[i];const requestStartInfo=requestStartTimestampsAndEvents[i];requestStartHistogram.addSample(requestStartInfo.timestamp-browserStart,{events:requestStartInfo.event});const fcpInfo=firstContentfulPaintTimestampsAndEvents[i];firstContentfulPaintHistogram.addSample(fcpInfo.timestamp-browserStart,{events:fcpInfo.event});}} +tr.metrics.MetricRegistry.register(accessibilityMetric);return{accessibilityMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MESSAGE_LOOP_EVENT_NAME='Startup.BrowserMessageLoopStartTimeFromMainEntry3';const FIRST_CONTENTFUL_PAINT_EVENT_NAME='firstContentfulPaint';function androidStartupMetric(histograms,model){let messageLoopStartEvents=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;for(const helper of chromeHelper.browserHelpers){for(const ev of helper.mainThread.asyncSliceGroup.childEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}}} +let firstContentfulPaintEvents=[];const rendererHelpers=chromeHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(!rendererHelper.mainThread)continue;for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}}} +let totalBrowserStarts=messageLoopStartEvents.length;let totalFcpEvents=firstContentfulPaintEvents.length;if(totalFcpEvents!==totalBrowserStarts||totalBrowserStarts===0){messageLoopStartEvents=[];firstContentfulPaintEvents=[];for(const proc of Object.values(model.processes)){for(const ev of proc.getDescendantEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}} +for(const ev of proc.getDescendantEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}}} +totalBrowserStarts=messageLoopStartEvents.length;totalFcpEvents=firstContentfulPaintEvents.length;} +if(totalFcpEvents!==totalBrowserStarts){throw new Error('Number of FCP events ('+totalFcpEvents+') differs from number of browser starts ('+totalBrowserStarts+')');} +const messageLoopStartHistogram=histograms.createHistogram('messageloop_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const firstContentfulPaintHistogram=histograms.createHistogram('first_contentful_paint_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);function orderEvents(event1,event2){return event1.start-event2.start;} +messageLoopStartEvents.sort(orderEvents);firstContentfulPaintEvents.sort(orderEvents);for(let i=2;i<totalBrowserStarts;i++){const startEvent=messageLoopStartEvents[i];messageLoopStartHistogram.addSample(startEvent.duration,{events:new tr.v.d.RelatedEventSet([startEvent])});const fcpEvent=firstContentfulPaintEvents[i];firstContentfulPaintHistogram.addSample(fcpEvent.end-startEvent.start,{events:new tr.v.d.RelatedEventSet([startEvent,fcpEvent])});}} tr.metrics.MetricRegistry.register(androidStartupMetric);return{androidStartupMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MAX_INPUT_EVENT_TO_STARTUP_DELAY_IN_MS=2000;const MIN_DRAW_DELAY_IN_MS=80;const MAX_DRAW_DELAY_IN_MS=2000;function findProcess(processName,model){for(const pid in model.processes){const process=model.processes[pid];if(process.name===processName){return process;}} return undefined;} function findThreads(process,threadPrefix){if(process===undefined)return undefined;const threads=[];for(const tid in process.threads){const thread=process.threads[tid];if(thread.name.startsWith(threadPrefix)){threads.push(thread);}} @@ -7966,7 +8119,7 @@ if(this.pieces.length>0&&this.pieces[this.pieces.length-1].x2>x1){throw new Erro if(x1<x2){this.pieces.push(new Piece(x1,y1,x2,y2));}},partBelow(y){return this.pieces.reduce((acc,p)=>(acc+p.partBelow(y)),0);},get min(){return this.pieces.reduce((acc,p)=>Math.min(acc,p.min),Infinity);},get max(){return this.pieces.reduce((acc,p)=>Math.max(acc,p.max),-Infinity);},get average(){let weightedSum=0;let totalWeight=0;this.pieces.forEach(function(piece){weightedSum+=piece.width*piece.average;totalWeight+=piece.width;});if(totalWeight===0)return 0;return weightedSum/totalWeight;},percentile(percent){if(!(percent>=0&&percent<=1)){throw new Error('percent must be [0,1]');} let lower=this.min;let upper=this.max;const total=this.partBelow(upper);if(total===0)return 0;while(upper-lower>PERCENTILE_PRECISION){const middle=(lower+upper)/2;const below=this.partBelow(middle);if(below/total<percent){lower=middle;}else{upper=middle;}} return(lower+upper)/2;}};function Piece(x1,y1,x2,y2){this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;} -Piece.prototype={partBelow(y){const width=this.width;if(width===0)return 0;const minY=this.min;const maxY=this.max;if(y>=maxY)return width;if(y<minY)return 0;return(y-minY)/(maxY-minY)*width;},get min(){return Math.min(this.y1,this.y2);},get max(){return Math.max(this.y1,this.y2);},get average(){return(this.y1+this.y2)/2;},get width(){return this.x2-this.x1;}};return{PiecewiseLinearFunction,};});'use strict';tr.exportTo('tr.metrics.v8.utils',function(){const IDLE_TASK_EVENT='SingleThreadIdleTaskRunner::RunTask';const V8_EXECUTE='V8.Execute';const GC_EVENT_PREFIX='V8.GC';const FULL_GC_EVENT='V8.GCCompactor';const LOW_MEMORY_EVENT='V8.GCLowMemoryNotification';const MAJOR_GC_EVENT='MajorGC';const MINOR_GC_EVENT='MinorGC';const TOP_GC_EVENTS={'V8.GCCompactor':'v8-gc-full-mark-compactor','V8.GCFinalizeMC':'v8-gc-latency-mark-compactor','V8.GCFinalizeMCReduceMemory':'v8-gc-memory-mark-compactor','V8.GCIncrementalMarking':'v8-gc-incremental-step','V8.GCIncrementalMarkingFinalize':'v8-gc-incremental-finalize','V8.GCIncrementalMarkingStart':'v8-gc-incremental-start','V8.GCPhantomHandleProcessingCallback':'v8-gc-phantom-handle-callback','V8.GCScavenger':'v8-gc-scavenger'};const LOW_MEMORY_MARK_COMPACTOR='v8-gc-low-memory-mark-compactor';function findParent(event,predicate){let parent=event.parentSlice;while(parent){if(predicate(parent)){return parent;} +Piece.prototype={partBelow(y){const width=this.width;if(width===0)return 0;const minY=this.min;const maxY=this.max;if(y>=maxY)return width;if(y<minY)return 0;return(y-minY)/(maxY-minY)*width;},get min(){return Math.min(this.y1,this.y2);},get max(){return Math.max(this.y1,this.y2);},get average(){return(this.y1+this.y2)/2;},get width(){return this.x2-this.x1;}};return{PiecewiseLinearFunction,};});'use strict';tr.exportTo('tr.metrics.v8.utils',function(){const IDLE_TASK_EVENT='SingleThreadIdleTaskRunner::RunTask';const V8_EXECUTE='V8.Execute';const GC_EVENT_PREFIX='V8.GC';const FULL_GC_EVENT='V8.GCCompactor';const LOW_MEMORY_EVENT='V8.GCLowMemoryNotification';const MAJOR_GC_EVENT='MajorGC';const MINOR_GC_EVENT='MinorGC';const TOP_GC_EVENTS={'V8.GCCompactor':'v8-gc-full-mark-compactor','V8.GCFinalizeMC':'v8-gc-latency-mark-compactor','V8.GCFinalizeMCReduceMemory':'v8-gc-memory-mark-compactor','V8.GCIncrementalMarking':'v8-gc-incremental-step','V8.GCIncrementalMarkingFinalize':'v8-gc-incremental-finalize','V8.GCIncrementalMarkingStart':'v8-gc-incremental-start','V8.GCPhantomHandleProcessingCallback':'v8-gc-phantom-handle-callback','V8.GCScavenger':'v8-gc-scavenger'};const MARK_COMPACTOR_EVENTS=new Set(['V8.GCCompactor','V8.GCFinalizeMC','V8.GCFinalizeMCReduceMemory','V8.GCIncrementalMarking','V8.GCIncrementalMarkingFinalize','V8.GCIncrementalMarkingStart','V8.GCPhantomHandleProcessingCallback']);const LOW_MEMORY_MARK_COMPACTOR='v8-gc-low-memory-mark-compactor';function findParent(event,predicate){let parent=event.parentSlice;while(parent){if(predicate(parent)){return parent;} parent=parent.parentSlice;} return null;} function isIdleTask(event){return event.title===IDLE_TASK_EVENT;} @@ -7978,7 +8131,11 @@ function isGarbageCollectionEvent(event){return event.title&&event.title.startsW function isTopGarbageCollectionEvent(event){return event.title in TOP_GC_EVENTS;} function isForcedGarbageCollectionEvent(event){return findParent(event,isLowMemoryEvent)!==null;} function isSubGarbageCollectionEvent(event){return isGarbageCollectionEvent(event)&&event.parentSlice&&(isTopGarbageCollectionEvent(event.parentSlice)||event.parentSlice.title===MAJOR_GC_EVENT||event.parentSlice.title===MINOR_GC_EVENT);} +function isNotForcedTopGarbageCollectionEvent(event){return tr.metrics.v8.utils.isTopGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);} +function isNotForcedSubGarbageCollectionEvent(event){return tr.metrics.v8.utils.isSubGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);} function isFullMarkCompactorEvent(event){return event.title==='V8.GCCompactor';} +function isMarkCompactorSummaryEvent(event){return event.title==='V8.GCMarkCompactorSummary';} +function isMarkCompactorMarkingSummaryEvent(event){return event.title==='V8.GCMarkCompactorMarkingSummary';} function isIncrementalMarkingEvent(event){return event.title.startsWith('V8.GCIncrementalMarking');} function isLatencyMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMC';} function isMemoryMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMCReduceMemory';} @@ -7988,6 +8145,9 @@ function isCompileUnoptimizeRCSCategory(name){return name==='Compile';} function isCompileParseRCSCategory(name){return name==='Parse';} function isCompileRCSCategory(name){return name==='Compile'||name==='Optimize'||name==='Parse';} function isV8RCSEvent(event){return event instanceof tr.e.v8.V8ThreadSlice;} +function isMarkCompactorEvent(event){return MARK_COMPACTOR_EVENTS.has(event.title);} +function isNotForcedMarkCompactorEvent(event){return!isForcedGarbageCollectionEvent(event)&&isMarkCompactorEvent(event);} +function forcedGCEventName(){return LOW_MEMORY_EVENT;} function topGarbageCollectionEventName(event){if(event.title===FULL_GC_EVENT){if(findParent(event,isLowMemoryEvent)){return LOW_MEMORY_MARK_COMPACTOR;}} return TOP_GC_EVENTS[event.title];} function subGarbageCollectionEventName(event){const topEvent=findParent(event,isTopGarbageCollectionEvent);const prefix=topEvent?topGarbageCollectionEventName(topEvent):'unknown';const name=event.title.replace('V8.GC_MC_','').replace('V8.GC_SCAVENGER_','').replace('V8.GC_','').replace(/_/g,'-').toLowerCase();return prefix+'-'+name;} @@ -7996,19 +8156,39 @@ for(const[name,events]of Object.entries(nameToEvents)){processCallback(name,even function unionOfIntervals(intervals){if(intervals.length===0)return[];return tr.b.math.mergeRanges(intervals.map(x=>{return{min:x.start,max:x.end};}),1e-6,function(ranges){return{start:ranges.reduce((acc,x)=>Math.min(acc,x.min),ranges[0].min),end:ranges.reduce((acc,x)=>Math.max(acc,x.max),ranges[0].max)};});} function hasV8Stats(globalMemoryDump){let v8stats=undefined;globalMemoryDump.iterateContainerDumps(function(dump){v8stats=v8stats||dump.getMemoryAllocatorDumpByFullName('v8');});return!!v8stats;} function rangeForMemoryDumps(model){const startOfFirstDumpWithV8=model.globalMemoryDumps.filter(hasV8Stats).reduce((start,dump)=>Math.min(start,dump.start),Infinity);if(startOfFirstDumpWithV8===Infinity)return new tr.b.math.Range();return tr.b.math.Range.fromExplicitRange(startOfFirstDumpWithV8,Infinity);} -return{findParent,groupAndProcessEvents,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMemoryMarkCompactorEvent,isScavengerEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,isV8RCSEvent,isCompileRCSCategory,isCompileOptimizeRCSCategory,isCompileUnoptimizeRCSCategory,isCompileParseRCSCategory,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){const BLINK_GC_EVENTS={'BlinkGC.AtomicPhaseMarking':'blink-gc-marking','BlinkGC.CompleteSweep':'blink-gc-complete-sweep','BlinkGC.LazySweepInIdle':'blink-gc-idle-lazy-sweep'};function isBlinkGarbageCollectionEvent(event){return event.title in BLINK_GC_EVENTS;} -function blinkGarbageCollectionEventName(event){return BLINK_GC_EVENTS[event.title];} -function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addIdleTimesOfTopEvents(histograms,model);addTotalIdleTimesOfTopEvents(histograms,model);} +class WindowEndpoint{constructor(start,points){this.points=points;this.lastIndex=-1;this.position=start;this.distanceUntilNextPoint=points[0].position-start;this.cummulativePause=0;this.stackDepth=0;} +advance(delta){if(delta<this.distanceUntilNextPoint){this.position+=delta;this.cummulativePause+=this.stackDepth>0?delta:0;this.distanceUntilNextPoint=this.points[this.lastIndex+1].position-this.position;}else{this.position+=this.distanceUntilNextPoint;this.cummulativePause+=this.stackDepth>0?this.distanceUntilNextPoint:0;this.distanceUntilNextPoint=0;this.lastIndex++;if(this.lastIndex<this.points.length){this.stackDepth+=this.points[this.lastIndex].delta;if(this.lastIndex+1<this.points.length){this.distanceUntilNextPoint=this.points[this.lastIndex+1].position-this.position;}}}}} +function mutatorUtilization(start,end,timeWindow,intervals){const mu=new tr.b.math.PiecewiseLinearFunction();if(end-start<=timeWindow){return mu;} +if(intervals.length===0){mu.push(start,1.0,end-timeWindow,1.0);return mu;} +intervals=unionOfIntervals(intervals);const points=[];for(const interval of intervals){points.push({position:interval.start,delta:1});points.push({position:interval.end,delta:-1});} +points.sort((a,b)=>a.position-b.position);points.push({position:end,delta:0});const left=new WindowEndpoint(start,points);const right=new WindowEndpoint(start,points);while(right.position-left.position<timeWindow){right.advance(timeWindow-(right.position-left.position));} +while(right.lastIndex<points.length){const distanceUntilNextPoint=Math.min(left.distanceUntilNextPoint,right.distanceUntilNextPoint);const position1=left.position;const value1=right.cummulativePause-left.cummulativePause;left.advance(distanceUntilNextPoint);right.advance(distanceUntilNextPoint);if(distanceUntilNextPoint>0){const position2=left.position;const value2=right.cummulativePause-left.cummulativePause;mu.push(position1,1.0-value1/timeWindow,position2,1.0-value2/timeWindow);}} +return mu;} +function addMutatorUtilization(metricName,eventFilter,timeWindows,rendererHelpers,histograms){const histogramMap=new Map();for(const timeWindow of timeWindows){const histogramOptions={avg:false,count:false,max:false,min:true,std:false,sum:false};const histogram=histograms.createHistogram(`${metricName}-${timeWindow}ms_window`,tr.b.Unit.byName.normalizedPercentage_biggerIsBetter,[],histogramOptions);histogramMap.set(timeWindow,histogram);} +for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;const pauses=[];for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){if(eventFilter(event)&&event.end>event.start){pauses.push({start:event.start,end:event.end});}} +pauses.sort((a,b)=>a.start-b.start);const start=rendererHelper.mainThread.bounds.min;const end=rendererHelper.mainThread.bounds.max;for(const timeWindow of timeWindows){const mu=mutatorUtilization(start,end,timeWindow,pauses);histogramMap.get(timeWindow).addSample(mu.min);}}} +return{addMutatorUtilization,findParent,forcedGCEventName,groupAndProcessEvents,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMarkCompactorSummaryEvent,isMarkCompactorMarkingSummaryEvent,isMemoryMarkCompactorEvent,isNotForcedMarkCompactorEvent,isNotForcedTopGarbageCollectionEvent,isNotForcedSubGarbageCollectionEvent,isScavengerEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,isV8RCSEvent,isCompileRCSCategory,isCompileOptimizeRCSCategory,isCompileUnoptimizeRCSCategory,isCompileParseRCSCategory,mutatorUtilization,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){const BLINK_TOP_GC_EVENTS={'BlinkGC.AtomicPhase':'blink-gc-atomic-phase','BlinkGC.CompleteSweep':'blink-gc-complete-sweep','BlinkGC.IncrementalMarkingStartMarking':'blink-gc-incremental-start','BlinkGC.IncrementalMarkingStep':'blink-gc-incremental-step','BlinkGC.LazySweepInIdle':'blink-gc-lazy-sweep-idle','BlinkGC.LazySweepOnAllocation':'blink-gc-lazy-sweep-allocation'};function blinkGarbageCollectionEventName(event){return BLINK_TOP_GC_EVENTS[event.title];} +function isNonForcedBlinkGarbageCollectionEvent(event){return event.title in BLINK_TOP_GC_EVENTS&&(!event.args||!event.args.forced)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);} +function isNonNestedNonForcedBlinkGarbageCollectionEvent(event){return isNonForcedBlinkGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isGarbageCollectionEvent);} +function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addIdleTimesOfTopEvents(histograms,model);addTotalIdleTimesOfTopEvents(histograms,model);addTotalDurationOfBlinkAndV8TopEvents(histograms,model);} tr.metrics.MetricRegistry.register(blinkGcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;} +function createNumericForTotalEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:false,min:false,std:false,sum:true,percentile:[0.90]});return n;} +function createNumericForUnifiedEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:true,min:false,std:false,sum:true,percentile:[0.90]});return n;} function createNumericForIdleTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;} function createPercentage(name,numerator,denominator){const histogram=new tr.v.Histogram(name,percentage_biggerIsBetter);if(denominator===0){histogram.addSample(0);}else{histogram.addSample(numerator/denominator);} return histogram;} -function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} -function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} -function addIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){addIdleTimes(histograms,model,name,events);});} -function addTotalIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){addIdleTimes(histograms,model,name,events);});} -function addIdleTimes(histograms,model,name,events){const cpuDuration=createNumericForIdleTime(name+'_cpu');const insideIdle=createNumericForIdleTime(name+'_inside_idle');const outsideIdle=createNumericForIdleTime(name+'_outside_idle');const idleDeadlineOverrun=createNumericForIdleTime(name+'_idle_deadline_overrun');events.forEach(function(event){const idleTask=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isIdleTask);let inside=0;let overrun=0;if(idleTask){const allottedTime=idleTask.args.allotted_time_ms;if(event.duration>allottedTime){overrun=event.duration-allottedTime;inside=event.cpuDuration*allottedTime/event.duration;}else{inside=event.cpuDuration;}} -cpuDuration.addSample(event.cpuDuration);insideIdle.addSample(inside);outsideIdle.addSample(event.cpuDuration-inside);idleDeadlineOverrun.addSample(overrun);});histograms.addHistogram(idleDeadlineOverrun);histograms.addHistogram(outsideIdle);const percentage=createPercentage(name+'_percentage_idle',insideIdle.sum,cpuDuration.sum);histograms.addHistogram(percentage);} +function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);} +histograms.addHistogram(cpuDuration);});} +function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);} +histograms.addHistogram(cpuDuration);});} +function addIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){addIdleTimes(histograms,model,name,events);});} +function addTotalIdleTimesOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){addIdleTimes(histograms,model,name,events);});} +function addIdleTimes(histograms,model,name,events){const cpuDuration=createNumericForIdleTime(name+'_cpu');const insideIdle=createNumericForIdleTime(name+'_inside_idle');const outsideIdle=createNumericForIdleTime(name+'_outside_idle');const idleDeadlineOverrun=createNumericForIdleTime(name+'_idle_deadline_overrun');for(const event of events){const idleTask=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isIdleTask);let inside=0;let overrun=0;if(idleTask){const allottedTime=idleTask.args.allotted_time_ms;if(event.duration>allottedTime){overrun=event.duration-allottedTime;inside=event.cpuDuration*allottedTime/event.duration;}else{inside=event.cpuDuration;}} +cpuDuration.addSample(event.cpuDuration);insideIdle.addSample(inside);outsideIdle.addSample(event.cpuDuration-inside);idleDeadlineOverrun.addSample(overrun);} +histograms.addHistogram(idleDeadlineOverrun);histograms.addHistogram(outsideIdle);const percentage=createPercentage(name+'_percentage_idle',insideIdle.sum,cpuDuration.sum);histograms.addHistogram(percentage);} +function isV8OrBlinkTopLevelGarbageCollectionEvent(event){return tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent(event)||isNonNestedNonForcedBlinkGarbageCollectionEvent(event);} +function addTotalDurationOfBlinkAndV8TopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8OrBlinkTopLevelGarbageCollectionEvent,event=>'unified-gc-total',function(name,events){const cpuDuration=createNumericForUnifiedEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);} +histograms.addHistogram(cpuDuration);});} return{blinkGcMetric,};});'use strict';tr.exportTo('tr.metrics.blink',function(){function leakDetectionMetric(histograms,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper===undefined){throw new Error('Chrome is not present.');} const rendererHelpers=modelHelper.rendererHelpers;if(Object.keys(rendererHelpers).length===0){throw new Error('Renderer process is not present.');} const pids=Object.keys(rendererHelpers);const chromeDumps=tr.metrics.sh.splitGlobalDumpsByBrowserName(model,undefined).get('chrome');const sumCounter=new Map();for(const pid of pids){for(const[key,count]of countLeakedBlinkObjects(chromeDumps,pid)){sumCounter.set(key,(sumCounter.get(key)||0)+count);}} @@ -8019,7 +8199,11 @@ const firstCounter=countBlinkObjects(dumps[0],pid);const lastCounter=countBlinkO return diffCounter;} function countBlinkObjects(dump,pid){const counter=new Map();const processesMemoryDumps=dump.processMemoryDumps;if(processesMemoryDumps[pid]===undefined)return counter;const blinkObjectsDump=processesMemoryDumps[pid].memoryAllocatorDumps.find(dump=>dump.fullName==='blink_objects');for(const v of blinkObjectsDump.children){counter.set(v.name,v.numerics.object_count.value);} return counter;} -return{leakDetectionMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){const snapshots=[];for(const pid in model.processes){const snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)continue;for(const object of snapshotInstances[0].snapshots){snapshots.push(object.args.processes);}} +return{leakDetectionMetric,};});'use strict';tr.exportTo('tr.metrics.console',function(){const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e4,30);const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;const SOURCES=['all','js','network'];function consoleErrorMetric(histograms,model){const counts={};for(const source of SOURCES){counts[source]=0;} +for(const slice of model.getDescendantEvents()){if(slice.category==='blink.console'&&slice.title==='ConsoleMessage::Error'){const source=slice.args.source.toLowerCase();counts.all++;if(source in counts){counts[source]++;}} +if(slice.category==='v8.console'&&(slice.title==='V8ConsoleMessage::Exception'||slice.title==='V8ConsoleMessage::Error'||slice.title==='V8ConsoleMessage::Assert')){counts.all++;counts.js++;}} +for(const source of SOURCES){histograms.createHistogram(`console:error:${source}`,tr.b.Unit.byName.count_smallerIsBetter,counts[source],{description:`Number of ${source} console error messages`,summaryOptions:SUMMARY_OPTIONS});}} +tr.metrics.MetricRegistry.register(consoleErrorMetric);return{consoleErrorMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){const snapshots=[];for(const pid in model.processes){const snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)continue;for(const object of snapshotInstances[0].snapshots){snapshots.push(object.args.processes);}} return snapshots;} function getProcessSumsFromSnapshot(snapshot){const processSums=new Map();for(const processData of snapshot){const processName=processData.name;if(!(processSums.has(processName))){processSums.set(processName,{sum:0.0,paths:new Set()});} processSums.get(processName).sum+=parseFloat(processData.pCpu);if(processData.path){processSums.get(processName).paths.add(processData.path);}} @@ -8067,39 +8251,71 @@ processOnEnded(playEndTime,duration){if(this.playStart_===undefined)return;if(th addMetricToHistograms(histograms){this.addSample_(histograms,'time_to_video_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToVideoPlay);this.addSample_(histograms,'time_to_audio_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToAudioPlay);this.addSample_(histograms,'dropped_frame_count',tr.b.Unit.byName.count_smallerIsBetter,this.droppedFrameCount);for(const[key,value]of this.seekTimes.entries()){const keyString=key.toString().replace('.','_');this.addSample_(histograms,'pipeline_seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.pipelineSeekTime);this.addSample_(histograms,'seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.seekTime);} this.addSample_(histograms,'buffering_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.bufferingTime);} addSample_(histograms,name,unit,sample){if(sample===undefined)return;const histogram=histograms.getHistogramNamed(name);if(histogram===undefined){histograms.createHistogram(name,unit,sample);}else{histogram.addSample(sample);}}} -tr.metrics.MetricRegistry.register(mediaMetric);return{mediaMetric,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const UNKNOWN_THREAD_NAME='Unknown';const CATEGORY_THREAD_MAP=new Map();CATEGORY_THREAD_MAP.set('all',[/.*/]);CATEGORY_THREAD_MAP.set('browser',[/^Browser Compositor$/,/^CrBrowserMain$/]);CATEGORY_THREAD_MAP.set('display_compositor',[/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('fast_path',[/^Browser Compositor$/,/^Chrome_InProcGpuThread$/,/^Compositor$/,/^CrBrowserMain$/,/^CrGpuMain$/,/IOThread/,/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('gpu',[/^Chrome_InProcGpuThread$/,/^CrGpuMain$/]);CATEGORY_THREAD_MAP.set('io',[/IOThread/]);CATEGORY_THREAD_MAP.set('raster',[/CompositorTileWorker/]);CATEGORY_THREAD_MAP.set('renderer_compositor',[/^Compositor$/]);CATEGORY_THREAD_MAP.set('renderer_main',[/^CrRendererMain$/]);const DRM_EVENT='DrmEventFlipComplete';const DISPLAY_EVENT='BenchmarkInstrumentation::DisplayRenderingStats';const GESTURE_EVENT='SyntheticGestureController::running';function addValueToMap_(map,key,value){const oldValue=map.get(key)||0;map.set(key,oldValue+value);} +tr.metrics.MetricRegistry.register(mediaMetric);return{mediaMetric,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const UNKNOWN_THREAD_NAME='Unknown';const CATEGORY_THREAD_MAP=new Map();CATEGORY_THREAD_MAP.set('all',[/.*/]);CATEGORY_THREAD_MAP.set('browser',[/^Browser Compositor$/,/^CrBrowserMain$/]);CATEGORY_THREAD_MAP.set('display_compositor',[/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('fast_path',[/^Browser Compositor$/,/^Chrome_InProcGpuThread$/,/^Compositor$/,/^CrBrowserMain$/,/^CrGpuMain$/,/IOThread/,/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('gpu',[/^Chrome_InProcGpuThread$/,/^CrGpuMain$/]);CATEGORY_THREAD_MAP.set('io',[/IOThread/]);CATEGORY_THREAD_MAP.set('raster',[/CompositorTileWorker/]);CATEGORY_THREAD_MAP.set('renderer_compositor',[/^Compositor$/]);CATEGORY_THREAD_MAP.set('renderer_main',[/^CrRendererMain$/]);const ALL_CATEGORIES=[...CATEGORY_THREAD_MAP.keys(),'other'];function addValueToMap_(map,key,value){const oldValue=map.get(key)||0;map.set(key,oldValue+value);} function*getCategories_(threadName){let isOther=true;for(const[category,regexps]of CATEGORY_THREAD_MAP){for(const regexp of regexps){if(regexp.test(threadName)){if(category!=='all')isOther=false;yield category;break;}}} if(isOther)yield'other';} -function addCoresPerSecondHistograms(histograms,model,segments){const totalDuration=tr.b.math.Statistics.sum(segments,segment=>segment.duration);const threadValues=new Map();for(const thread of model.getAllThreads()){addValueToMap_(threadValues,thread.name||UNKNOWN_THREAD_NAME,tr.b.math.Statistics.sum(segments,segment=>thread.getCpuTimeForRange(segment.boundsRange))/totalDuration);} +function isSubset_(regexps1,regexps2){for(const r1 of regexps1){if(regexps2.find(r2=>r2.toString()===r1.toString())===undefined){return false;}} +return true;} +function addCpuUtilizationHistograms(histograms,model,segments,segmentName,shouldNormalize){const histogramMap=new Map();for(const category of ALL_CATEGORIES){const histogram=histograms.createHistogram(`thread_${category}_cpu_time_per_${segmentName}_tbmv2`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(1,50,20),description:`CPU cores per ${segmentName} of a thread group`,summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histogramMap.set(category,histogram);} +for(const[category,regexps]of CATEGORY_THREAD_MAP){const relatedCategories=new tr.v.d.RelatedNameMap();const histogram=histogramMap.get(category);for(const[otherCategory,otherRegexps]of CATEGORY_THREAD_MAP){if(otherCategory===category)continue;if(category!=='all'&&!isSubset_(otherRegexps,regexps))continue;const otherHistogram=histogramMap.get(otherCategory);relatedCategories.set(otherCategory,otherHistogram.name);} +if([...relatedCategories.values()].length>0){histogram.diagnostics.set('breakdown',relatedCategories);}} +for(const segment of segments){const threadValues=new Map();for(const thread of model.getAllThreads()){addValueToMap_(threadValues,thread.name||UNKNOWN_THREAD_NAME,thread.getCpuTimeForRange(segment.boundsRange));} const categoryValues=new Map();const breakdowns=new Map();for(const[threadName,coresPerSec]of threadValues){for(const category of getCategories_(threadName)){addValueToMap_(categoryValues,category,coresPerSec);if(!breakdowns.has(category)){breakdowns.set(category,new tr.v.d.Breakdown());} breakdowns.get(category).set(threadName,coresPerSec);}} -for(const[category,coresPerSec]of categoryValues){histograms.createHistogram(`cores_per_second_${category}_thread`,tr.b.Unit.byName.unitlessNumber_smallerIsBetter,{value:coresPerSec,diagnostics:{breakdown:breakdowns.get(category)}},{description:'CPU cores per second of a thread group'});}} -function addFrameTimeHistograms(histograms,model,segments){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper||!modelHelper.browserProcess)return;let events=[];if(modelHelper.gpuHelper){const gpuProcess=modelHelper.gpuHelper.process;events=[...gpuProcess.findTopmostSlicesNamed(DRM_EVENT)];if(events.length===0){events=[...gpuProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];}} -if(events.length===0){events=[...modelHelper.browserProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];} -if(events.length===0)return;const timestamps=events.map(event=>(event.title!==DRM_EVENT?event.start:(tr.b.convertUnit(event.args.data['vblank.tv_sec'],tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)+ -tr.b.convertUnit(event.args.data['vblank.tv_usec'],tr.b.UnitScale.TIME.MICRO_SEC,tr.b.UnitScale.TIME.MILLI_SEC))));const frameTimes=[];for(const segment of segments){const filteredTimestamps=segment.boundsRange.filterArray(timestamps);for(let i=1;i<filteredTimestamps.length;i++){frameTimes.push(filteredTimestamps[i]-filteredTimestamps[i-1]);}} -histograms.createHistogram('frame_times_tbmv2',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,frameTimes,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),description:'Raw frame times.'});histograms.createHistogram('mean_frame_time_tbmv2',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.b.math.Statistics.mean(frameTimes),{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),description:'Arithmetic mean of frame times.'});histograms.createHistogram('smooth_frames',tr.b.Unit.byName.normalizedPercentage_biggerIsBetter,tr.b.math.Statistics.sum(frameTimes,(x=>(x<17?1:0)))/frameTimes.length,{description:'Percentage of frames that were hitting 60 FPS.'});} -function eventIsValidGraphicsEvent(event,eventMap){if(!event.bindId||!event.args||!event.args.step){return false;} -const bindId=event.bindId;if(bindId in eventMap&&event.args.step in eventMap[bindId]){if(event.args.step==='IssueBeginFrame'||event.args.step==='ReceiveBeginFrame'){throw new Error('Unexpected duplicate step: '+event.args.step);} +for(const category of ALL_CATEGORIES){let value=categoryValues.get(category)||0;if(shouldNormalize)value/=segment.duration;const diagnostics=new tr.v.d.DiagnosticMap();const breakdown=breakdowns.get(category);if(breakdown)diagnostics.set('breakdown',breakdown);const histogram=histogramMap.get(category);histogram.addSample(value,diagnostics);}}} +const SUMMARY_OPTIONS={percentile:[0.90,0.95],};return{addCpuUtilizationHistograms,SUMMARY_OPTIONS,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const DISPLAY_EVENT='BenchmarkInstrumentation::DisplayRenderingStats';const DRM_EVENT='DrmEventFlipComplete';const SURFACE_FLINGER_EVENT='vsync_before';const COMPOSITOR_FRAME_PRESENTED_EVENT='FramePresented';const MIN_FRAME_LENGTH=0.5;const PAUSE_THRESHOLD=20;const ASH_ENVIRONMENT='ash';const BROWSER_ENVIRONMENT='browser';function getDisplayCompositorPresentationEvents_(modelHelper){if(!modelHelper||!modelHelper.browserProcess)return[];let events=[];if(modelHelper.surfaceFlingerProcess){events=[...modelHelper.surfaceFlingerProcess.findTopmostSlicesNamed(SURFACE_FLINGER_EVENT)];if(events.length>0)return events;} +if(modelHelper.gpuHelper){const gpuProcess=modelHelper.gpuHelper.process;events=[...gpuProcess.findTopmostSlicesNamed(DRM_EVENT)];if(events.length>0)return events;events=[...gpuProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];if(events.length>0)return events;} +return[...modelHelper.browserProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];} +function getUIPresentationEvents_(modelHelper){if(!modelHelper||!modelHelper.browserProcess)return[];const legacyEvents=[];const eventsByEnvironment={};eventsByEnvironment[ASH_ENVIRONMENT]=[];eventsByEnvironment[BROWSER_ENVIRONMENT]=[];for(const event of modelHelper.browserProcess.findTopmostSlicesNamed(COMPOSITOR_FRAME_PRESENTED_EVENT)){if(!('environment'in event.args)){legacyEvents.push(event);}else{eventsByEnvironment[event.args.environment].push(event);}} +if(eventsByEnvironment[ASH_ENVIRONMENT].length>0){return eventsByEnvironment[ASH_ENVIRONMENT];} +if(eventsByEnvironment[BROWSER_ENVIRONMENT].length>0){return eventsByEnvironment[BROWSER_ENVIRONMENT];} +return legacyEvents;} +function addSurfaceFlingerHistograms_(histograms,frameSegments,refreshPeriod){let frameLengths=frameSegments.map(x=>x.duration/refreshPeriod);frameLengths=frameLengths.filter(length=>length>=MIN_FRAME_LENGTH);histograms.createHistogram('frame_lengths',tr.b.Unit.byName.unitlessNumber_smallerIsBetter,frameLengths,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,5,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Frame times in vsyncs.'});histograms.createHistogram('avg_surface_fps',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,frameLengths.length/tr.b.convertUnit(tr.b.math.Statistics.sum(frameSegments,x=>x.duration),tr.b.UnitScale.TIME.MILLI_SEC,tr.b.UnitScale.TIME.SEC),{description:'Average frames per second.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});let jankCount=0;for(let i=1;i<frameLengths.length;i++){const change=Math.round(frameLengths[i]-frameLengths[i-1]);if(change>0&&change<PAUSE_THRESHOLD)jankCount++;} +histograms.createHistogram('jank_count',tr.b.Unit.byName.unitlessNumber_smallerIsBetter,jankCount,{description:'Number of changes in frame rate.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});} +function computeFrameSegments_(timestamps,segments){const frameSegments=[];for(const segment of segments){const filtered=segment.boundsRange.filterArray(timestamps,x=>x[0]);for(let i=1;i<filtered.length;i++){const duration=filtered[i][1]-filtered[i-1][1];frameSegments.push(new tr.model.um.Segment(filtered[i-1][0],duration));}} +return frameSegments;} +function addBasicFrameTimeHistograms_(histograms,frameSegments,prefix){const frameTimes=frameSegments.map(x=>x.duration);histograms.createHistogram(`${prefix}frame_times`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,frameTimes,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),description:'Raw frame times.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram(`${prefix}percentage_smooth`,tr.b.Unit.byName.unitlessNumber_biggerIsBetter,100*tr.b.math.Statistics.sum(frameTimes,(x=>(x<17?1:0)))/frameTimes.length,{description:'Percentage of frames that were hitting 60 FPS.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});} +function addFrameTimeHistograms(histograms,model,segments){const events=getDisplayCompositorPresentationEvents_(model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper));if(!events)return;const timestamps=events.map(event=>[event.start,event.title!==DRM_EVENT?event.start:(tr.b.convertUnit(event.args.data['vblank.tv_sec'],tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)+ +tr.b.convertUnit(event.args.data['vblank.tv_usec'],tr.b.UnitScale.TIME.MICRO_SEC,tr.b.UnitScale.TIME.MILLI_SEC))]);const frameSegments=computeFrameSegments_(timestamps,segments);addBasicFrameTimeHistograms_(histograms,frameSegments,'');tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,'frame',false);for(const metadata of model.metadata){if(metadata.value&&metadata.value.surface_flinger){addSurfaceFlingerHistograms_(histograms,frameSegments,metadata.value.surface_flinger.refresh_period);return;}}} +function addUIFrameTimeHistograms(histograms,model,segments){const events=getUIPresentationEvents_(model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper));if(!events)return;const timestamps=events.map(event=>[event.start,event.start]);const frameSements=computeFrameSegments_(timestamps,segments);addBasicFrameTimeHistograms_(histograms,frameSements,'ui_');} +return{addFrameTimeHistograms,addUIFrameTimeHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const BEGIN_SCROLL_UPDATE_COMP_NAME='LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';function*iterAsyncEvents_(processHelpers,ranges,processEventFn){for(const processHelper of processHelpers){const process=processHelper.process;for(const event of process.getDescendantEventsInSortedRanges(ranges,container=>container instanceof tr.model.AsyncSliceGroup)){yield*processEventFn(event);}}} +function*processInputLatencyEvent(event){if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice))return;const latency=event.inputLatency;if(latency===undefined)return;yield tr.b.Unit.timestampFromUs(latency);} +function*processLatencyEvent(event){if(event.title!=='Latency::ScrollUpdate'||!('data'in event.args)||!(END_COMP_NAME in event.args.data)){return;} +const data=event.args.data;const endTime=data[END_COMP_NAME].time;if(BEGIN_SCROLL_UPDATE_COMP_NAME in data){yield tr.b.Unit.timestampFromUs(endTime-data[BEGIN_SCROLL_UPDATE_COMP_NAME].time);}else{throw new Error('LatencyInfo has no begin component');}} +function*processGestureScrollUpdateLatencyEvent(event){if(event.title!=='InputLatency::GestureScrollUpdate')return;if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice)){throw new Error('Gesture scroll update latency event is not an '+'instance of tr.e.cc.InputLatencyAsyncSlice');} +const latency=event.inputLatency;if(latency===undefined)return;yield[event.start,tr.b.Unit.timestampFromUs(latency)];} +function addLatencyHistograms(histograms,model,segments){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper)return;const ranges=segments.map(s=>s.boundsRange);const inputEventLatencies=[...iterAsyncEvents_(modelHelper.browserHelpers,ranges,processInputLatencyEvent)];histograms.createHistogram('input_event_latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,inputEventLatencies,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Input event latencies.'});const mainThreadScrollLatencies=[...iterAsyncEvents_(Object.values(modelHelper.rendererHelpers),ranges,processLatencyEvent)];histograms.createHistogram('main_thread_scroll_latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,mainThreadScrollLatencies,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,50),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Main thread scroll latencies.'});const gestureScrollUpdateLatencies=[...iterAsyncEvents_(modelHelper.browserHelpers,ranges,processGestureScrollUpdateLatencyEvent)].sort();if(gestureScrollUpdateLatencies.length){histograms.createHistogram('first_gesture_scroll_update_latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,gestureScrollUpdateLatencies[0][1],{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Latency of the first gesture scroll update.'});}} +return{addLatencyHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){function eventIsValidGraphicsEvent_(event,eventMap){if(event.title!=='Graphics.Pipeline'||!event.bindId||!event.args||!event.args.step){return false;} +const bindId=event.bindId;if(eventMap.has(bindId)&&event.args.step in eventMap.get(bindId)){if(event.args.step==='IssueBeginFrame'||event.args.step==='ReceiveBeginFrame'){throw new Error('Unexpected duplicate step: '+event.args.step);} return false;} return true;} -function generateBreakdownForCompositorPipelineInClient(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('time before GenerateRenderPass',flow.GenerateRenderPass.start-flow.ReceiveBeginFrame.start);breakdown.set('GenerateRenderPass duration',flow.GenerateRenderPass.duration);breakdown.set('GenerateCompositorFrame duration',flow.GenerateCompositorFrame.duration);breakdown.set('SubmitCompositorFrame duration',flow.SubmitCompositorFrame.duration);return breakdown;} -function generateBreakdownForCompositorPipelineInService(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('Processing CompositorFrame on reception',flow.ReceiveCompositorFrame.duration);breakdown.set('Delay before SurfaceAggregation',flow.SurfaceAggregation.start-flow.ReceiveCompositorFrame.end);breakdown.set('SurfaceAggregation duration',flow.SurfaceAggregation.duration);return breakdown;} -function generateBreakdownForDraw(drawEvent){const breakdown=new tr.v.d.Breakdown();for(const slice of drawEvent.subSlices){breakdown.set(slice.title,slice.duration);} +function generateBreakdownForCompositorPipelineInClient_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('time before GenerateRenderPass',flow.GenerateRenderPass.start-flow.ReceiveBeginFrame.start);breakdown.set('GenerateRenderPass duration',flow.GenerateRenderPass.duration);breakdown.set('GenerateCompositorFrame duration',flow.GenerateCompositorFrame.duration);breakdown.set('SubmitCompositorFrame duration',flow.SubmitCompositorFrame.duration);return breakdown;} +function generateBreakdownForCompositorPipelineInService_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('Processing CompositorFrame on reception',flow.ReceiveCompositorFrame.duration);breakdown.set('Delay before SurfaceAggregation',flow.SurfaceAggregation.start-flow.ReceiveCompositorFrame.end);breakdown.set('SurfaceAggregation duration',flow.SurfaceAggregation.duration);return breakdown;} +function generateBreakdownForDraw_(drawEvent){const breakdown=new tr.v.d.Breakdown();for(const slice of drawEvent.subSlices){breakdown.set(slice.title,slice.duration);} return breakdown;} -function getDisplayCompositorThread(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const gpuHelper=chromeHelper.gpuHelper;if(gpuHelper){const thread=gpuHelper.process.findAtMostOneThreadNamed('VizCompositorThread');if(thread){return thread;}} -const browserHelper=chromeHelper.browserHelpers[0];return browserHelper.process.findAtMostOneThreadNamed('CrBrowserMain');} -function addPipelineHistograms(histograms,model,segments){let events=[];for(const thread of model.getAllThreads()){const graphicsEvents=thread.sliceGroup.slices.filter(slice=>slice.title==='Graphics.Pipeline');for(const segment of segments){const filteredEvents=segment.boundsRange.filterArray(graphicsEvents,evt=>evt.start);events=events.concat(filteredEvents);}} -const bindEvents={};for(const event of events){if(!eventIsValidGraphicsEvent(event,bindEvents))continue;const bindId=event.bindId;if(!(bindId in bindEvents)){bindEvents[bindId]={};} -bindEvents[bindId][event.args.step]=event;} -const dcThread=getDisplayCompositorThread(model);const drawEvents={};if(dcThread){const events=[...dcThread.findTopmostSlicesNamed('Graphics.Pipeline.DrawAndSwap')];for(const segment of segments){const filteredEvents=segment.boundsRange.filterArray(events,evt=>evt.start);for(const event of filteredEvents){if(!event.id.startsWith(':ptr:'))continue;const id=parseInt(event.id.substring(5),16);if(id in drawEvents){throw new Error('Duplicate draw events: '+id);} +function getDisplayCompositorThread_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const gpuHelper=chromeHelper.gpuHelper;if(gpuHelper){const thread=gpuHelper.process.findAtMostOneThreadNamed('VizCompositorThread');if(thread){return thread;}} +if(!chromeHelper.browserProcess)return null;return chromeHelper.browserProcess.findAtMostOneThreadNamed('CrBrowserMain');} +function addPipelineHistograms(histograms,model,segments){const ranges=segments.map(s=>s.boundsRange);const bindEvents=new Map();for(const thread of model.getAllThreads()){for(const event of thread.sliceGroup.childEvents()){if(!eventIsValidGraphicsEvent_(event,bindEvents))continue;for(const range of ranges){if(range.containsExplicitRangeInclusive(event.start,event.end)){if(!bindEvents.has(event.bindId))bindEvents.set(event.bindId,{});break;}} +if(bindEvents.has(event.bindId)){bindEvents.get(event.bindId)[event.args.step]=event;}}} +const dcThread=getDisplayCompositorThread_(model);const drawEvents={};if(dcThread){const events=[...dcThread.findTopmostSlicesNamed('Graphics.Pipeline.DrawAndSwap')];for(const segment of segments){const filteredEvents=segment.boundsRange.filterArray(events,evt=>evt.start);for(const event of filteredEvents){if((event.args&&event.args.status==='canceled')||!event.id.startsWith(':ptr:')){continue;} +const id=parseInt(event.id.substring(5),16);if(id in drawEvents){throw new Error('Duplicate draw events: '+id);} drawEvents[id]=event;}}} -const issueToReceipt=histograms.createHistogram('pipeline:begin_frame_transport',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency of begin-frame message from the display '+'compositor to the client, including the IPC latency and task-'+'queue time in the client.',});const receiptToSubmit=histograms.createHistogram('pipeline:begin_frame_to_frame_submission',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame reception and '+'CompositorFrame submission in the renderer.'});const submitToAggregate=histograms.createHistogram('pipeline:frame_submission_to_display',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between CompositorFrame submission in the '+'renderer to display in the display-compositor, including IPC '+'latency, task-queue time in the display-compositor, and '+'additional processing (e.g. surface-sync etc.)',});const aggregateToDraw=histograms.createHistogram('pipeline:draw',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'How long it takes for the gpu-swap step.'});for(const flow of Object.values(bindEvents)){if(!flow.IssueBeginFrame||!flow.ReceiveBeginFrame||!flow.SubmitCompositorFrame||!flow.SurfaceAggregation){continue;} +const issueToReceipt=histograms.createHistogram('pipeline:begin_frame_transport',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency of begin-frame message from the display '+'compositor to the client, including the IPC latency and task-'+'queue time in the client.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const receiptToSubmit=histograms.createHistogram('pipeline:begin_frame_to_frame_submission',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame reception and '+'CompositorFrame submission in the renderer.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const submitToAggregate=histograms.createHistogram('pipeline:frame_submission_to_display',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between CompositorFrame submission in the '+'renderer to display in the display-compositor, including IPC '+'latency, task-queue time in the display-compositor, and '+'additional processing (e.g. surface-sync etc.)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const aggregateToDraw=histograms.createHistogram('pipeline:draw',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'How long it takes for the gpu-swap step.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});for(const flow of bindEvents.values()){if(!flow.IssueBeginFrame||!flow.ReceiveBeginFrame||!flow.SubmitCompositorFrame||!flow.SurfaceAggregation){continue;} issueToReceipt.addSample(flow.ReceiveBeginFrame.start- -flow.IssueBeginFrame.start);receiptToSubmit.addSample(flow.SubmitCompositorFrame.end-flow.ReceiveBeginFrame.start,{breakdown:generateBreakdownForCompositorPipelineInClient(flow)});submitToAggregate.addSample(flow.SurfaceAggregation.end-flow.SubmitCompositorFrame.end,{breakdown:generateBreakdownForCompositorPipelineInService(flow)});if(flow.SurfaceAggregation.args&&flow.SurfaceAggregation.args.display_trace){const displayTrace=flow.SurfaceAggregation.args.display_trace;if(!(displayTrace in drawEvents))continue;const drawEvent=drawEvents[displayTrace];aggregateToDraw.addSample(drawEvent.duration,{breakdown:generateBreakdownForDraw(drawEvent)});}}} -function renderingMetric(histograms,model){const segments=[];const IRExp=/Interaction\.([^/]+)(\/[^/]*)?$/;for(const thread of model.getAllThreads()){for(const slice of thread.asyncSliceGroup.slices){if(slice.title===GESTURE_EVENT){segments.push(new tr.model.um.Segment(slice.start,slice.duration));}else{const parts=IRExp.exec(slice.title);if(parts&&!parts[1].startsWith('Gesture_')){segments.push(new tr.model.um.Segment(slice.start,slice.duration));}}}} -if(segments.length===0)return;addCoresPerSecondHistograms(histograms,model,segments);addFrameTimeHistograms(histograms,model,segments);addPipelineHistograms(histograms,model,segments);} -tr.metrics.MetricRegistry.register(renderingMetric);return{renderingMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleExceptionMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}} +flow.IssueBeginFrame.start);receiptToSubmit.addSample(flow.SubmitCompositorFrame.end-flow.ReceiveBeginFrame.start,{breakdown:generateBreakdownForCompositorPipelineInClient_(flow)});submitToAggregate.addSample(flow.SurfaceAggregation.end-flow.SubmitCompositorFrame.end,{breakdown:generateBreakdownForCompositorPipelineInService_(flow)});if(flow.SurfaceAggregation.args&&flow.SurfaceAggregation.args.display_trace){const displayTrace=flow.SurfaceAggregation.args.display_trace;if(!(displayTrace in drawEvents))continue;const drawEvent=drawEvents[displayTrace];aggregateToDraw.addSample(drawEvent.duration,{breakdown:generateBreakdownForDraw_(drawEvent)});}}} +return{addPipelineHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const IMPL_THREAD_RENDERING_STATS_EVENT='BenchmarkInstrumentation::ImplThreadRenderingStats';const VISIBLE_CONTENT_DATA='visible_content_area';const APPROXIMATED_VISIBLE_CONTENT_DATA='approximated_visible_content_area';const CHECKERBOARDED_VISIBLE_CONTENT_DATA='checkerboarded_visible_content_area';function addPixelsHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;const approximatedPixelPercentages=[];const checkerboardedPixelPercentages=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.compositorThread===undefined)continue;const slices=rendererHelper.compositorThread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==IMPL_THREAD_RENDERING_STATS_EVENT)continue;const data=slice.args.data;if(!(VISIBLE_CONTENT_DATA in data)){throw new Error(`${VISIBLE_CONTENT_DATA} is missing`);} +const visibleContentArea=data[VISIBLE_CONTENT_DATA];if(visibleContentArea===0){continue;} +if(APPROXIMATED_VISIBLE_CONTENT_DATA in data){approximatedPixelPercentages.push(data[APPROXIMATED_VISIBLE_CONTENT_DATA]/visibleContentArea);} +if(CHECKERBOARDED_VISIBLE_CONTENT_DATA in data){checkerboardedPixelPercentages.push(data[CHECKERBOARDED_VISIBLE_CONTENT_DATA]/visibleContentArea);}}} +histograms.createHistogram('mean_pixels_approximated',tr.b.Unit.byName.normalizedPercentage_smallerIsBetter,100*tr.b.math.Statistics.mean(approximatedPixelPercentages),{description:'Percentage of pixels that were approximated '+'(checkerboarding, low-resolution tiles, etc.).',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('mean_pixels_checkerboarded',tr.b.Unit.byName.normalizedPercentage_smallerIsBetter,100*tr.b.math.Statistics.mean(checkerboardedPixelPercentages),{description:'Percentage of pixels that were checkerboarded.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});} +return{addPixelsHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const BEGIN_MAIN_FRAME_EVENT='ThreadProxy::BeginMainFrame';const SEND_BEGIN_FRAME_EVENT='ThreadProxy::ScheduledActionSendBeginMainFrame';function getEventTimesByBeginFrameId_(thread,title,ranges){const out=new Map();const slices=thread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==title)continue;const id=slice.args.begin_frame_id;if(id===undefined)throw new Error('Event is missing begin_frame_id');if(out.has(id))throw new Error(`There must be exactly one ${title}`);out.set(id,slice.start);} +return out;} +function addQueueingDurationHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;let targetRenderers=chromeHelper.telemetryHelper.renderersWithIR;if(targetRenderers.length===0){targetRenderers=Object.values(chromeHelper.rendererHelpers);} +const queueingDurations=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of targetRenderers){const mainThread=rendererHelper.mainThread;const compositorThread=rendererHelper.compositorThread;if(mainThread===undefined||compositorThread===undefined)continue;const beginMainFrameTimes=getEventTimesByBeginFrameId_(mainThread,BEGIN_MAIN_FRAME_EVENT,ranges);const sendBeginFrameTimes=getEventTimesByBeginFrameId_(compositorThread,SEND_BEGIN_FRAME_EVENT,ranges);for(const[id,time]of sendBeginFrameTimes){queueingDurations.push(beginMainFrameTimes.get(id)-time);}} +histograms.createHistogram('queueing_durations',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,queueingDurations,{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(0.01,2,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Time between ScheduledActionSendBeginMainFrame in '+'the compositor thread and the corresponding '+'BeginMainFrame in the main thread.'});} +return{addQueueingDurationHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const GESTURE_EVENT='SyntheticGestureController::running';function renderingMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;const segments=chromeHelper.telemetryHelper.segments;if(segments.length>0){tr.metrics.rendering.addFrameTimeHistograms(histograms,model,segments);tr.metrics.rendering.addLatencyHistograms(histograms,model,segments);tr.metrics.rendering.addPipelineHistograms(histograms,model,segments);tr.metrics.rendering.addPixelsHistograms(histograms,model,segments);tr.metrics.rendering.addQueueingDurationHistograms(histograms,model,segments);} +const uiSegments=chromeHelper.telemetryHelper.uiSegments;if(uiSegments.length>0){tr.metrics.rendering.addUIFrameTimeHistograms(histograms,model,chromeHelper.telemetryHelper.uiSegments);}} +tr.metrics.MetricRegistry.register(renderingMetric,{requiredCategories:['benchmark','toplevel'],});return{renderingMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleExceptionMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}} const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const[pid,process]of Object.entries(model.processes)){} histograms.addHistogram(hist);throw new Error('There was an error');} tr.metrics.MetricRegistry.register(sampleExceptionMetric);return{sampleExceptionMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}} @@ -8147,12 +8363,17 @@ const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,2 return snapshot;} function findAllEvents(rendererHelper,category,title){const targetEvents=[];for(const ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,category,title))continue;targetEvents.push(ev);} return targetEvents;} -function collectTimeToEvent(category,eventName,rendererHelper,frameToNavStartEvents){const targetEvents=findAllEvents(rendererHelper,category,eventName);const samples=[];for(const ev of targetEvents){if(rendererHelper.isTelemetryInternalEvent(ev))continue;const frameIdRef=ev.args.frame;const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ev.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)continue;const url=snapshot.args.documentLoaderURL;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(url))continue;const navigationStartEvent=EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(frameToNavStartEvents.get(frameIdRef)||[],ev.start);if(navigationStartEvent===undefined)continue;const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,ev.start);const networkEvents=getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),url:new tr.v.d.GenericSet([url]),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(ev)}});} +function findTimeToXEntries(category,eventName,rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const targetEvents=findAllEvents(rendererHelper,category,eventName);const entries=[];for(const targetEvent of targetEvents){if(rendererHelper.isTelemetryInternalEvent(targetEvent))continue;const frameIdRef=targetEvent.args.frame;const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,targetEvent.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)continue;const url=snapshot.args.documentLoaderURL;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(url))continue;let navigationStartEvent;if(targetEvent.args.data===undefined||targetEvent.args.data.navigationId===undefined){navigationStartEvent=EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(frameToNavStartEvents.get(frameIdRef)||[],targetEvent.start);}else{navigationStartEvent=navIdToNavStartEvents.get(targetEvent.args.data.navigationId);} +if(navigationStartEvent===undefined)continue;entries.push({navigationStartEvent,targetEvent,url,});} +return entries;} +function collectTimeToEvent(rendererHelper,timeToXEntries){const samples=[];for(const{targetEvent,navigationStartEvent,url}of timeToXEntries){const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,targetEvent.start);const networkEvents=getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),url:new tr.v.d.GenericSet([url]),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(targetEvent)}});} +return samples;} +function collectTimeToEventInCpuTime(rendererHelper,timeToXEntries){const samples=[];for(const{targetEvent,navigationStartEvent,url}of timeToXEntries){const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,targetEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToEventRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToEventRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStartEvent),end:new RelatedEventSet(targetEvent),infos:new tr.v.d.GenericSet([{pid:rendererHelper.pid,start:navigationStartEvent.start,event:targetEvent.start,}]),}});} return samples;} function addFirstMeaningfulPaintSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const networkEvents=getNetworkEventsInRange(rendererHelper.process,navStartToFMPRange);const timeToFirstMeaningfulPaint=navStartToFMPRange.duration;const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToFMPRange);samples.push({value:timeToFirstMeaningfulPaint,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});} function addFirstMeaningfulPaintCpuTimeSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToFMPRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToFMPRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});} function decorateInteractivitySampleWithDiagnostics_(rendererHelper,eventTimestamp,navigationStartEvent,firstMeaningfulPaintTime,domContentLoadedEndTime,url){if(eventTimestamp===undefined)return undefined;const navigationStartTime=navigationStartEvent.start;const navStartToEventTimeRange=tr.b.math.Range.fromExplicitRange(navigationStartTime,eventTimestamp);const networkEvents=getNetworkEventsInRange(rendererHelper.process,navStartToEventTimeRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventTimeRange);const breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);return{value:navStartToEventTimeRange.duration,diagnostics:tr.v.d.DiagnosticMap.fromObject({'Start':new RelatedEventSet(navigationStartEvent),'Navigation infos':new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,navigationStartTime,firstMeaningfulPaintTime,domContentLoadedEndTime,eventTimestamp,}]),'Breakdown of [navStart, eventTimestamp]':breakdownDiagnostic,}),};} -function collectLoadingMetricsForRenderer(rendererHelper){const frameToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const firstPaintSamples=collectTimeToEvent('loading','firstPaint',rendererHelper,frameToNavStartEvents);const firstContentfulPaintSamples=collectTimeToEvent('loading','firstContentfulPaint',rendererHelper,frameToNavStartEvents);const onLoadSamples=collectTimeToEvent('blink.user_timing','loadEventStart',rendererHelper,frameToNavStartEvents);return{frameToNavStartEvents,firstPaintSamples,firstContentfulPaintSamples,onLoadSamples,};} +function collectLoadingMetricsForRenderer(rendererHelper){const frameToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const navIdToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByNavId(rendererHelper,'navigationStart','blink.user_timing');const firstPaintSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('loading','firstPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const timeToFCPEntries=findTimeToXEntries('loading','firstContentfulPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const firstContentfulPaintSamples=collectTimeToEvent(rendererHelper,timeToFCPEntries);const firstContentfulPaintCpuTimeSamples=collectTimeToEventInCpuTime(rendererHelper,timeToFCPEntries);const onLoadSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('blink.user_timing','loadEventStart',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));return{frameToNavStartEvents,firstPaintSamples,firstContentfulPaintSamples,firstContentfulPaintCpuTimeSamples,onLoadSamples,};} function collectMetricsFromLoadExpectations(model,chromeHelper){const interactiveSamples=[];const firstCpuIdleSamples=[];const firstMeaningfulPaintSamples=[];const firstMeaningfulPaintCpuTimeSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;} const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];if(expectation.fmpEvent!==undefined){addFirstMeaningfulPaintSample(firstMeaningfulPaintSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);addFirstMeaningfulPaintCpuTimeSample(firstMeaningfulPaintCpuTimeSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);} if(expectation.firstCpuIdleTime!==undefined){firstCpuIdleSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.firstCpuIdleTime,expectation.navigationStart,expectation.fmpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));} @@ -8161,7 +8382,7 @@ return{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstCpuId function addSamplesToHistogram(samples,histogram,histograms){for(const sample of samples){histogram.addSample(sample.value,sample.diagnostics);if(histogram.name!=='timeToFirstContentfulPaint')continue;if(!sample.breakdownTree)continue;for(const[category,breakdown]of Object.entries(sample.breakdownTree)){const relatedName=`${histogram.name}:${category}`;let relatedHist=histograms.getHistogramsNamed(relatedName)[0];if(!relatedHist){relatedHist=histograms.createHistogram(relatedName,histogram.unit,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,summaryOptions:{count:false,max:false,min:false,sum:false,},});let relatedNames=histogram.diagnostics.get('breakdown');if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();histogram.diagnostics.set('breakdown',relatedNames);} relatedNames.set(category,relatedName);} relatedHist.addSample(breakdown.total,{breakdown:tr.v.d.Breakdown.fromEntries(Object.entries(breakdown.events)),});}}} -function loadingMetric(histograms,model){const firstPaintHistogram=histograms.createHistogram('timeToFirstPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintHistogram=histograms.createHistogram('timeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const onLoadHistogram=histograms.createHistogram('timeToOnload',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintHistogram=histograms.createHistogram('timeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractiveHistogram=histograms.createHistogram('timeToInteractive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to Interactive',summaryOptions:SUMMARY_OPTIONS,});const timeToFirstCpuIdleHistogram=histograms.createHistogram('timeToFirstCpuIdle',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to First CPU Idle',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;const samplesSet=collectLoadingMetricsForRenderer(rendererHelper);addSamplesToHistogram(samplesSet.firstPaintSamples,firstPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintSamples,firstContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.onLoadSamples,onLoadHistogram,histograms);} +function loadingMetric(histograms,model){const firstPaintHistogram=histograms.createHistogram('timeToFirstPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintHistogram=histograms.createHistogram('timeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const onLoadHistogram=histograms.createHistogram('timeToOnload',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintHistogram=histograms.createHistogram('timeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractiveHistogram=histograms.createHistogram('timeToInteractive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to Interactive',summaryOptions:SUMMARY_OPTIONS,});const timeToFirstCpuIdleHistogram=histograms.createHistogram('timeToFirstCpuIdle',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to First CPU Idle',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;const samplesSet=collectLoadingMetricsForRenderer(rendererHelper);addSamplesToHistogram(samplesSet.firstPaintSamples,firstPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintSamples,firstContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintCpuTimeSamples,firstContentfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.onLoadSamples,onLoadHistogram,histograms);} const samplesSet=collectMetricsFromLoadExpectations(model,chromeHelper);addSamplesToHistogram(samplesSet.firstMeaningfulPaintSamples,firstMeaningfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstMeaningfulPaintCpuTimeSamples,firstMeaningfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.interactiveSamples,timeToInteractiveHistogram,histograms);addSamplesToHistogram(samplesSet.firstCpuIdleSamples,timeToFirstCpuIdleHistogram,histograms);} tr.metrics.MetricRegistry.register(loadingMetric);return{loadingMetric,getNetworkEventsInRange,};});'use strict';tr.exportTo('tr.metrics',function(){const SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY=tr.v.HistogramBinBoundaries.createExponential(1,1000,50);function spaNavigationMetric(histograms,model){const histogram=new tr.v.Histogram('spaNavigationStartToFpDuration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY);histogram.description='Latency between the input event causing'+' a SPA navigation and the first paint event after it';histogram.customizeSummaryOptions({count:false,sum:false,});const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper){return;} const rendererHelpers=modelHelper.rendererHelpers;if(!rendererHelpers){return;} @@ -8216,7 +8437,8 @@ lookupHistogram(guid){return this.histogramsByGuid_.get(guid);} lookupDiagnostic(guid){return this.sharedDiagnosticsByGuid_.get(guid);} resolveRelatedHistograms(){const handleDiagnosticMap=dm=>{for(const[name,diagnostic]of dm){if(diagnostic instanceof tr.v.d.RelatedHistogramMap){diagnostic.resolve(this);}}};for(const hist of this){handleDiagnosticMap(hist.diagnostics);for(const dm of hist.nanDiagnosticMaps){handleDiagnosticMap(dm);} for(const bin of hist.allBins){for(const dm of bin.diagnosticMaps){handleDiagnosticMap(dm);}}}} -importDicts(dicts){for(const dict of dicts){if(dict.type&&tr.v.d.Diagnostic.findTypeInfoWithName(dict.type)){this.sharedDiagnosticsByGuid_.set(dict.guid,tr.v.d.Diagnostic.fromDict(dict));}else{const hist=tr.v.Histogram.fromDict(dict);this.addHistogram(hist);hist.diagnostics.resolveSharedDiagnostics(this,true);}}} +importDicts(dicts){for(const dict of dicts){this.importDict(dict);}} +importDict(dict){if(dict.type&&tr.v.d.Diagnostic.findTypeInfoWithName(dict.type)){this.sharedDiagnosticsByGuid_.set(dict.guid,tr.v.d.Diagnostic.fromDict(dict));}else{const hist=tr.v.Histogram.fromDict(dict);this.addHistogram(hist);hist.diagnostics.resolveSharedDiagnostics(this,true);}} asDicts(){const dicts=[];for(const diagnostic of this.sharedDiagnosticsByGuid_.values()){dicts.push(diagnostic.asDict());} for(const hist of this){dicts.push(hist.asDict());} return dicts;} @@ -8280,7 +8502,8 @@ return maxEQT;} return{getPostInteractiveTaskWindows,getNavStartTimestamps,getInteractiveTimestamps,expectedQueueingTime,maxExpectedQueueingTimeInSlidingWindow,weightedExpectedQueueingTime};});'use strict';tr.exportTo('tr.metrics.sh',function(){const WINDOW_SIZE_MS=500;const EQT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.01,WINDOW_SIZE_MS,50);function containsForcedGC_(slice){return slice.findTopmostSlicesRelativeToThisSlice(tr.metrics.v8.utils.isForcedGarbageCollectionEvent).length>0;} function createHistogramForEQT_(name,description){const histogram=new tr.v.Histogram(name,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,EQT_BOUNDARIES);histogram.customizeSummaryOptions({avg:false,count:false,max:true,min:false,std:false,sum:false,});histogram.description=description;return histogram;} function expectedQueueingTimeMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=Object.values(chromeHelper.rendererHelpers);const rendererToInteractiveTimestamps=tr.e.chrome.getInteractiveTimestamps(model);addExpectedQueueingTimeMetric_('renderer_eqt',event=>{return{start:event.start,duration:event.duration};},false,rendererHelpers,rendererToInteractiveTimestamps,histograms,model);addExpectedQueueingTimeMetric_('renderer_eqt_cpu',event=>{return{start:event.cpuStart,duration:event.cpuDuration};},true,rendererHelpers,rendererToInteractiveTimestamps,histograms,model);} -function addExpectedQueueingTimeMetric_(eqtName,getEventTimes,isCpuTime,rendererHelpers,rendererToInteractiveTimestamps,histograms,model){function getTasks(rendererHelper){const tasks=[];for(const slice of rendererHelper.mainThread.sliceGroup.topLevelSlices){const times=getEventTimes(slice);if(times.duration>0&&!containsForcedGC_(slice)){tasks.push({start:times.start,end:times.start+times.duration});}} +function addExpectedQueueingTimeMetric_(eqtName,getEventTimes,isCpuTime,rendererHelpers,rendererToInteractiveTimestamps,histograms,model){function getTasks(rendererHelper){const tasks=[];for(const slice of +tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread)){const times=getEventTimes(slice);if(times.duration>0&&!containsForcedGC_(slice)){tasks.push({start:times.start,end:times.start+times.duration});}} return tasks;} const totalHistogram=createHistogramForEQT_(`total:${WINDOW_SIZE_MS}ms_window:${eqtName}`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer');const interactiveHistogram=createHistogramForEQT_(`interactive:${WINDOW_SIZE_MS}ms_window:${eqtName}`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer while the page is interactive');for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread.bounds.duration<WINDOW_SIZE_MS)continue;const tasks=getTasks(rendererHelper);totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks));const interactiveTimestamps=rendererToInteractiveTimestamps.get(rendererHelper.pid);if(interactiveTimestamps.length===0)continue;if(interactiveTimestamps.length>1){continue;} const interactiveWindow=tr.b.math.Range.fromExplicitRange(interactiveTimestamps[0],Infinity).findIntersection(rendererHelper.mainThread.bounds);interactiveHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(interactiveWindow.min,interactiveWindow.max,WINDOW_SIZE_MS,tasks));} @@ -8380,7 +8603,8 @@ typeToSize[entry.objectTypeName]+=entry.size;} let largestValue=0;let largestType='';for(const key in typeToSize){if(largestValue<typeToSize[key]){largestValue=typeToSize[key];largestType=key;}} addProcessScalar({source:'reported_by_chrome',component:[allocatorName,largestType],property:HEAP_CATEGORY_SIZE,value:largestValue});}} function addV8MemoryDumpValues(processDump,addProcessScalar){const v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)return;v8Dump.children.forEach(function(isolateDump){const mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);} -const heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.code_and_metadata_size});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.bytecode_and_metadata_size});} +let heapDump=isolateDump.getDescendantDumpByFullName('heap');if(heapDump===undefined){heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');} +if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.code_and_metadata_size});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.bytecode_and_metadata_size});} function addV8ComponentValues(componentDump,componentPath,addProcessScalar){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:componentPath,property,value:componentDump.numerics[property.name]});});} const PROCESS_COUNT={unit:count_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){if(componentPath.length>0){throw new Error('Unexpected process count non-empty component path: '+ componentPath.join(':'));} @@ -8391,7 +8615,7 @@ nameParts.push(componentPath.join(':'));nameParts.push(formatSpec.userFriendlyPr nameParts.push(formatSpec.userFriendlyPropertyName);nameParts.push(formatSpec.componentPreposition);if(componentPath[componentPath.length-1]==='allocated_by_malloc'){nameParts.push('objects allocated by malloc for');nameParts.push(componentPath.slice(0,componentPath.length-1).join(':'));}else{nameParts.push(componentPath.join(':'));}} nameParts.push('in');} nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');} -const RESIDENT_SIZE={name:'resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'resident set size (RSS)');}};const PEAK_RESIDENT_SIZE={name:'peak_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'peak resident set size');}};const PROPORTIONAL_RESIDENT_SIZE={name:'proportional_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'proportional resident size (PSS)');}};const PRIVATE_DIRTY_SIZE={name:'private_dirty_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private dirty size');}};const PRIVATE_FOOTPRINT_SIZE={name:'private_footprint_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private footprint size');}};const NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT={name:'native_library_private_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library private clean resident size');}};const NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT={name:'native_library_shared_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library shared clean resident size');}};const NATIVE_LIBRARY_PROPORTIONAL_RESIDENT={name:'native_library_proportional_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library proportional resident size');}};function buildOsValueDescriptionPrefix(componentPath,processName,userFriendlyPropertyName){if(componentPath.length>2){throw new Error('OS value component path for \''+ +const RESIDENT_SIZE={name:'resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'resident set size (RSS)');}};const PEAK_RESIDENT_SIZE={name:'peak_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'peak resident set size');}};const PROPORTIONAL_RESIDENT_SIZE={name:'proportional_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'proportional resident size (PSS)');}};const PRIVATE_DIRTY_SIZE={name:'private_dirty_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private dirty size');}};const PRIVATE_FOOTPRINT_SIZE={name:'private_footprint_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private footprint size');}};const JAVA_BASE_CLEAN_RESIDENT={name:'java_base_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'java base odex and vdex total clean resident size');}};const JAVA_BASE_PSS={name:'java_base_pss',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'java base odex and vdex proportional resident size');}};const NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT={name:'native_library_private_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library private clean resident size');}};const NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT={name:'native_library_shared_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library shared clean resident size');}};const NATIVE_LIBRARY_PROPORTIONAL_RESIDENT={name:'native_library_proportional_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library proportional resident size');}};function buildOsValueDescriptionPrefix(componentPath,processName,userFriendlyPropertyName){if(componentPath.length>2){throw new Error('OS value component path for \''+ userFriendlyPropertyName+'\' too long: '+componentPath.join(':'));} const nameParts=[];if(componentPath.length<2){nameParts.push('total');} nameParts.push(userFriendlyPropertyName);if(componentPath.length>0){switch(componentPath[0]){case'system_memory':if(componentPath.length>1){const userFriendlyComponentName=SYSTEM_VALUE_COMPONENTS[componentPath[1]].userFriendlyName;if(userFriendlyComponentName===undefined){throw new Error('System value sub-component for \''+ @@ -8404,9 +8628,11 @@ userFriendlyPropertyName+'\' unknown: '+ componentPath.join(':'));}}else{nameParts.push('reported by the OS for');} nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');} function addDetailedMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,g=>g.levelOfDetail===DETAILED,function(processDump,addProcessScalar){for(const[componentName,componentSpec]of -Object.entries(SYSTEM_VALUE_COMPONENTS)){const node=getDescendantVmRegionClassificationNode(processDump.vmRegions,componentSpec.classificationPath);const componentPath=['system_memory'];if(componentName)componentPath.push(componentName);addProcessScalar({source:'reported_by_os',component:componentPath,property:PROPORTIONAL_RESIDENT_SIZE,value:node===undefined?0:(node.byteStats.proportionalResident||0)});addProcessScalar({source:'reported_by_os',component:componentPath,property:PRIVATE_DIRTY_SIZE,value:node===undefined?0:(node.byteStats.privateDirtyResident||0)});if(node){if(node.byteStats.nativeLibraryPrivateCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT,value:node===undefined?0:(node.byteStats.nativeLibraryPrivateCleanResident||0)});} -if(node.byteStats.nativeLibrarySharedCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT,value:node===undefined?0:(node.byteStats.nativeLibrarySharedCleanResident||0)});} -if(node.byteStats.nativeLibraryProportionalResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PROPORTIONAL_RESIDENT,value:node===undefined?0:(node.byteStats.nativeLibraryProportionalResident||0)});}}} +Object.entries(SYSTEM_VALUE_COMPONENTS)){const node=getDescendantVmRegionClassificationNode(processDump.vmRegions,componentSpec.classificationPath);const componentPath=['system_memory'];if(componentName)componentPath.push(componentName);addProcessScalar({source:'reported_by_os',component:componentPath,property:PROPORTIONAL_RESIDENT_SIZE,value:node===undefined?0:(node.byteStats.proportionalResident||0)});addProcessScalar({source:'reported_by_os',component:componentPath,property:PRIVATE_DIRTY_SIZE,value:node===undefined?0:(node.byteStats.privateDirtyResident||0)});if(node){if(node.byteStats.javaBasePss){addProcessScalar({source:'reported_by_os',component:componentPath,property:JAVA_BASE_PSS,value:node.byteStats.javaBasePss});} +if(node.byteStats.javaBaseCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:JAVA_BASE_CLEAN_RESIDENT,value:node.byteStats.javaBaseCleanResident});}} +if(node){if(node.byteStats.nativeLibraryPrivateCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT,value:node.byteStats.nativeLibraryPrivateCleanResident});} +if(node.byteStats.nativeLibrarySharedCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT,value:node.byteStats.nativeLibrarySharedCleanResident});} +if(node.byteStats.nativeLibraryProportionalResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PROPORTIONAL_RESIDENT,value:node.byteStats.nativeLibraryProportionalResident});}}} const memtrackDump=processDump.getMemoryAllocatorDumpByFullName('gpu/android_memtrack');if(memtrackDump!==undefined){memtrackDump.children.forEach(function(memtrackChildDump){addProcessScalar({source:'reported_by_os',component:['gpu_memory',memtrackChildDump.name],property:PROPORTIONAL_RESIDENT_SIZE,value:memtrackChildDump.numerics.memtrack_pss});});}},function(componentTree){},values);} const SYSTEM_VALUE_COMPONENTS={'':{classificationPath:[],},'java_heap':{classificationPath:['Android','Java runtime','Spaces'],userFriendlyName:'the Java heap'},'ashmem':{classificationPath:['Android','Ashmem'],userFriendlyName:'ashmem'},'native_heap':{classificationPath:['Native heap'],userFriendlyName:'the native heap'},'stack':{classificationPath:['Stack'],userFriendlyName:'the thread stacks'}};function getDescendantVmRegionClassificationNode(node,path){for(let i=0;i<path.length;i++){if(node===undefined)break;node=node.children.find(c=>c.title===path[i]);} return node;} @@ -8512,21 +8738,26 @@ histograms.addHistogram(cpuTotalOptimizeCode);histograms.addHistogram(wallTotalO function computeDeoptimizeCodeMetrics(histograms,model){const cpuTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalDeoptimizeCode.description='cpu total time spent in code deoptimization';const wallTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalDeoptimizeCode.description='wall total time spent in code deoptimization';for(const e of model.findTopmostSlicesNamed('V8.DeoptimizeCode')){cpuTotalDeoptimizeCode.addSample(e.cpuDuration);wallTotalDeoptimizeCode.addSample(e.duration);} histograms.addHistogram(cpuTotalDeoptimizeCode);histograms.addHistogram(wallTotalDeoptimizeCode);} function executionMetric(histograms,model){computeExecuteMetrics(histograms,model);computeParseLazyMetrics(histograms,model);computeCompileIgnitionMetrics(histograms,model);computeCompileFullCodeMetrics(histograms,model);computeRecompileMetrics(histograms,model);computeOptimizeCodeMetrics(histograms,model);computeDeoptimizeCodeMetrics(histograms,model);} -tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const TARGET_FPS=60;const MS_PER_SECOND=1000;const WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addDurationOfSubEvents(histograms,model);addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);} +tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const TARGET_FPS=60;const MS_PER_SECOND=1000;const WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addDurationOfSubEvents(histograms,model);addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);addMarkCompactorMutatorUtilization(histograms,model);addTotalMarkCompactorTime(histograms,model);addTotalMarkCompactorMarkingTime(histograms,model);} tr.metrics.MetricRegistry.register(gcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;} function createNumericForSubEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:false,percentile:[0.90]});return n;} function createNumericForIdleTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;} function createPercentage(name,numerator,denominator,unit){const hist=new tr.v.Histogram(name,unit);if(denominator===0){hist.addSample(0);}else{hist.addSample(numerator/denominator);} hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,percentile:[]});return hist;} -function isNotForcedTopGarbageCollectionEvent(event){return tr.metrics.v8.utils.isTopGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);} -function isNotForcedSubGarbageCollectionEvent(event){return tr.metrics.v8.utils.isSubGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);} -function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} -function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} -function addDurationOfSubEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} -function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);});} -function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);});} +function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} +function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} +function isV8MarkCompactorSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorSummaryEvent(event);} +function isV8MarkCompactorMarkingSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorMarkingSummaryEvent(event);} +function createHistogramFromSummary(histograms,name,events){const foregroundDuration=createNumericForTopEventTime(name+'-foreground');const backgroundDuration=createNumericForTopEventTime(name+'-background');const totalDuration=createNumericForTopEventTime(name+'-total');const relatedNames=new tr.v.d.RelatedNameMap();relatedNames.set('foreground',foregroundDuration.name);relatedNames.set('background',backgroundDuration.name);for(const event of events){foregroundDuration.addSample(event.args.duration);backgroundDuration.addSample(event.args.background_duration);const breakdownForTotal=new tr.v.d.Breakdown();breakdownForTotal.set('foreground',event.args.duration);breakdownForTotal.set('background',event.args.background_duration);totalDuration.addSample(event.args.duration+event.args.background_duration,{breakdown:breakdownForTotal});} +histograms.addHistogram(foregroundDuration);histograms.addHistogram(backgroundDuration);histograms.addHistogram(totalDuration,{breakdown:relatedNames});} +function addTotalMarkCompactorTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorSummary,event=>'v8-gc-mark-compactor',(name,events)=>createHistogramFromSummary(histograms,name,events));} +function addTotalMarkCompactorMarkingTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorMarkingSummary,event=>'v8-gc-mark-compactor-marking',(name,events)=>createHistogramFromSummary(histograms,name,events));} +function addDurationOfSubEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});} +function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);});} +function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);});} function addPercentageInV8Execute(histograms,model,name,events){let cpuDurationInV8Execute=0;let cpuDurationTotal=0;events.forEach(function(event){const v8Execute=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isV8ExecuteEvent);if(v8Execute){cpuDurationInV8Execute+=event.cpuDuration;} cpuDurationTotal+=event.cpuDuration;});const percentage=createPercentage(name+'_percentage_in_v8_execute',cpuDurationInV8Execute,cpuDurationTotal,percentage_smallerIsBetter);histograms.addHistogram(percentage);} +function addMarkCompactorMutatorUtilization(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=Object.values(chromeHelper.rendererHelpers);tr.metrics.v8.utils.addMutatorUtilization('v8-gc-mark-compactor-mmu',tr.metrics.v8.utils.isNotForcedMarkCompactorEvent,[100],rendererHelpers,histograms);} return{gcMetric,WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const COUNT_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1000000,50);const DURATION_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.1,10000,50);const SUMMARY_OPTIONS={std:false,count:false,sum:false,min:false,max:false,};function computeDomContentLoadedTime_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let domContentLoadedTime=0;for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title==='domContentLoadedEventEnd'&&ev.start>domContentLoadedTime){domContentLoadedTime=ev.start;}}} return domContentLoadedTime;} function computeInteractiveTime_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let interactiveTime=0;for(const expectation of model.userModel.expectations){if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;} @@ -8558,7 +8789,7 @@ computeRuntimeStatsBucketOnUE(histograms,v8ThreadSlices,v8SlicesBucketOnUEMap);} tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric);tr.metrics.MetricRegistry.register(runtimeStatsMetric);return{runtimeStatsMetric,runtimeStatsTotalMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(histograms,model){tr.metrics.v8.executionMetric(histograms,model);tr.metrics.v8.gcMetric(histograms,model);tr.metrics.sh.memoryMetric(histograms,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});} tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics,};});'use strict';tr.exportTo('tr.metrics.vr',function(){function createHistograms(histograms,name,options,hasCpuTime){const createdHistograms={wall:histograms.createHistogram(name+'_wall',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options)};if(hasCpuTime){createdHistograms.cpu=histograms.createHistogram(name+'_cpu',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options);} return createdHistograms;} -function frameCycleDurationMetric(histograms,model,opt_options){const histogramsByEventTitle=new Map();histogramsByEventTitle.set('VrShellGl::DrawFrame',createHistograms(histograms,'draw_frame',{description:'Duration to render one frame'},true));histogramsByEventTitle.set('VrShellGl::AcquireFrame',createHistograms(histograms,'acquire_frame',{description:'Duration acquire a frame from GVR'},true));histogramsByEventTitle.set('VrShellGl::UpdateController',createHistograms(histograms,'update_controller',{description:'Duration to query input from the controller'},true));histogramsByEventTitle.set('VrShellGl::DrawFrameSubmitNow',createHistograms(histograms,'submit_frame',{description:'Duration to submit a frame to GVR'},true));histogramsByEventTitle.set('VrShellGl::PostSubmitDrawOnGpu',createHistograms(histograms,'post_submit_draw_on_gpu',{description:'Duration to draw a frame on GPU post submit to '+'GVR. Note this duration may include time spent on '+'reprojection'},false));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateAnimationsAndOpacity',createHistograms(histograms,'update_animations_and_opacity',{description:'Duration to apply animation and opacity changes'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateBindings',createHistograms(histograms,'update_bindings',{description:'Duration to push binding values'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateLayout',createHistograms(histograms,'update_layout',{description:'Duration to compute element sizes, layout and textures'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateWorldSpaceTransform',createHistograms(histograms,'update_world_space_transforms',{description:'Duration to calculate element transforms in world space'},true));histogramsByEventTitle.set('UiRenderer::DrawUiView',createHistograms(histograms,'draw_ui',{description:'Duration to draw the UI'},true));histogramsByEventTitle.set('UiElementRenderer::DrawTexturedQuad',createHistograms(histograms,'draw_textured_quad',{description:'Duration to draw a textured element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientQuad',createHistograms(histograms,'draw_gradient_quad',{description:'Duration to draw a gradient element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientGridQuad',createHistograms(histograms,'draw_gradient_grid_quad',{description:'Duration to draw a gradient grid element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawController',createHistograms(histograms,'draw_controller',{description:'Duration to draw the controller'},true));histogramsByEventTitle.set('UiElementRenderer::DrawLaser',createHistograms(histograms,'draw_laser',{description:'Duration to draw the laser'},true));histogramsByEventTitle.set('UiElementRenderer::DrawReticle',createHistograms(histograms,'draw_reticle',{description:'Duration to draw the reticle'},true));histogramsByEventTitle.set('UiElementRenderer::DrawShadow',createHistograms(histograms,'draw_shadow',{description:'Duration to draw a shadow element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawStars',createHistograms(histograms,'draw_stars',{description:'Duration to draw the stars'},true));histogramsByEventTitle.set('UiElementRenderer::DrawBackground',createHistograms(histograms,'draw_background',{description:'Duration to draw the textured background'},true));histogramsByEventTitle.set('UiElementRenderer::DrawKeyboard',createHistograms(histograms,'draw_keyboard',{description:'Duration to draw the keyboard'},true));const drawUiSubSlicesMap=new Map();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let rangeOfInterest=model.bounds;const userExpectationsOfInterest=[tr.model.um.AnimationExpectation];if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;userExpectationsOfInterest.push(tr.model.um.ResponseExpectation);} +function frameCycleDurationMetric(histograms,model,opt_options){const histogramsByEventTitle=new Map();histogramsByEventTitle.set('Vr.DrawFrame',createHistograms(histograms,'draw_frame',{description:'Duration to render one frame'},true));histogramsByEventTitle.set('Vr.AcquireGvrFrame',createHistograms(histograms,'acquire_frame',{description:'Duration acquire a frame from GVR'},true));histogramsByEventTitle.set('Vr.ProcessControllerInput',createHistograms(histograms,'update_controller',{description:'Duration to query input from the controller'},true));histogramsByEventTitle.set('Vr.ProcessControllerInputForWebXr',createHistograms(histograms,'update_controller_webxr',{description:'Duration to query input from the controller '+'for WebXR'},true));histogramsByEventTitle.set('Vr.SubmitFrameNow',createHistograms(histograms,'submit_frame',{description:'Duration to submit a frame to GVR'},true));histogramsByEventTitle.set('Vr.PostSubmitDrawOnGpu',createHistograms(histograms,'post_submit_draw_on_gpu',{description:'Duration to draw a frame on GPU post submit to '+'GVR. Note this duration may include time spent on '+'reprojection'},false));histogramsByEventTitle.set('VrShellGl::DrawFrame',histogramsByEventTitle.get('Vr.DrawFrame'));histogramsByEventTitle.set('VrShellGl::AcquireFrame',histogramsByEventTitle.get('Vr.AcquireGvrFrame'));histogramsByEventTitle.set('VrShellGl::UpdateController',histogramsByEventTitle.get('Vr.ProcessControllerInput'));histogramsByEventTitle.set('VrShellGl::DrawFrameSubmitNow',histogramsByEventTitle.get('Vr.SubmitFrameNow'));histogramsByEventTitle.set('VrShellGl::PostSubmitDrawOnGpu',histogramsByEventTitle.get('Vr.PostSubmitDrawOnGpu'));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateAnimationsAndOpacity',createHistograms(histograms,'update_animations_and_opacity',{description:'Duration to apply animation and opacity changes'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateBindings',createHistograms(histograms,'update_bindings',{description:'Duration to push binding values'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateLayout',createHistograms(histograms,'update_layout',{description:'Duration to compute element sizes, layout and textures'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateWorldSpaceTransform',createHistograms(histograms,'update_world_space_transforms',{description:'Duration to calculate element transforms in world space'},true));histogramsByEventTitle.set('UiRenderer::DrawUiView',createHistograms(histograms,'draw_ui',{description:'Duration to draw the UI'},true));histogramsByEventTitle.set('UiElementRenderer::DrawTexturedQuad',createHistograms(histograms,'draw_textured_quad',{description:'Duration to draw a textured element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientQuad',createHistograms(histograms,'draw_gradient_quad',{description:'Duration to draw a gradient element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientGridQuad',createHistograms(histograms,'draw_gradient_grid_quad',{description:'Duration to draw a gradient grid element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawController',createHistograms(histograms,'draw_controller',{description:'Duration to draw the controller'},true));histogramsByEventTitle.set('UiElementRenderer::DrawLaser',createHistograms(histograms,'draw_laser',{description:'Duration to draw the laser'},true));histogramsByEventTitle.set('UiElementRenderer::DrawReticle',createHistograms(histograms,'draw_reticle',{description:'Duration to draw the reticle'},true));histogramsByEventTitle.set('UiElementRenderer::DrawShadow',createHistograms(histograms,'draw_shadow',{description:'Duration to draw a shadow element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawStars',createHistograms(histograms,'draw_stars',{description:'Duration to draw the stars'},true));histogramsByEventTitle.set('UiElementRenderer::DrawBackground',createHistograms(histograms,'draw_background',{description:'Duration to draw the textured background'},true));histogramsByEventTitle.set('UiElementRenderer::DrawKeyboard',createHistograms(histograms,'draw_keyboard',{description:'Duration to draw the keyboard'},true));const drawUiSubSlicesMap=new Map();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let rangeOfInterest=model.bounds;const userExpectationsOfInterest=[tr.model.um.AnimationExpectation];if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;userExpectationsOfInterest.push(tr.model.um.ResponseExpectation);} for(const ue of model.userModel.expectations){if(ue.initiatorType!==tr.model.um.INITIATOR_TYPE.VR){continue;} if(!userExpectationsOfInterest.some(function(ueOfInterest){return ue instanceof ueOfInterest;})){continue;} if(!rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){continue;} @@ -8862,11 +9093,17 @@ this.graphWidth);} this.margin.right=Math.max(this.margin.right,xAxisTickOverhangPx);},getXForDatum_(datum,index){return index;},get xAxisTickOffset(){return 0.5;},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;const nameTexts=xAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('transform',(d,index)=>'translate(0, '+ this.textHeightPx_*(this.data_.length-index)+')').attr('x',(d,index)=>this.xScale_(index)).attr('y',d=>this.graphHeight).text(d=>d.x);nameTexts.exit().remove();const guideLines=xAxis.selectAll('line.guide').data(this.data_);guideLines.enter().append('line').attr('x1',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('x2',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('y1',()=>this.graphHeight).attr('y2',(d,index)=>this.graphHeight+Math.max(MIN_GUIDELINE_HEIGHT_PX,(this.textHeightPx_*(this.data_.length-index-1))));}};return{NameColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const LineChart=tr.ui.b.LineChart;const NameLineChart=tr.ui.b.define('name-line-chart',LineChart);NameLineChart.prototype={__proto__:LineChart.prototype,getXForDatum_(datum,index){return index;},get xAxisHeight(){return 5+(this.textHeightPx_*this.data_.length);},get xAxisTickOffset(){return 0;},updateMargins_(){tr.ui.b.NameColumnChart.prototype.updateMargins_.call(this);},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);const baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length-1)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();}};return{NameLineChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const BoxChart=tr.ui.b.define('box-chart',tr.ui.b.NameLineChart);BoxChart.prototype={__proto__:tr.ui.b.NameLineChart.prototype,get hideLegend(){return true;},updateDataRange_(){if(this.overrideDataRange_!==undefined){return;} this.autoDataRange_.reset();for(const datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateScales_(){super.updateScales_();this.xScale_.domain([0,this.data_.length]);},get xAxisTickOffset(){return 0.5;},updateDataRange_(){if(this.overrideDataRange_!==undefined)return;this.autoDataRange_.reset();for(const datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);const baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const boxesSel=dataSel.selectAll('path');for(let index=0;index<this.data_.length;++index){const datum=this.data_[index];const color=datum.color||'black';let sel=boxesSel.data([datum]);sel.enter().append('rect').attr('fill',color).attr('x',this.xScale_(index+0.2)).attr('width',this.xScale_(index+0.8)-this.xScale_(index+0.2)).attr('y',this.yScale_(datum.percentile_75)).attr('height',this.yScale_(datum.percentile_25)- -this.yScale_(datum.percentile_75));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index)).attr('x2',this.xScale_(index+1)).attr('y1',this.yScale_(datum.percentile_50)).attr('y2',this.yScale_(datum.percentile_50));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_0)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_100));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.5)).attr('x2',this.xScale_(index+0.5)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();}}};return{BoxChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const BarChart=tr.ui.b.define('bar-chart',tr.ui.b.ColumnChart);BarChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,decorate(){super.decorate();this.verticalScale_=undefined;this.horizontalScale_=undefined;},updateScales_(){super.updateScales_();this.yScale_.range([this.graphWidth,0]);this.xScale_.range([0,this.graphHeight]);this.verticalScale_=this.isYLogScale_?d3.scale.log(10):d3.scale.linear();this.verticalScale_.domain(this.xScale_.domain());this.verticalScale_.range([this.graphHeight,0]);this.horizontalScale_=d3.scale.linear();this.horizontalScale_.domain(this.yScale_.domain());this.horizontalScale_.range([0,this.graphWidth]);},get defaultGraphHeight(){return Math.max(20,10*this.data_.length);},get defaultGraphWidth(){return 100;},get barHeight(){return this.graphHeight/this.data.length;},drawBrush_(brushRectsSel){brushRectsSel.attr('x',0).attr('width',this.graphWidth).attr('y',d=>this.verticalScale_(d.max)).attr('height',d=>this.verticalScale_(d.min)-this.verticalScale_(d.max)).attr('fill','rgb(213, 236, 229)');},getDataPointAtChartPoint_(chartPoint){const flippedPoint={x:this.graphHeight-chartPoint.y,y:this.graphWidth-chartPoint.x};return super.getDataPointAtChartPoint_(flippedPoint);},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.horizontalScale_).orient('bottom'));},get yAxisWidth(){return this.computeScaleTickWidth_(this.verticalScale_);},drawYAxis_(yAxis){const axisModifier=d3.svg.axis().scale(this.verticalScale_).orient('left');yAxis.call(axisModifier);},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;let xWidthPx=0;let xHeightPx=0;if(seriesKeys.length>1){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width;keyHeightPx=this.textHeightPx_;} -if(this.data.length>1){xWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,''+rect.datum.x).width;xHeightPx=this.textHeightPx_;} -const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.value).width;const valueHeightPx=this.textHeightPx_;const hoverWidthPx=Math.min(Math.max(keyWidthPx,xWidthPx,valueWidthPx)+5,Math.max(50,rect.widthPx));const hoverTopPx=rect.topPx+(rect.heightPx/2);const hoverLeftPx=rect.leftPx+rect.widthPx-hoverWidthPx;chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',keyHeightPx+xHeightPx+valueHeightPx);if(seriesKeys.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-3).text(rect.key);} -if(this.data.length>1){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+valueHeightPx-3).text(''+rect.datum.x);} -chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+xHeightPx+keyHeightPx+valueHeightPx-3).text(rect.value);},flipRect_(rect){return{datum:rect.datum,index:rect.index,key:rect.key,value:rect.value,color:rect.color,topPx:this.graphHeight-rect.leftPx-rect.widthPx,leftPx:this.graphWidth-rect.topPx-rect.heightPx,widthPx:rect.heightPx,heightPx:rect.widthPx,underflow:rect.underflow,overflow:rect.overflow,};},drawRect_(rect,sel){super.drawRect_(this.flipRect_(rect),sel);},drawUnderflow_(rect,rectsSel){let sel=rectsSel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',0).attr('y',this.graphHeight-rect.leftPx+ +this.yScale_(datum.percentile_75));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index)).attr('x2',this.xScale_(index+1)).attr('y1',this.yScale_(datum.percentile_50)).attr('y2',this.yScale_(datum.percentile_50));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_0)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_100));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.5)).attr('x2',this.xScale_(index+0.5)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();}}};return{BoxChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const BarChart=tr.ui.b.define('bar-chart',tr.ui.b.ColumnChart);BarChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,decorate(){super.decorate();this.verticalScale_=undefined;this.horizontalScale_=undefined;this.isWaterfall_=false;},updateScales_(){super.updateScales_();this.yScale_.range([this.graphWidth,0]);this.xScale_.range([0,this.graphHeight]);this.verticalScale_=this.isYLogScale_?d3.scale.log(10):d3.scale.linear();this.verticalScale_.domain(this.xScale_.domain());this.verticalScale_.range([this.graphHeight,0]);this.horizontalScale_=d3.scale.linear();this.horizontalScale_.domain(this.yScale_.domain());this.horizontalScale_.range([0,this.graphWidth]);},set isWaterfall(waterfall){this.isWaterfall_=waterfall;if(waterfall){this.getDataSeries('hide').color='transparent';} +this.updateContents_();},get isWaterfall(){return this.isWaterfall_;},get defaultGraphHeight(){return Math.max(20,10*this.data_.length);},get defaultGraphWidth(){return 100;},get barHeight(){return this.graphHeight/this.data.length;},drawBrush_(brushRectsSel){brushRectsSel.attr('x',0).attr('width',this.graphWidth).attr('y',d=>this.verticalScale_(d.max)).attr('height',d=>this.verticalScale_(d.min)-this.verticalScale_(d.max)).attr('fill','rgb(213, 236, 229)');},getDataPointAtChartPoint_(chartPoint){const flippedPoint={x:this.graphHeight-chartPoint.y,y:this.graphWidth-chartPoint.x};return super.getDataPointAtChartPoint_(flippedPoint);},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.horizontalScale_).orient('bottom'));},get yAxisWidth(){return this.computeScaleTickWidth_(this.verticalScale_);},drawYAxis_(yAxis){const axisModifier=d3.svg.axis().scale(this.verticalScale_).orient('left');yAxis.call(axisModifier);},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox||(this.isWaterfall_&&rect.key==='hide')){return;} +const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;let xWidthPx=0;let xHeightPx=0;let groupWidthPx=0;let groupHeightPx=0;if(seriesKeys.length>1&&!this.isWaterfall_){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width;keyHeightPx=this.textHeightPx_;} +if(this.data.length>1&&!this.isWaterfall_){xWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,''+rect.datum.x).width;xHeightPx=this.textHeightPx_;} +if(this.isGrouped&&rect.datum.group!==undefined){groupWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.group).width;groupHeightPx=this.textHeightPx_;} +const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.value).width;const valueHeightPx=this.textHeightPx_;const maxWidthPx=Math.max(keyWidthPx,xWidthPx,groupWidthPx,valueWidthPx)+5;const hoverWidthPx=this.isGrouped?maxWidthPx:Math.min(maxWidthPx,Math.max(50,rect.widthPx));const hoverTopPx=rect.topPx+(rect.heightPx/2);const hoverLeftPx=rect.leftPx+rect.widthPx-hoverWidthPx;chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',keyHeightPx+xHeightPx+ +valueHeightPx+groupHeightPx);if(seriesKeys.length>1&&!this.isWaterfall_){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-3).text(rect.key);} +if(this.data.length>1&&!this.isWaterfall_){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+valueHeightPx-3).text(''+rect.datum.x);} +if(this.isGrouped&&rect.datum.group!==undefined){chartAreaSel.append('text').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+xHeightPx+groupHeightPx-3).text(rect.datum.group);} +chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+xHeightPx+keyHeightPx+ +groupHeightPx+valueHeightPx-3).text(rect.value);},flipRect_(rect){return{datum:rect.datum,index:rect.index,key:rect.key,value:rect.value,color:rect.color,topPx:this.graphHeight-rect.leftPx-rect.widthPx,leftPx:this.graphWidth-rect.topPx-rect.heightPx,widthPx:rect.heightPx,heightPx:rect.widthPx,underflow:rect.underflow,overflow:rect.overflow,};},drawRect_(rect,sel){super.drawRect_(this.flipRect_(rect),sel);},drawUnderflow_(rect,rectsSel){let sel=rectsSel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',0).attr('y',this.graphHeight-rect.leftPx+ 3+(rect.widthPx/2));sel.exit().remove();sel=rectsSel.data([rect]);sel.enter().append('rect').attr('fill','rgba(0, 0, 0, 0)').attr('x',0).attr('y',this.graphHeight-rect.leftPx-rect.widthPx).attr('width',10).attr('height',rect.widthPx).on('mouseenter',()=>this.drawHoverValueBox_(this.flipRect_(rect))).on('mouseleave',()=>this.clearHoverValueBox_(rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',this.graphWidth).attr('y',this.graphHeight-rect.leftPx+ 3+(rect.widthPx/2));sel.exit().remove();}};return{BarChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const NameBarChart=tr.ui.b.define('name-bar-chart',tr.ui.b.BarChart);const Y_AXIS_PADDING=2;NameBarChart.prototype={__proto__:tr.ui.b.BarChart.prototype,getDataPointAtChartPoint_(chartPoint){return{x:tr.ui.b.BarChart.prototype.getDataPointAtChartPoint_.call(this,chartPoint).x,y:parseInt(Math.floor((this.graphHeight-chartPoint.y)/this.barHeight))};},getXForDatum_(datum,index){return index;},get yAxisWidth(){if(this.data.length===0)return 0;return Y_AXIS_PADDING+tr.b.math.Statistics.max(this.data_,d=>tr.ui.b.getSVGTextSize(this,d.x).width);},get defaultGraphHeight(){return(3+this.textHeightPx_)*this.data.length;},updateYAxis_(yAxis){if(tr.ui.b.getSVGTextSize(this,'test').width===0){tr.b.requestAnimationFrame(()=>this.updateYAxis_(yAxis));return;} yAxis.selectAll('*').remove();const nameTexts=yAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('x',d=>-(tr.ui.b.getSVGTextSize(this,d.x).width+Y_AXIS_PADDING)).attr('y',(d,index)=>this.verticalScale_(index)).text(d=>d.x);nameTexts.exit().remove();let previousTop=undefined;for(const text of nameTexts[0]){const bbox=text.getBBox();if((previousTop===undefined)||(previousTop>(bbox.y+bbox.height))){previousTop=bbox.y;}else{text.style.opacity=0;}}}};return{NameBarChart,};});'use strict';tr.exportTo('tr.v.ui',function(){const DIAGNOSTIC_SPAN_BEHAVIOR={created(){this.diagnostic_=undefined;this.name_=undefined;this.histogram_=undefined;},attached(){if(this.diagnostic_)this.updateContents_();},get diagnostic(){return this.diagnostic_;},build(diagnostic,name,histogram){this.diagnostic_=diagnostic;this.name_=name;this.histogram_=histogram;if(this.isAttached)this.updateContents_();},updateContents_(){throw new Error('dom-modules must override updateContents_()');}};return{DIAGNOSTIC_SPAN_BEHAVIOR,};});'use strict';tr.exportTo('tr.v.ui',function(){const DEFAULT_COLOR_SCHEME=new tr.b.SinebowColorGenerator();function getHistogramName(histogram,diagnosticName,key){if(histogram===undefined)return undefined;const nameMap=histogram.diagnostics.get(diagnosticName);if(nameMap===undefined)return undefined;return nameMap.get(key);} @@ -9275,9 +9512,9 @@ return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);},collaps h=h*0.5;}}else{for(let i=0;i<this.tracks.length;++i){this.tracks[i].height=this.tracks[0].height;this.tracks[i].style.display='';}}}};return{ThreadTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const OtherThreadsTrack=tr.ui.b.define('other-threads-track',tr.ui.tracks.OtherThreadsTrack);const SpacingTrack=tr.ui.tracks.SpacingTrack;OtherThreadsTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.header_=document.createElement('tr-ui-b-heading');this.header_.addEventListener('click',this.onHeaderClick_.bind(this));this.header_.heading='Other Threads';this.header_.tooltip='Threads with only scheduling information';this.header_.arrowVisible=true;this.threads_=[];this.expanded=false;this.collapsible_=true;},set threads(threads){this.threads_=threads;this.updateContents_();},set collapsible(collapsible){this.collapsible_=collapsible;this.updateContents_();},onHeaderClick_(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},get expanded(){return this.header_.expanded;},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;this.header_.expanded=expanded;this.viewport_.dispatchChangeEvent();this.updateContents_();},updateContents_(){this.detach();if(this.collapsible_){Polymer.dom(this).appendChild(this.header_);} if(this.expanded||!this.collapsible_){for(const thread of this.threads_){const track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}}}};return{OtherThreadsTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const ProcessSummaryTrack=tr.ui.b.define('process-summary-track',tr.ui.tracks.RectTrack);ProcessSummaryTrack.buildRectsFromProcess=function(process){if(!process)return[];const ops=[];const pushOp=function(isStart,time,slice){ops.push({isStart,time,slice});};for(const tid in process.threads){const sliceGroup=process.threads[tid].sliceGroup;sliceGroup.topLevelSlices.forEach(function(slice){pushOp(true,slice.start,undefined);pushOp(false,slice.end,undefined);});sliceGroup.slices.forEach(function(slice){if(slice.important){pushOp(true,slice.start,slice);pushOp(false,slice.end,slice);}});} ops.sort(function(a,b){return a.time-b.time;});const rects=[];const genericColorId=ColorScheme.getColorIdForReservedName('generic_work');const pushRect=function(start,end,slice){rects.push(new tr.ui.tracks.Rect(slice,slice?slice.title:'',slice?slice.colorId:genericColorId,start,end-start));};let depth=0;let currentSlice=undefined;let lastStart=undefined;ops.forEach(function(op){depth+=op.isStart?1:-1;if(currentSlice){if(!op.isStart&&op.slice===currentSlice){pushRect(lastStart,op.time,currentSlice);lastStart=depth>=1?op.time:undefined;currentSlice=undefined;}}else{if(op.isStart){if(depth===1){lastStart=op.time;currentSlice=op.slice;}else if(op.slice){if(op.time!==lastStart){pushRect(lastStart,op.time,undefined);lastStart=op.time;} -currentSlice=op.slice;}}else{if(depth===0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;const ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;const SpacingTrack=tr.ui.tracks.SpacingTrack;const ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){const modelSettings=new tr.model.ModelSettings(this.processBase_.model);const defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);} -this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;const modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get hasVisibleContent(){if(this.expanded){return this.children.length>1;} -return true;},onHeaderClick_(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},updateContents_(){this.clearTracks_();if(!this.processBase_)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();} +currentSlice=op.slice;}}else{if(depth===0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;const ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;const SpacingTrack=tr.ui.tracks.SpacingTrack;const ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.closeEl_=tr.ui.b.createSpan();Polymer.dom(this.closeEl_).classList.add('process-track-close');this.closeEl_.textContent='X';this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);Polymer.dom(this.headerEl_).appendChild(this.closeEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){const modelSettings=new tr.model.ModelSettings(this.processBase_.model);const defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);} +this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;const modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},set visible(visible){if(visible===this.visible)return;this.hidden=!visible;tr.b.dispatchSimpleEvent(this,'visibility');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get visible(){return!this.hidden;},get hasVisibleContent(){if(this.expanded){return this.children.length>1;} +return true;},onHeaderClick_(e){e.stopPropagation();e.preventDefault();if(e.target===this.closeEl_){this.visible=false;}else{this.expanded=!this.expanded;}},updateContents_(){this.clearTracks_();if(!this.processBase_)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();} this.didAppendTracks_();},willAppendTracks_(){},didAppendTracks_(){},appendMemoryDumpTrack_(){},appendSummaryTrack_(){const track=new tr.ui.tracks.ProcessSummaryTrack(this.viewport);track.process=this.process;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendFrameTrack_(){const frames=this.process?this.process.frames:undefined;if(!frames||!frames.length)return;const track=new tr.ui.tracks.FrameTrack(this.viewport);track.frames=frames;Polymer.dom(this).appendChild(track);},appendObjectInstanceTracks_(){const instancesByTypeName=this.processBase_.objects.getAllInstancesByTypeName();const instanceTypeNames=Object.keys(instancesByTypeName);instanceTypeNames.sort();let didAppendAtLeastOneTrack=false;instanceTypeNames.forEach(function(typeName){const allInstances=instancesByTypeName[typeName];let instanceViewInfo=ObjectInstanceView.getTypeInfo(undefined,typeName);let snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(instanceViewInfo&&!instanceViewInfo.metadata.showInTrackView){instanceViewInfo=undefined;} if(snapshotViewInfo&&!snapshotViewInfo.metadata.showInTrackView){snapshotViewInfo=undefined;} const hasViewInfo=instanceViewInfo||snapshotViewInfo;const visibleInstances=[];for(let i=0;i<allInstances.length;i++){const instance=allInstances[i];if(instance.snapshots.length===0)continue;if(instance.hasImplicitSnapshots&&!hasViewInfo)continue;visibleInstances.push(instance);} @@ -9292,7 +9529,7 @@ tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground draw=!draw;if(!draw)continue;const bounds=this.children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_(){const processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){const pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);} const instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.processBase.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.processBase.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ProcessTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SelectionState=tr.model.SelectionState;const ColorScheme=tr.b.ColorScheme;const EventPresenter=tr.ui.b.EventPresenter;const ModelTrack=tr.ui.b.define('model-track',tr.ui.tracks.ContainerTrack);ModelTrack.VSYNC_HIGHLIGHT_ALPHA=0.1;ModelTrack.VSYNC_DENSITY_TRANSPARENT=0.20;ModelTrack.VSYNC_DENSITY_OPAQUE=0.10;ModelTrack.VSYNC_DENSITY_RANGE=ModelTrack.VSYNC_DENSITY_TRANSPARENT-ModelTrack.VSYNC_DENSITY_OPAQUE;ModelTrack.generateStripes_=function(times,minTime,maxTime){if(times.length===0)return[];const lowIndex=tr.b.findLowIndexInSortedArray(times,(x=>x),minTime);let highIndex=lowIndex-1;while(times[highIndex+1]<=maxTime){highIndex++;} const stripes=[];for(let i=lowIndex-(lowIndex%2);i<=highIndex;i+=2){const left=i<lowIndex?minTime:times[i];const right=i+1>highIndex?maxTime:times[i+1];stripes.push(tr.b.math.Range.fromExplicitRange(left,right));} -return stripes;};ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');this.upperMode_=false;this.annotationViews_=[];this.vSyncTimes_=[];},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_(){Polymer.dom(this).textContent='';if(!this.model_)return;if(this.upperMode_){this.updateContentsForUpperMode_();}else{this.updateContentsForLowerMode_();}},updateContentsForUpperMode_(){},updateContentsForLowerMode_(){if(this.model_.userModel.expectations.length>1){const mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);} +return stripes;};ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');this.upperMode_=false;this.annotationViews_=[];this.vSyncTimes_=[];},get processViews(){return Polymer.dom(this).querySelectorAll('.process-track-base');},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_(){Polymer.dom(this).textContent='';if(!this.model_)return;if(this.upperMode_){this.updateContentsForUpperMode_();}else{this.updateContentsForLowerMode_();}},updateContentsForUpperMode_(){},updateContentsForLowerMode_(){if(this.model_.userModel.expectations.length>1){const mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);} if(this.model_.alerts.length){const at=new tr.ui.tracks.AlertTrack(this.viewport_);at.alerts=this.model_.alerts;Polymer.dom(this).appendChild(at);} if(this.model_.globalMemoryDumps.length){const gmdt=new tr.ui.tracks.GlobalMemoryDumpTrack(this.viewport_);gmdt.memoryDumps=this.model_.globalMemoryDumps;Polymer.dom(this).appendChild(gmdt);} this.appendDeviceTrack_();this.appendCpuUsageTrack_();this.appendMemoryTrack_();this.appendKernelTrack_();const processes=this.model_.getAllProcesses();processes.sort(tr.model.Process.compare);for(let i=0;i<processes.length;++i){const process=processes[i];const track=new tr.ui.tracks.ProcessTrack(this.viewport);track.process=process;if(!track.hasVisibleContent)continue;Polymer.dom(this).appendChild(track);} @@ -9319,7 +9556,7 @@ const leftMarker=interestRange.min;const rightMarker=interestRange.max;const lef ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);ctx.strokeStyle=arrowColor;ctx.beginPath();tr.ui.b.drawLine(ctx,leftMarkerView,arrowPosY,rightMarkerView,arrowPosY);ctx.stroke();ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftMarkerView-1.5*arrowSpacing,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightMarkerView+1.5*arrowSpacing,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}else if(spaceForArrowsView<=distanceBetweenMarkersView){let leftArrowStart;let rightArrowStart;if(spaceForArrowsAndTextView<=distanceBetweenMarkersView){ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);leftArrowStart=textLeftView-arrowSpacing;rightArrowStart=textRightView+arrowSpacing;}else{leftArrowStart=positionInMiddleOfMarkersView;rightArrowStart=positionInMiddleOfMarkersView;} ctx.strokeStyle=arrowColor;ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftArrowStart,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightArrowStart,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);} ctx.restore();},drawMarkers_(viewLWorld,viewRWorld){const pixelRatio=window.devicePixelRatio||1;const trackBounds=this.getBoundingClientRect();const viewHeight=trackBounds.height*pixelRatio;if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(this.context(),viewLWorld,viewRWorld,viewHeight);}},addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection){},addAllEventsMatchingFilterToSelection(filter,selection){}};return{XAxisTrack,};});'use strict';Polymer({is:'tr-ui-timeline-track-view',ready(){this.displayTransform_=new tr.ui.TimelineDisplayTransform();this.model_=undefined;this.timelineView_=undefined;this.pollIfViewportAttachedInterval_=undefined;this.viewport_=new tr.ui.TimelineViewport(this);this.viewportDisplayTransformAtMouseDown_=undefined;this.brushingStateController_=undefined;this.rulerTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.rulerTrackContainer_);this.rulerTrackContainer_.invalidate();this.rulerTrackContainer_.style.overflowY='hidden';this.rulerTrackContainer_.style.flexShrink='0';this.rulerTrack_=new tr.ui.tracks.XAxisTrack(this.viewport_);Polymer.dom(this.rulerTrackContainer_).appendChild(this.rulerTrack_);this.upperModelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);this.upperModelTrack_.upperMode=true;Polymer.dom(this.rulerTrackContainer_).appendChild(this.upperModelTrack_);this.modelTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.modelTrackContainer_);this.modelTrackContainer_.style.display='block';this.modelTrackContainer_.style.flexGrow='1';this.modelTrackContainer_.invalidate();this.viewport_.modelTrackContainer=this.modelTrackContainer_;this.modelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);Polymer.dom(this.modelTrackContainer_).appendChild(this.modelTrack_);this.timingTool_=new tr.ui.b.TimingTool(this.viewport_,this);this.initMouseModeSelector();this.hideDragBox_();this.initHintText_();this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.addEventListener('dblclick',this.onDblClick_);this.onMouseWheel_=this.onMouseWheel_.bind(this);this.addEventListener('mousewheel',this.onMouseWheel_);this.onMouseDown_=this.onMouseDown_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.onMouseMove_=this.onMouseMove_.bind(this);this.addEventListener('mousemove',this.onMouseMove_);this.onTouchStart_=this.onTouchStart_.bind(this);this.addEventListener('touchstart',this.onTouchStart_);this.onTouchMove_=this.onTouchMove_.bind(this);this.addEventListener('touchmove',this.onTouchMove_);this.onTouchEnd_=this.onTouchEnd_.bind(this);this.addEventListener('touchend',this.onTouchEnd_);this.addHotKeys_();this.mouseViewPosAtMouseDown_={x:0,y:0};this.lastMouseViewPos_={x:0,y:0};this.lastTouchViewPositions_=[];this.alert_=undefined;this.isPanningAndScanning_=false;this.isZooming_=false;},initMouseModeSelector(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this;Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.addEventListener('beginpan',this.onBeginPanScan_.bind(this));this.mouseModeSelector_.addEventListener('updatepan',this.onUpdatePanScan_.bind(this));this.mouseModeSelector_.addEventListener('endpan',this.onEndPanScan_.bind(this));this.mouseModeSelector_.addEventListener('beginselection',this.onBeginSelection_.bind(this));this.mouseModeSelector_.addEventListener('updateselection',this.onUpdateSelection_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onEndSelection_.bind(this));this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));this.mouseModeSelector_.addEventListener('entertiming',this.timingTool_.onEnterTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('begintiming',this.timingTool_.onBeginTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('updatetiming',this.timingTool_.onUpdateTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('endtiming',this.timingTool_.onEndTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('exittiming',this.timingTool_.onExitTiming.bind(this.timingTool_));const m=tr.ui.b.MOUSE_SELECTOR_MODE;this.mouseModeSelector_.supportedModeMask=m.SELECTION|m.PANSCAN|m.ZOOM|m.TIMING;this.mouseModeSelector_.settingsKey='timelineTrackView.mouseModeSelector';this.mouseModeSelector_.setKeyCodeForMode(m.PANSCAN,'2'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.SELECTION,'1'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.ZOOM,'3'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.TIMING,'4'.charCodeAt(0));this.mouseModeSelector_.setModifierForAlternateMode(m.SELECTION,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(m.PANSCAN,tr.ui.b.MODIFIER.SPACE);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);} -this.brushingStateController_=brushingStateController;if(this.brushingStateController_){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);}},set timelineView(view){this.timelineView_=view;},onSelectionChanged_(){this.showHintText_('Press \'m\' to mark current selection');this.viewport_.dispatchChangeEvent();},set selection(selection){throw new Error('DO NOT CALL THIS');},set highlight(highlight){throw new Error('DO NOT CALL THIS');},detach(){this.modelTrack_.detach();this.upperModelTrack_.detach();if(this.pollIfViewportAttachedInterval_){window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;} +this.brushingStateController_=brushingStateController;if(this.brushingStateController_){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);}},set timelineView(view){this.timelineView_=view;},get processViews(){return this.modelTrack_.processViews;},onSelectionChanged_(){this.showHintText_('Press \'m\' to mark current selection');this.viewport_.dispatchChangeEvent();},set selection(selection){throw new Error('DO NOT CALL THIS');},set highlight(highlight){throw new Error('DO NOT CALL THIS');},detach(){this.modelTrack_.detach();this.upperModelTrack_.detach();if(this.pollIfViewportAttachedInterval_){window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;} this.viewport_.detach();},get viewport(){return this.viewport_;},get model(){return this.model_;},set model(model){if(!model){throw new Error('Model cannot be undefined');} const modelInstanceChanged=this.model_!==model;this.model_=model;this.modelTrack_.model=model;this.upperModelTrack_.model=model;if(modelInstanceChanged){this.pollIfViewportAttachedInterval_=window.setInterval(this.pollIfViewportAttached_.bind(this),250);}},get hasVisibleContent(){return this.modelTrack_.hasVisibleContent||this.upperModelTrack_.hasVisibleContent;},pollIfViewportAttached_(){if(!this.viewport_.isAttachedToDocumentOrInTestMode||this.viewport_.clientWidth===0){return;} window.addEventListener('resize',this.viewport_.dispatchChangeEvent);window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;this.setInitialViewport_();},setInitialViewport_(){this.modelTrackContainer_.updateCanvasSizeIfNeeded_();const w=this.modelTrackContainer_.canvas.width;let min;let range;if(this.model_.bounds.isEmpty){min=0;range=1000;}else if(this.model_.bounds.range===0){min=this.model_.bounds.min;range=1000;}else{min=this.model_.bounds.min;range=this.model_.bounds.range;} @@ -9395,13 +9632,14 @@ if(previouslyActivePanelType&&supportedPanelTypes.includes(previouslyActivePanel Polymer.dom(this).removeAttribute('expanded');}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){if(range===undefined){throw new Error('Must not be undefined');} this.rangeOfInterest_=range;if(this.activePanel){this.activePanel.rangeOfInterest=range;}}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready(){const mod=tr.isMac?'cmd ':'ctrl';const spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(let i=0;i<spans.length;i++){Polymer.dom(spans[i]).textContent=mod;}}});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created(){this.metadata_=undefined;},ready(){this.$.table.tableColumns=[{title:'name',value:d=>d.name,},{title:'value',value:d=>{const gov=document.createElement('tr-ui-a-generic-object-view');gov.object=d.value;return gov;},}];},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.table.tableRows=this.metadata_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready(){this.preferredTimeDisplayMode_=undefined;},attached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';Polymer({is:'tr-ui-timeline-view',created(){this.trackViewContainer_=undefined;this.queuedModel_=undefined;this.builtPromise_=undefined;this.doneBuilding_=undefined;},attached(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_){throw new Error('missing trackviewContainer');} if(this.queuedModel_)this.updateContents_();});},ready(){this.tabIndex=0;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;} -this.optionsDropdown_=this.$.view_options_dropdown;Polymer.dom(this.optionsDropdown_.iconElement).textContent='View Options';this.showFlowEvents_=false;Polymer.dom(this.optionsDropdown_).appendChild(tr.ui.b.createCheckBox(this,'showFlowEvents','tr.ui.TimelineView.showFlowEvents',false,'Flow events'));this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){const sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},updateDocumentFavicon(){let hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;} +this.processFilter_=this.$.process_filter_dropdown;this.optionsDropdown_=this.$.view_options_dropdown;Polymer.dom(this.optionsDropdown_.iconElement).textContent='View Options';this.showFlowEvents_=false;Polymer.dom(this.optionsDropdown_).appendChild(tr.ui.b.createCheckBox(this,'showFlowEvents','tr.ui.TimelineView.showFlowEvents',false,'Flow events'));this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){const sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},updateDocumentFavicon(){let hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;} let faviconData=tr.ui.b.FaviconsByHue[hue];if(faviconData===undefined){faviconData=tr.ui.b.FaviconsByHue.blue;} let link=Polymer.dom(document.head).querySelector('link[rel="shortcut icon"]');if(!link){link=document.createElement('link');link.rel='shortcut icon';Polymer.dom(document.head).appendChild(link);} link.href=faviconData;},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;if(!this.trackView_)return;this.trackView_.viewport.showFlowEvents=showFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_(){const helpButtonEl=this.$.view_help_button;const dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();} helpButtonEl.addEventListener('click',onClick.bind(this));},initConsoleButton_(){const toggleEl=this.$.view_console_button;function onClick(e){this.scriptingCtl_.toggleVisibility();e.stopPropagation();return false;} toggleEl.addEventListener('click',onClick.bind(this));},initMetadataButton_(){const showEl=this.$.view_metadata_button;function onClick(e){const dlg=new tr.ui.b.Overlay();dlg.title='Metadata for trace';const metadataOverlay=document.createElement('tr-ui-timeline-view-metadata-overlay');metadataOverlay.metadata=this.model.metadata;Polymer.dom(dlg).appendChild(metadataOverlay);dlg.visible=true;e.stopPropagation();return false;} -showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_(){const showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},get leftControls(){return this.leftControlsEl_;},get rightControls(){return this.rightControlsEl_;},get collapsingControls(){return this.collapsingControlsEl_;},get viewTitle(){return Polymer.dom(this.titleEl_).textContent.substring(Polymer.dom(this.titleEl_).textContent.length-2);},set viewTitle(text){if(text===undefined){Polymer.dom(this.titleEl_).textContent='';this.titleEl_.hidden=true;return;} +showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_(){const showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},updateProcessList_(){const dropdown=Polymer.dom(this.processFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);} +if(!this.model)return;const trackView=this.trackViewContainer_.querySelector('tr-ui-timeline-track-view');const processViews=trackView.processViews;const cboxes=[];const updateAll=(checked)=>{for(const cbox of cboxes){cbox.checked=checked;}};dropdown.appendChild(tr.ui.b.createButton('All',()=>updateAll(true)));dropdown.appendChild(tr.ui.b.createButton('None',()=>updateAll(false)));for(const view of processViews){const cbox=tr.ui.b.createCheckBox(undefined,undefined,undefined,true,view.processBase.userFriendlyName,()=>view.visible=cbox.checked);cbox.checked=view.visible;cboxes.push(cbox);view.addEventListener('visibility',()=>cbox.checked=view.visible);dropdown.appendChild(cbox);}},get leftControls(){return this.leftControlsEl_;},get rightControls(){return this.rightControlsEl_;},get collapsingControls(){return this.collapsingControlsEl_;},get viewTitle(){return Polymer.dom(this.titleEl_).textContent.substring(Polymer.dom(this.titleEl_).textContent.length-2);},set viewTitle(text){if(text===undefined){Polymer.dom(this.titleEl_).textContent='';this.titleEl_.hidden=true;return;} this.titleEl_.hidden=false;Polymer.dom(this.titleEl_).textContent=text;},get model(){if(this.trackView_){return this.trackView_.model;} return undefined;},set model(model){this.build(model);},async build(model){this.queuedModel_=model;this.builtPromise_=new Promise((resolve,reject)=>{this.doneBuilding_=resolve;});if(this.trackViewContainer_)await this.updateContents_();},get builtPromise(){return this.builtPromise_;},async updateContents_(){if(this.trackViewContainer_===undefined){throw new Error('timeline-view.updateContents_ requires trackViewContainer_');} const model=this.queuedModel_;this.queuedModel_=undefined;const modelInstanceChanged=model!==this.model;const modelValid=model&&!model.bounds.isEmpty;const importWarningsEl=Polymer.dom(this.root).querySelector('#import-warnings');Polymer.dom(importWarningsEl).textContent='';if(modelInstanceChanged){if(this.railScoreSpan_){this.railScoreSpan_.model=undefined;} @@ -9411,7 +9649,7 @@ if(modelValid&&!this.trackView_){this.trackView_=document.createElement('tr-ui-t if(modelValid){this.trackView_.model=model;this.trackView_.viewport.showFlowEvents=this.showFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_){this.railScoreSpan_.model=model;} this.$.display_unit.preferredTimeDisplayMode=model.intrinsicTimeUnit;} if(model){for(const warning of model.importWarningsThatShouldBeShownToUser){importWarningsEl.addMessage(`Import Warning: ${warning.type}: ${warning.message}`,[{buttonText:'Dismiss',onClick(event,infobar){infobar.visible=false;}}]);}} -if(modelInstanceChanged){this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();} +if(modelInstanceChanged){this.updateProcessList_();this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();} this.doneBuilding_();},get brushingStateController(){return this.brushingStateController_;},get trackView(){return this.trackView_;},get settings(){if(!this.settings_){this.settings_=new tr.b.Settings();} return this.settings_;},set focusElement(value){throw new Error('This is deprecated. Please set globalMode to true.');},bindKeyListeners_(){const hkc=this.hotkeyController;hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'`'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){this.scriptingCtl_.toggleVisibility();if(!this.scriptingCtl_.hasFocus){this.focus();} e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'/'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){if(this.scriptingCtl_.hasFocus)return;if(this.findCtl_.hasFocus){this.focus();}else{this.findCtl_.focus();} @@ -9541,8 +9779,8 @@ get statisticNames(){return Array.from(this.statisticNames_);} get labels(){const displayLabels=Array.from(this.labelsToStartTimes_.keys());displayLabels.sort((x,y)=>this.labelsToStartTimes_.get(x)-this.labelsToStartTimes_.get(y));return displayLabels;} get possibleGroupings(){for(const[key,values]of this.keysToValues_){if(values.size>=2)continue;this.keysToGroupings_.delete(key);} return Array.from(this.keysToGroupings_.values());}} -return{HistogramParameterCollector,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-controls-export',exportRawCsv_(){this.export_(false,'csv');},exportRawJson_(){this.export_(false,'json');},exportMergedCsv_(){this.export_(true,'csv');},exportMergedJson_(){this.export_(true,'json');},export_(merged,format){tr.b.dispatchSimpleEvent(this,'export',true,true,{merged,format});},});return{};});'use strict';tr.exportTo('tr.v.ui',function(){const ALPHA_OPTIONS=[];for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-3);for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-2);ALPHA_OPTIONS.push(0.1);Polymer({is:'tr-v-ui-histogram-set-controls',properties:{searchQuery:{type:String,value:'',observer:'onUserChange_',},showAll:{type:Boolean,value:true,observer:'onUserChange_',},referenceDisplayLabel:{type:String,value:'',observer:'onUserChange_',},displayStatisticName:{type:String,value:'',observer:'onUserChange_',},alphaString:{type:String,computed:'getAlphaString_(alphaIndex)',},alphaIndex:{type:Number,value:9,observer:'onUserChange_',},},created(){this.viewState_=undefined;this.rowListener_=this.onRowViewStateUpdate_.bind(this);this.baseStatisticNames_=[];this.isInOnViewStateUpdate_=false;},ready(){this.$.picker.addEventListener('current-groups-changed',this.onGroupsChanged_.bind(this));},get viewState(){return this.viewState_;},set viewState(vs){if(this.viewState_){throw new Error('viewState must be set exactly once.');} -this.viewState_=vs;this.viewState.addUpdateListener(this.onViewStateUpdate_.bind(this));},async onUserChange_(){if(!this.viewState)return;if(this.isInOnViewStateUpdate_)return;const marks=[];if(this.searchQuery!==this.viewState.searchQuery){marks.push(tr.b.Timing.mark('histogram-set-controls','search'));} +return{HistogramParameterCollector,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-controls-export',exportRawCsv_(){this.export_(false,'csv');},exportRawJson_(){this.export_(false,'json');},exportMergedCsv_(){this.export_(true,'csv');},exportMergedJson_(){this.export_(true,'json');},export_(merged,format){tr.b.dispatchSimpleEvent(this,'export',true,true,{merged,format});},});return{};});'use strict';tr.exportTo('tr.v.ui',function(){const ALPHA_OPTIONS=[];for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-3);for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-2);ALPHA_OPTIONS.push(0.1);Polymer({is:'tr-v-ui-histogram-set-controls',properties:{searchQuery:{type:String,value:'',observer:'onSearchQueryChange_',},showAll:{type:Boolean,value:true,observer:'onUserChange_',},referenceDisplayLabel:{type:String,value:'',observer:'onUserChange_',},displayStatisticName:{type:String,value:'',observer:'onUserChange_',},alphaString:{type:String,computed:'getAlphaString_(alphaIndex)',},alphaIndex:{type:Number,value:9,observer:'onUserChange_',},},created(){this.viewState_=undefined;this.rowListener_=this.onRowViewStateUpdate_.bind(this);this.baseStatisticNames_=[];this.isInOnViewStateUpdate_=false;this.searchQueryDebounceMs=200;},ready(){this.$.picker.addEventListener('current-groups-changed',this.onGroupsChanged_.bind(this));},get viewState(){return this.viewState_;},set viewState(vs){if(this.viewState_){throw new Error('viewState must be set exactly once.');} +this.viewState_=vs;this.viewState.addUpdateListener(this.onViewStateUpdate_.bind(this));},async onSearchQueryChange_(){if(this.searchQueryDebounceMs===0)return this.onUserChange_();this.debounce('onSearchQueryDebounce',this.onUserChange_,this.searchQueryDebounceMs);},async onUserChange_(){if(!this.viewState)return;if(this.isInOnViewStateUpdate_)return;const marks=[];if(this.searchQuery!==this.viewState.searchQuery){marks.push(tr.b.Timing.mark('histogram-set-controls','search'));} if(this.showAll!==this.viewState.showAll){marks.push(tr.b.Timing.mark('histogram-set-controls','showAll'));} if(this.referenceDisplayLabel!==this.viewState.referenceDisplayLabel){marks.push(tr.b.Timing.mark('histogram-set-controls','referenceColumn'));} if(this.displayStatisticName!==this.viewState.displayStatisticName){marks.push(tr.b.Timing.mark('histogram-set-controls','statistic'));} @@ -9567,7 +9805,7 @@ for(const name of names){const option=document.createElement('option');option.te if(names.includes(this.viewState.displayStatisticName)){this.displayStatisticName=this.viewState.displayStatisticName;this.$.statistic.value=this.displayStatisticName;}else{this.viewState.displayStatisticName=names[0]||'';}},get anyOverviewCharts_(){for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){if(row.isOverviewed)return true;} return false;},async toggleOverviewLineCharts_(){const showOverviews=!this.anyOverviewCharts_;const mark=tr.b.Timing.mark('histogram-set-controls',(showOverviews?'show':'hide')+'OverviewCharts');for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){await row.update({isOverviewed:showOverviews});} this.$.hide_overview.style.display=showOverviews?'inline':'none';this.$.show_overview.style.display=showOverviews?'none':'inline';await tr.b.animationFrame();mark.end();},set helpHref(href){this.$.help.href=href;this.$.help.style.display='inline';},set feedbackHref(href){this.$.feedback.href=href;this.$.feedback.style.display='inline';},clearSearch_(){this.set('searchQuery','');this.$.search.focus();},getAlphaString_(alphaIndex){return(''+ALPHA_OPTIONS[alphaIndex]).substr(0,5);},openAlphaSlider_(){const alphaButtonRect=this.$.alpha.getBoundingClientRect();this.$.alpha_slider_container.style.display='flex';this.$.alpha_slider_container.style.top=alphaButtonRect.bottom+'px';this.$.alpha_slider_container.style.left=alphaButtonRect.left+'px';this.$.alpha_slider.focus();},closeAlphaSlider_(){this.$.alpha_slider_container.style.display='';},updateAlpha_(){this.alphaIndex=this.$.alpha_slider.value;},getAlphaIndexFromViewState_(){for(let i=0;i<ALPHA_OPTIONS.length;++i){if(ALPHA_OPTIONS[i]>=this.viewState.alpha)return i;} -return ALPHA_OPTIONS.length-1;},});return{};});'use strict';tr.exportTo('tr.v',function(){function deleteMergedToDiagnostics(histogramArrayMap){for(const[name,histograms]of histogramArrayMap){if(histograms instanceof Array){for(const histogram of histograms){histogram.diagnostics.delete(tr.v.d.RESERVED_NAMES.MERGED_TO);}}else if(histograms instanceof Map){deleteMergedToDiagnostics(histograms);}}} +return ALPHA_OPTIONS.length-1;},set enableVisualization(enable){this.$.show_visualization.style.display=enable?'inline':'none';},loadVisualization_(){tr.b.dispatchSimpleEvent(this,'loadVisualization',true,true,{});},});return{};});'use strict';tr.exportTo('tr.v',function(){function deleteMergedToDiagnostics(histogramArrayMap){for(const[name,histograms]of histogramArrayMap){if(histograms instanceof Array){for(const histogram of histograms){histogram.diagnostics.delete(tr.v.d.RESERVED_NAMES.MERGED_TO);}}else if(histograms instanceof Map){deleteMergedToDiagnostics(histograms);}}} class HistogramSetHierarchy{constructor(name){this.name=name;this.description='';this.depth=0;this.subRows=[];this.columns=new Map();this.mergeRelationshipsForColumn_=new Map();}*walk(){yield this;for(const row of this.subRows)yield*row.walk();} static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();} static build(histogramArrayMap){const rootRows=[];HistogramSetHierarchy.buildInternal_(histogramArrayMap,[],rootRows);const histograms=new tr.v.HistogramSet();for(const row of HistogramSetHierarchy.walkAll(rootRows)){for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}} @@ -9676,11 +9914,32 @@ if(event.delta.tableRowStates){if(this.tableRows_.length!==this.viewState.tableR for(const row of this.tableRows_){if(this.viewState.tableRowStates.get(row.name)!==row.viewState){throw new Error('Only histogram-set-table may update tableRowStates');}}} await this.updateContents_();},onSortColumnChanged_(event){tr.b.Timing.instant('histogram-set-table','sortColumn');this.viewState.update({sortColumnIndex:event.sortColumnIndex,sortDescending:event.sortDescending,});},onRequestSelectionChange_(event){if(event.selection instanceof tr.model.EventSet)return;event.stopPropagation();tr.b.Timing.instant('histogram-set-table','selectHistogramNames');let histogramNames=event.selection;histogramNames.sort();histogramNames=histogramNames.map(escapeRegExp).join('|');this.viewState.update({showAll:true,searchQuery:`^(${histogramNames})$`,});},get leafHistograms(){const histograms=new tr.v.HistogramSet();for(const row of tr.v.ui.HistogramSetTableRow.walkAll(this.$.table.tableRows)){if(row.subRows.length)continue;for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}} -return histograms;}});return{MIDLINE_HORIZONTAL_ELLIPSIS,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',listeners:{export:'onExport_',},created(){this.brushingStateController_=new tr.ui.NullBrushingStateController();this.viewState_=new tr.v.ui.HistogramSetViewState();},ready(){this.$.table.viewState=this.viewState;this.$.controls.viewState=this.viewState;},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},get brushingStateController(){return this.brushingStateController_;},get viewState(){return this.viewState_;},get histograms(){return this.$.table.histograms;},async build(histograms,opt_options){const options=opt_options||{};const progress=options.progress||(()=>Promise.resolve());if(options.helpHref)this.$.controls.helpHref=options.helpHref;if(options.feedbackHref){this.$.controls.feedbackHref=options.feedbackHref;} +return histograms;}});return{MIDLINE_HORIZONTAL_ELLIPSIS,};});'use strict';const COLORS=[['#FFD740','#FFC400','#FFAB00','#E29800'],['#FF6E40','#FF3D00','#DD2C00','#A32000'],['#40C4FF','#00B0FF','#0091EA','#006DAF'],['#89C641','#54B503','#4AA510','#377A0D'],['#B388FF','#7C4DFF','#651FFF','#6200EA'],['#FF80AB','#FF4081','#F50057','#C51162'],['#FFAB40','#FF9100','#FF6D00','#D65C02'],['#8C9EFF','#536DFE','#3D5AFE','#304FFE']];const METRICS=new Map([['Pipeline',['pipeline:begin_frame_transport','pipeline:begin_frame_to_frame_submission','pipeline:frame_submission_to_display','pipeline:draw']],['Thread',['thread_browser_cpu_time_per_frame','thread_display_compositor_cpu_time_per_frame','thread_GPU_cpu_time_per_frame','thread_IO_cpu_time_per_frame','thread_other_cpu_time_per_frame','thread_raster_cpu_time_per_frame','thread_renderer_compositor_cpu_time_per_frame','thread_renderer_main_cpu_time_per_frame']]]);const STATISTIC_KEY='statistic';Polymer({is:'tr-v-ui-metrics-visualization',created(){this.sortedPages_=new Map();this.displayLabels_=new Map();this.groups_=new Map();this.charts_=new Map();this.subMetricNames_=new Map();},build(leafHistograms,histograms){if(!leafHistograms||leafHistograms.length<1||!histograms||histograms.length<1){this.$.data_error.style.display='block';return;} +this.histograms=histograms;for(const key of METRICS.keys()){const newAggregateChart=this.initializeColumnChart(key);Polymer.dom(this.$.container).appendChild(newAggregateChart);this.populateChart_(leafHistograms,key,newAggregateChart,true);newAggregateChart.toolTipCallBack=()=>this.openMetricsChart_(key);} +if(this.groups_.size<1){this.$.data_error.style.display='block';return;}},populateChart_(histograms,metricName,chart,aggregate){const storiesGrouping=tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.STORIES);const benchmarkStartGrouping=tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.BENCHMARK_START);const labels=new Map();const groups=new Map();for(const metric of METRICS.get(metricName)){const metricHistograms=histograms.getHistogramsNamed(metric);for(const histogram of metricHistograms){const benchmarkStart=benchmarkStartGrouping.callback(histogram);const page=storiesGrouping.callback(histogram);const displayLabel=tr.v.HistogramGrouping.DISPLAY_LABEL.callback(histogram);const average=histogram.average===undefined?0:histogram.average;labels.set(displayLabel,Date.parse(benchmarkStart));let pageMap=new Map();let labelMap=new Map();if(groups.get(page)===undefined){groups.set(page,pageMap);pageMap.set(displayLabel,labelMap);}else{pageMap=groups.get(page);if(pageMap.get(displayLabel)===undefined){pageMap.set(displayLabel,labelMap);}else{labelMap=pageMap.get(displayLabel);}} +let metricMap=new Map();let statistic=new tr.b.math.RunningStatistics();if(labelMap.has(metric)){metricMap=labelMap.get(metric);statistic=metricMap.get(STATISTIC_KEY);} +statistic.add(average);metricMap.set(STATISTIC_KEY,statistic);labelMap.set(metric,metricMap);const merged=new tr.v.d.DiagnosticMap();for(const bin of histogram.allBins){for(const map of bin.diagnosticMaps){merged.addDiagnostics(map);}} +const subMetrics=[];if(merged.get('breakdown')===undefined){metricMap.set(metric,statistic);subMetrics.push(metric);}else{for(const[subMetric,total]of merged.get('breakdown')){let subStatistic=new tr.b.math.RunningStatistics();if(metricMap.has(subMetric)){subStatistic=metricMap.get(subMetric);} +subStatistic.add(total);metricMap.set(subMetric,subStatistic);subMetrics.push(subMetric);}} +const prevSubMetrics=this.subMetricNames_.get(metric);if(!prevSubMetrics||subMetrics.length>prevSubMetrics.length){this.subMetricNames_.set(metric,subMetrics);}}} +const displayLabels=this.getSortedDisplayLabels_(labels);const sortedPages=[...groups.keys()].sort((a,b)=>this.sortGroups_(a,b,groups,displayLabels,METRICS.get(metricName)));this.setChartColors_(METRICS.get(metricName),displayLabels,chart);this.setChartSize_(groups.size,displayLabels.length,chart);this.setChartData_(groups,sortedPages,displayLabels,chart);let mapName=metricName;if(aggregate){mapName='Aggregate '+mapName;} +this.groups_.set(mapName,groups);this.displayLabels_.set(mapName,displayLabels);this.sortedPages_.set(mapName,sortedPages);},getSortedDisplayLabels_(labels){return Array.from(labels.keys()).sort((a,b)=>labels.get(a)-labels.get(b));},sortGroups_(a,b,groups,displayLabels,mainMetricNames){let aValue=0;const aMap=groups.get(a);if(aMap.get(displayLabels[0])!==undefined){for(const metricName of mainMetricNames){const aMetricMap=aMap.get(displayLabels[0]).get(metricName);aValue+=aMetricMap.get(STATISTIC_KEY).mean;}} +let bValue=0;const bMap=groups.get(b);if(bMap.get(displayLabels[0])!==undefined){for(const metricName of mainMetricNames){const bMetricMap=bMap.get(displayLabels[0]).get(metricName);bValue+=bMetricMap.get(STATISTIC_KEY).mean;}} +if(aValue===bValue)return 0;return(aValue<bValue)?-1:1;},setChartData_(groups,sortedPages,displayLabels,chart){const chartData=[];for(const page of sortedPages){const pageMap=groups.get(page);for(const label of displayLabels){const data={x:label,group:page};if(!pageMap.has(label))continue;const labelMap=pageMap.get(label);for(const[metric,metricMap]of labelMap){const key=this.getSeriesKey_(metric,label);const mean=metricMap.get(STATISTIC_KEY).mean;data[key]=Math.round(mean*100)/100;if(label===displayLabels[0]){chart.getDataSeries(key).title=metric;}else{chart.getDataSeries(key).title='';}} +chartData.push(data);} +chartData.push({});} +chart.data=chartData;},setChartSize_(pageCount,displayLabelCount,chart){chart.graphHeight=tr.b.math.clamp(pageCount*displayLabelCount*20,300,600);chart.graphWidth=tr.b.math.clamp(pageCount*displayLabelCount*25,500,1000);},setChartColors_(mainMetricNames,displayLabels,chart){for(let i=0;i<mainMetricNames.length;++i){const mainMetric=mainMetricNames[i];for(let j=0;j<displayLabels.length;++j){const mainColorIndex=i%COLORS.length;const subColorIndex=j%COLORS[mainColorIndex].length;const color=COLORS[mainColorIndex][subColorIndex];const displayLabel=displayLabels[j];const series=this.getSeriesKey_(mainMetric,displayLabel);chart.getDataSeries(series).color=color;}}},getSeriesKey_(metricName,displayLabel){return metricName+'-'+displayLabel;},initializeColumnChart(title){const newChart=new tr.ui.b.NameColumnChart();newChart.isStacked=true;newChart.yAxisLabel='ms';newChart.hideXAxis=true;newChart.displayXInHover=true;newChart.isGrouped=true;newChart.enableToolTip=true;newChart.showTitleInLegend=true;newChart.chartTitle=title;newChart.titleHeight='14pt';return newChart;},initializeChildChart_(title,height,width){const childChart=new tr.ui.b.NameBarChart();childChart.xAxisLabel='ms';childChart.chartTitle=title;childChart.graphHeight=height;childChart.graphWidth=width;childChart.titleHeight='14pt';childChart.isStacked=true;childChart.hideLegend=true;childChart.isGrouped=true;childChart.isWaterfall=true;return childChart;},initializeCloseButton_(div,parent){const button=this.$.close.cloneNode(true);button.style.display='inline-block';button.addEventListener('click',()=>{Polymer.dom(parent).removeChild(div);});return button;},initializeSelectors_(mainStep){const select=this.$.selectors.cloneNode(true);select.style.display='block';Polymer.dom(select).querySelector('#start').addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_(select,mainStep);});Polymer.dom(select).querySelector('#end').addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_(select,mainStep);});Polymer.dom(select).querySelector('#filter').addEventListener('click',()=>{this.filterByPercentile_(select,mainStep);});Polymer.dom(select).querySelector('#search_page').addEventListener('keydown',(e)=>{if(e.key==='Enter')this.searchByPage_(select,mainStep);});Polymer.dom(select).querySelector('#search').addEventListener('click',()=>{this.searchByPage_(select,mainStep);});return select;},openMetricsChart_(mainStep){const div=document.createElement('div');div.classList.add('child_container');Polymer.dom(this.$.container).appendChild(div);const selectors=this.initializeSelectors_(mainStep);div.appendChild(selectors);const childChart=this.initializeColumnChart(mainStep+' Breakdown');childChart.toolTipCallBack=(rect)=>this.openChildChart_(rect,mainStep);div.appendChild(childChart);const button=this.initializeCloseButton_(div,this.$.container);div.appendChild(button);this.populateChart_(this.histograms,mainStep,childChart,false);this.charts_.set(mainStep,childChart);},initializeChild_(title,height,width){const div=document.createElement('div');div.classList.add('child_container');Polymer.dom(this.$.children).insertBefore(div,this.$.children.firstChild);const childChart=this.initializeChildChart_(title,height,width);div.appendChild(childChart);const button=this.initializeCloseButton_(div,this.$.children);div.appendChild(button);return childChart;},openChildChart_(rect,metricName){let mainStep;let mainStepIndex;for(let i=0;i<METRICS.get(metricName).length;++i){if(rect.key.startsWith(METRICS.get(metricName)[i])){mainStep=METRICS.get(metricName)[i];mainStepIndex=i;break;}} +const subSteps=this.subMetricNames_.get(mainStep);const width=tr.b.math.clamp(subSteps.length*150,300,700);const height=tr.b.math.clamp(subSteps.length*this.displayLabels_.get(metricName).length*50,300,700);const pageName=rect.datum.group;const title=mainStep+': '+pageName;const childChart=this.initializeChild_(title,height,width);const pageData=this.groups_.get(metricName).get(pageName);const subStepData=this.processSubStepData(childChart,mainStepIndex,mainStep,pageData,pageName,0);childChart.data=subStepData.childData;},processSubStepData(childChart,mainStepIndex,mainStep,pageData,pageName,total){const childData=[];const subSteps=this.subMetricNames_.get(mainStep);if(subSteps===undefined)return{childData,total};for(const subStep of subSteps){const currentTotal=total;let j=0;for(const[displayLabel,labelMap]of pageData){const data={x:subStep,hide:currentTotal,group:displayLabel};const metricMap=labelMap.get(mainStep);const series=this.getSeriesKey_(subStep,displayLabel);const mean=metricMap.get(subStep).mean;const roundedMean=Math.round(mean*100)/100;data[series]=roundedMean===undefined?0:roundedMean;childData.push(data);if(j===0)total+=data[series];else data.x='.';const subColorIndex=j%COLORS[mainStepIndex].length;const color=COLORS[mainStepIndex][subColorIndex];childChart.getDataSeries(series).color=color;j++;} +childData.push({x:'.'});} +return{childData,total};},filterByPercentile_(select,metricName){const startPercentile=Polymer.dom(select).querySelector('#start').value;const endPercentile=Polymer.dom(select).querySelector('#end').value;if(startPercentile===''||endPercentile==='')return;const length=this.sortedPages_.get(metricName).length;const startIndex=this.getPercentileIndex_(startPercentile,length);const endIndex=this.getPercentileIndex_(endPercentile,length);const slicedPages=this.sortedPages_.get(metricName).slice(startIndex,endIndex);this.setChartData_(this.groups_.get(metricName),slicedPages,this.displayLabels_.get(metricName),this.charts_.get(metricName));},getPercentileIndex_(percentile,arrayLength){const index=Math.ceil(arrayLength*(percentile/100.0));if(index===-1)return 0;if(index>=arrayLength)return arrayLength;return index;},searchByPage_(select,metricName){const criteria=Polymer.dom(select).querySelector('#search_page').value;if(criteria==='')return;const query=new RegExp(criteria);const filteredGroups=[...this.groups_.get(metricName)].filter(group=>group[0].match(query));if(filteredGroups.length<1){Polymer.dom(select).querySelector('#search_error').style.display='block';return;} +Polymer.dom(select).querySelector('#search_error').style.display='none';const matchedPageName=filteredGroups[0][0];const match=this.groups_.get(metricName).get(matchedPageName);const title=metricName+' Breakdown: '+matchedPageName;let totalSteps=0;for(const[mainStep,subSteps]of this.subMetricNames_){totalSteps+=subSteps.length;} +const width=tr.b.math.clamp(totalSteps*150,300,700);const height=tr.b.math.clamp(totalSteps*this.displayLabels_.get(metricName).length*30,300,700);const childChart=this.initializeChild_(title,height,width);const childData=[];let total=0;for(let i=0;i<METRICS.get(metricName).length;++i){const stepData=this.processSubStepData(childChart,i,METRICS.get(metricName)[i],match,matchedPageName,total);childData.push(...stepData.childData);total=stepData.total;} +childChart.data=childData;},});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',listeners:{export:'onExport_',loadVisualization:'onLoadVisualization_'},created(){this.brushingStateController_=new tr.ui.NullBrushingStateController();this.viewState_=new tr.v.ui.HistogramSetViewState();this.metricsVisualizationLoaded_=false;},ready(){this.$.table.viewState=this.viewState;this.$.controls.viewState=this.viewState;},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},get brushingStateController(){return this.brushingStateController_;},get viewState(){return this.viewState_;},get histograms(){return this.$.table.histograms;},async build(histograms,opt_options){const options=opt_options||{};const progress=options.progress||(()=>Promise.resolve());if(options.helpHref)this.$.controls.helpHref=options.helpHref;if(options.feedbackHref){this.$.controls.feedbackHref=options.feedbackHref;} if(histograms===undefined||histograms.length===0){this.$.container.style.display='none';this.$.zero.style.display='block';this.style.display='block';return;} -this.$.zero.style.display='none';this.$.container.style.display='block';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';const buildMark=tr.b.Timing.mark('histogram-set-view','build');await progress('Finding important Histograms...');const sourceHistogramsMark=tr.b.Timing.mark('histogram-set-view','sourceHistograms');const sourceHistograms=histograms.sourceHistograms;sourceHistogramsMark.end();this.$.controls.showAllEnabled=(sourceHistograms.length!==histograms.length);await progress('Collecting parameters...');const collectParametersMark=tr.b.Timing.mark('histogram-set-view','collectParameters');const parameterCollector=new tr.v.HistogramParameterCollector();parameterCollector.process(histograms);this.$.controls.baseStatisticNames=parameterCollector.statisticNames;this.$.controls.possibleGroupings=parameterCollector.possibleGroupings;const displayLabels=parameterCollector.labels;this.$.controls.displayLabels=displayLabels;collectParametersMark.end();await this.$.table.build(histograms,sourceHistograms,displayLabels,progress);buildMark.end();},onExport_(event){const mark=tr.b.Timing.mark('histogram-set-view','export'+ +this.$.zero.style.display='none';this.$.container.style.display='block';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';const buildMark=tr.b.Timing.mark('histogram-set-view','build');await progress('Finding important Histograms...');const sourceHistogramsMark=tr.b.Timing.mark('histogram-set-view','sourceHistograms');const sourceHistograms=histograms.sourceHistograms;sourceHistogramsMark.end();this.$.controls.showAllEnabled=(sourceHistograms.length!==histograms.length);await progress('Collecting parameters...');const collectParametersMark=tr.b.Timing.mark('histogram-set-view','collectParameters');const parameterCollector=new tr.v.HistogramParameterCollector();parameterCollector.process(histograms);this.$.controls.baseStatisticNames=parameterCollector.statisticNames;this.$.controls.possibleGroupings=parameterCollector.possibleGroupings;const displayLabels=parameterCollector.labels;this.$.controls.displayLabels=displayLabels;collectParametersMark.end();const hist=[...histograms][0];const benchmarks=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARKS);let enable=false;if(benchmarks!==undefined&&benchmarks.length>0){for(const benchmark of benchmarks){if(benchmark.includes('rendering')){enable=true;break;}}} +this.$.controls.enableVisualization=enable;await this.$.table.build(histograms,sourceHistograms,displayLabels,progress);buildMark.end();},onExport_(event){const mark=tr.b.Timing.mark('histogram-set-view','export'+ (event.merged?'Merged':'Raw')+event.format.toUpperCase());const histograms=event.merged?this.$.table.leafHistograms:this.histograms;let blob;if(event.format==='csv'){const csv=new tr.v.CSVBuilder(histograms);csv.build();blob=new window.Blob([csv.toString()],{type:'text/csv'});}else if(event.format==='json'){blob=new window.Blob([JSON.stringify(histograms.asDicts())],{type:'text/json'});}else{throw new Error(`Unable to export format "${event.format}"`);} -const path=window.location.pathname.split('/');const basename=path[path.length-1].split('.')[0]||'histograms';const anchor=document.createElement('a');anchor.download=`${basename}.${event.format}`;anchor.href=window.URL.createObjectURL(blob);anchor.click();mark.end();},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.metrics_.sort((x,y)=>x.label.localeCompare(y.label));this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';const metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);this.$.results.addEventListener('display-ready',()=>{this.$.results.style.display='';});},async build(model){this.model_=model;await this.updateContents_();},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel(m){if(!m){return{supported:false,reason:'No model available'};} +const path=window.location.pathname.split('/');const basename=path[path.length-1].split('.')[0]||'histograms';const anchor=document.createElement('a');anchor.download=`${basename}.${event.format}`;anchor.href=window.URL.createObjectURL(blob);anchor.click();mark.end();},onLoadVisualization_(event){if(!this.metricsVisualizationLoaded_){this.$.metrics.build(this.$.table.leafHistograms,this.histograms);this.metricsVisualizationLoaded_=true;}else if(this.$.metrics.style.display==='none'){this.$.metrics.style.display='block';}else{this.$.metrics.style.display='none';}},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.metrics_.sort((x,y)=>x.label.localeCompare(y.label));this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';const metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);this.$.results.addEventListener('display-ready',()=>{this.$.results.style.display='';});},async build(model){this.model_=model;await this.updateContents_();},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel(m){if(!m){return{supported:false,reason:'No model available'};} return{supported:true};},get model(){return this.model_;},set model(model){this.build(model);},get selection(){},set selection(_){},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){this.rangeOfInterest_=range;if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest){if((this.metricLatencyMs===undefined)||(this.metricLatencyMs<100)){this.updateContents_();}else{this.recomputeButton_.style.background='red';}}},async updateContents_(){Polymer.dom(this.$.error).textContent='';this.$.results.style.display='none';if(!this.model_){Polymer.dom(this.$.error).textContent='Missing model';return;} const options={metrics:[this.currentMetricName_]};if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest&&this.rangeOfInterest&&!this.rangeOfInterest.isEmpty){options.rangeOfInterest=this.rangeOfInterest;} const startDate=new Date();const addFailureCb=failure=>{Polymer.dom(this.$.error).textContent=failure.description;};const histograms=tr.metrics.runMetrics(this.model_,options,addFailureCb);this.metricLatenciesMs_.push(new Date()-startDate);while(this.metricLatenciesMs_.length>20){this.metricLatenciesMs_.shift();} diff --git a/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py b/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py index ee621d68..58ddb7a3 100644 --- a/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py +++ b/catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py @@ -2,8 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import hashlib import os import re +import stat +import subprocess +import sys +import urllib import py_utils @@ -17,13 +22,77 @@ TRACE_START_REGEXP = r'TRACE\:' # Text that ADB sends, but does not need to be displayed to the user. ADB_IGNORE_REGEXP = r'^capturing trace\.\.\. done|^capturing trace\.\.\.' +# Constants required for converting perfetto traces. +LINUX_SHA1 = 'cd9dbc5c92ed0167245c4559bf1971bb21378928' +MAC_SHA1 = 'aed4ad02da526a3f1e4f9df47d4989ae9305b30e' +T2T_OUTPUT = 'trace.systrace' def try_create_agent(options): if options.from_file is not None: + with open(options.from_file, 'rb') as f_in: + if is_perfetto(f_in): + if convert_perfetto_trace(options.from_file): + options.from_file = T2T_OUTPUT + else: + print ('Perfetto trace file: ' + options.from_file + + ' could not be converted.') + sys.exit(1) return AtraceFromFileAgent(options) else: return False +def convert_perfetto_trace(in_file): + t2t_path = os.path.abspath(os.path.join(os.path.dirname(__file__), + '../trace_to_text')) + loaded_t2t = False + if sys.platform.startswith('linux'): + t2t_path += '-linux-' + LINUX_SHA1 + loaded_t2t = load_trace_to_text(t2t_path, 'linux', LINUX_SHA1) + elif sys.platform.startswith('darwin'): + t2t_path += '-mac-' + MAC_SHA1 + loaded_t2t = load_trace_to_text(t2t_path, 'mac', MAC_SHA1) + if loaded_t2t: + os.chmod(t2t_path, stat.S_IXUSR) + return subprocess.call([t2t_path, 'systrace', in_file, T2T_OUTPUT]) == 0 + return False + +def load_trace_to_text(t2t_path, platform, sha_value): + if not os.path.exists(t2t_path): + with open(t2t_path, 'w') as t2t: + url = ('https://storage.googleapis.com/chromium-telemetry/binary_dependencies/trace_to_text-' + + platform + '-' + sha_value) + return urllib.urlretrieve(url, t2t_path) + os.chmod(t2t_path, stat.S_IRUSR) + with open(t2t_path, 'rb') as t2t: + existing_file_hash = hashlib.sha1(t2t.read()).hexdigest() + if existing_file_hash != sha_value: + print 'Hash of trace_to_text binary does not match.' + os.remove(t2t_path) + return False + return True + +def is_perfetto(from_file): + # Starts with a preamble for field ID=1 (TracePacket) + if ord(from_file.read(1)) != 0x0a: + return False + for _ in range(10): # Check the first 10 packets are structured correctly + # Then a var int that specifies field size + field_size = 0 + shift = 0 + while True: + c = ord(from_file.read(1)) + field_size |= (c & 0x7f) << shift + shift += 7 + if not c & 0x80: + break + # The packet itself + from_file.seek(field_size, os.SEEK_CUR) + # The preamble for the next field ID=1 (TracePacket) + if ord(from_file.read(1)) != 0x0a: + return False + # Go back to the beginning of the file + from_file.seek(0) + return True class AtraceFromFileConfig(tracing_agents.TracingConfig): def __init__(self, from_file): diff --git a/catapult/tracing/tracing/trace_data/trace_data.py b/catapult/tracing/tracing/trace_data/trace_data.py index 6c0ff048..b3b783cc 100644 --- a/catapult/tracing/tracing/trace_data/trace_data.py +++ b/catapult/tracing/tracing/trace_data/trace_data.py @@ -50,7 +50,6 @@ ATRACE_PROCESS_DUMP_PART = TraceDataPart('atraceProcessDump') CHROME_TRACE_PART = TraceDataPart('traceEvents') CPU_TRACE_DATA = TraceDataPart('cpuSnapshots') INSPECTOR_TRACE_PART = TraceDataPart('inspectorTimelineEvents') -SURFACE_FLINGER_PART = TraceDataPart('surfaceFlinger') TELEMETRY_PART = TraceDataPart('telemetry') WALT_TRACE_PART = TraceDataPart('waltTraceEvents') @@ -60,7 +59,6 @@ ALL_TRACE_PARTS = {ANDROID_PROCESS_DATA_PART, CHROME_TRACE_PART, CPU_TRACE_DATA, INSPECTOR_TRACE_PART, - SURFACE_FLINGER_PART, TELEMETRY_PART} ALL_TRACE_PARTS_RAW_NAMES = set(k.raw_field_name for k in ALL_TRACE_PARTS) @@ -274,7 +272,7 @@ class TraceDataBuilder(object): self._raw_data = {} def AsData(self): - if self._raw_data == None: + if self._raw_data is None: raise Exception('Can only AsData once') data = TraceData() data._SetFromBuilder(self._raw_data) @@ -292,7 +290,7 @@ class TraceDataBuilder(object): isinstance(trace, dict) or isinstance(trace, list)) - if self._raw_data == None: + if self._raw_data is None: raise Exception('Already called AsData() on this builder.') self._raw_data.setdefault(part.raw_field_name, []) |