aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsabelle Taylor <taylori@google.com>2018-10-31 11:57:24 +0000
committerIsabelle Taylor <taylori@google.com>2018-10-31 11:57:24 +0000
commit758efa702d67e7e8f7fce4e4b4d404fe36cf713c (patch)
tree4b32ed3904208724a5b1ec4a5481861c67677c99
parent42ec7d183c39a9f0b7d1c02c54711d92aaa2b0b8 (diff)
downloadchromium-trace-758efa702d67e7e8f7fce4e4b4d404fe36cf713c.tar.gz
Update to latest catapult (625dca847)
git log --oneline --no-merges eae13a4..625dca847 tracing systrace 625dca847 Handle perfetto protobuf files 00755b36f V8 GC metrics: Split of foreground, background, and total time 38b827049 Add support for metric abbreviation for physics units. c6a85907f Telemetry: migrate the rest of smoothness metrics df56c1dae Telemetry: detect telemetry target renderers 0339f0866 [tracing] Do not skip over entries when importing histograms b145370ba [CpuFCP] Support CPU time FCP 6292d2f5e androidStartupMetric: ignore the 2nd run 623a134fb Reland Migrate trace_viewer.gypi to gni b026043a4 Import perfetto json traces 4bcf107ea Add units for Ampers, Volts, Hertz, bytes per second 1922eb00b Update trace categories to reflect new trace names. 547a6910a Updating user timing argument regex to not parse on subsequent ':' chars. a10f776b1 [results.html] Speed up <histogram-json> parsing 2d4733846 V8 GC metrics: Add GC cycle time 519565187 Revert "Migrate trace_viewer.gypi to gni" fd3370d4a aura: filter out frames not submitted from browser 30824c537 Migrate trace_viewer.gypi to gni b273e0cd2 Add aggregate graphs to visualization tool. e302dcb5a event_finder_util: skip renderer events when no mainThread b8ae746e2 Telemetry: fix a bug in percentage_smooth c2f72b8a9 Fix top level scheduler task title 22c3e6c8f Telemetry: process all rendering pipeline events 6190da59c rendering: Generate metrics at the 95%ile. 1dc9e0a00 androidStartupMetric: re-introduce First Contentful Paint 0c42948b7 Clean up categories to match current tracing in Blink. 0480f8cae Telemetry: migrate frame_times & percentage_smooth cc7dfbec4 [tracing] Restore the minimum mutator utilization metric. 86bdcbf37 androidStartupMetric: slow fallback for incorrect process names e28148f1c Ignore forced Blink GCs in metrics 9ec8468cf Implementing firstContfulPaint metric using navigationId when present. 56216d783 Telemetry: pixel approximated/checkerboarded a7a24422d Telemetry: fix thread_other_cpu_time_per_frame_tbmv2 4c28d39fe Add more system stats to the UI tracing view 929a7f4eb Telemetry: clean up legacy surface flinger metrics bfe2c0046 androidStartupMetric: skip the first start 59297c6f7 Tracing: add java_base* executable memory metrics. 95ed233ed tracing: Avoid rAF callbacks when hidden. 2ba11d1c2 [Dashboard] Key histogram uploads by revision timestamp if necessary 5d1bdd129 trace-viewer: Add a close-button to hide a process. 97534cbfe Dashboard - Add a diagnostic for the build url. 9ba3159e0 tracing: Fix Blink metrics and add unified GC total sum 69f64b270 Allow bindId for separate begin/end slices as well a0faa9d4d Telemetry: cpu_per_frame metrics in TBMv2 7a4e890f2 Re-enable symbolize_trace_end_to_end_test_slow.SymbolizeTraceEndToEndTest.testMacv2 3cf15a9b4 Fix links in tracing/docs/ 8498332d3 trace-viewer: Fix crash in timeline process filter. 7931f7f4a Disable symbolize_trace_end_to_end_test_slow.SymbolizeTraceEndToEndTest.testMacv2 c51eb628d Telemetry: UI frame time metrics in TBMv2 cfc8160ac Telemetry: surface flinger metrics in TBMv2 64f2ed4bb Revert "Use vpython and remove vendored pymock." 23c67a511 Use vpython and remove vendored pymock. 5e9714532 trace-viewer: Allow filtering processes. 582a06eb7 Telemetry: don't fail when SF cannot find win name b7ae965a6 results2.html debounce search d282eec79 Fixing frames in animations following response. deef6ea33 androidStartupMetric: remove the FCP 356cb8473 rendering: Remove some redundant mean_ metrics. 703fd41ed rendering: Remove discrepancy metrics. 758dedc2a Telemetry: Fix SF events cc38544e3 androidStartupMetric: remove request_start_time from comments 945f73d77 VR: Restore metrics after refactoring 62c9dfc97 rendering: Ignore trace-events for canceled draws. 67653ff0e androidStartupMetric: Remove request_start_time 134ee3695 Allow cswitch version 4 as well as 2 916e932a9 Reland "[TBMv2] Support Chromium commit positions in legacy_json_converter" 6333b6741 Revert "[TBMv2] Support Chromium commit positions in legacy_json_converter" f633aa104 [TBMv2] Support Chromium commit positions in legacy_json_converter 66b08ec93 Display cswitch header version on errors 0c8cc6141 Fix a bug in console error metric. 106d366ce Add a metric that tracks the number of console error messages 0349b720c [Tracing] Add support for Optimize-Background bucket to V8's runtime call stats table 658e07ae3 Fix systrace/bin/OWNERS. 86b118ed9 Telemetry: first_gesture_scroll_update_latency 5167fb3f6 Telemetry: latency metrics in TBMv2 e0a3ea946 Tracing: Fix an input latency bug 18259e73a Update memory metric for the upcoming V8 memory dump provider changes. b6bda5336 rendering: Fix for null browser thread. 19aaaf25f [TBMv2] Add legacy JSON converter 7a1ed44d2 Telemetry: fix a rendering benchmark failure 4679c5508 [Tracing] Fix Pylint errors d55ab227c Fix search bug in metrics visualization tool 8eb323fab Telemetry: pixel metrics in TBMv2 68c00fdca Telemetry: rename metrics as per crbug.com/627461 b791754c7 Telemetry: break rendering_metric.html f62079a0f Telemetry: queueing_durations in TBMv2 ccd130b9a Add windows performance counters processing. 11877aab1 Make small fixes to metrics visualization tool 615ae9b6f Add tool for visualizing metrics times 58bf2845e Telemetry: fix Gesture IRs fe6031638 Fix propertyMode in ic_stats_entry d38bbdff0 rendering: Fix filtering events. Bug: 117587646 Test: ./systrace.py Merged-In: Iffd7f590796b07df49ed4fdfd8bc0f695ab9225f (cherry picked from commit 3f5bbbf437b9b03d42e87e60943f21a663f10421) Change-Id: Id88893c5807f91b4f0f1d84cf5b609b240414695
-rw-r--r--UPSTREAM_REVISION2
-rwxr-xr-xcatapult/common/lab/commits.py24
-rwxr-xr-xcatapult/common/node_runner/node_runner/minify13
-rw-r--r--catapult/common/node_runner/node_runner/package.json6
-rw-r--r--catapult/dependency_manager/dependency_manager/__init__.py2
-rw-r--r--catapult/dependency_manager/dependency_manager/archive_info.py3
-rw-r--r--catapult/dependency_manager/dependency_manager/cloud_storage_info_unittest.py2
-rw-r--r--catapult/dependency_manager/dependency_manager/dependency_info.py2
-rw-r--r--catapult/dependency_manager/dependency_manager/local_path_info.py29
-rw-r--r--catapult/dependency_manager/dependency_manager/local_path_info_unittest.py136
-rw-r--r--catapult/dependency_manager/dependency_manager/manager.py2
-rw-r--r--catapult/dependency_manager/dependency_manager/uploader.py2
-rw-r--r--catapult/devil/BUILD.gn21
-rw-r--r--catapult/devil/devil/android/battery_utils.py8
-rw-r--r--catapult/devil/devil/android/decorators.py2
-rw-r--r--catapult/devil/devil/android/device_errors.py4
-rw-r--r--catapult/devil/devil/android/device_utils.py56
-rwxr-xr-xcatapult/devil/devil/android/device_utils_test.py439
-rw-r--r--catapult/devil/devil/android/flag_changer.py7
-rw-r--r--catapult/devil/devil/android/perf/perf_control.py226
-rw-r--r--catapult/devil/devil/android/perf/perf_control_test.py105
-rw-r--r--catapult/devil/devil/android/perf/surface_stats_collector.py24
-rw-r--r--catapult/devil/devil/android/sdk/adb_wrapper.py35
-rw-r--r--catapult/devil/devil/android/sdk/shared_prefs.py11
-rwxr-xr-xcatapult/devil/devil/android/sdk/shared_prefs_test.py32
-rw-r--r--catapult/devil/devil/android/sdk/version_codes.py1
-rwxr-xr-xcatapult/devil/devil/android/tools/provision_devices.py29
-rwxr-xr-xcatapult/devil/devil/android/tools/system_app.py4
-rw-r--r--catapult/devil/devil/android/tools/system_app_test.py6
-rw-r--r--catapult/devil/devil/android/tools/unlock_bootloader.py2
-rw-r--r--catapult/devil/devil/devil_dependencies.json6
-rw-r--r--catapult/devil/devil/utils/cmd_helper.py4
-rw-r--r--catapult/devil/devil/utils/lazy/weak_constant_test.py4
-rw-r--r--catapult/systrace/systrace/systrace_trace_viewer.html527
-rw-r--r--catapult/systrace/systrace/tracing_agents/atrace_from_file_agent.py69
-rw-r--r--catapult/tracing/tracing/trace_data/trace_data.py6
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, [])