summaryrefslogtreecommitdiff
path: root/chromium/v8/tools
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/v8/tools
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/tools')
-rw-r--r--chromium/v8/tools/clusterfuzz/v8_commands.py7
-rwxr-xr-xchromium/v8/tools/clusterfuzz/v8_foozzie.py80
-rwxr-xr-xchromium/v8/tools/clusterfuzz/v8_foozzie_test.py21
-rw-r--r--chromium/v8/tools/clusterfuzz/v8_mock.js14
-rw-r--r--chromium/v8/tools/debug_helper/BUILD.gn1
-rw-r--r--chromium/v8/tools/debug_helper/compiler-types.cc31
-rw-r--r--chromium/v8/tools/debug_helper/debug-helper.h8
-rwxr-xr-xchromium/v8/tools/dev/gm.py32
-rw-r--r--chromium/v8/tools/gcmole/BUILD.gn5
-rw-r--r--chromium/v8/tools/gcmole/gcmole-test.cc5
-rw-r--r--chromium/v8/tools/gcmole/gcmole-tools.tar.gz.sha12
-rw-r--r--chromium/v8/tools/gcmole/gcmole.cc86
-rw-r--r--chromium/v8/tools/gcmole/gcmole.lua27
-rw-r--r--chromium/v8/tools/gcmole/ignored_files2
-rw-r--r--chromium/v8/tools/gcmole/suspects.whitelist2
-rw-r--r--chromium/v8/tools/gcmole/test-expectations.txt14
-rw-r--r--chromium/v8/tools/gen-postmortem-metadata.py4
-rw-r--r--chromium/v8/tools/heap-stats/details-selection-template.html11
-rw-r--r--chromium/v8/tools/heap-stats/details-selection.js106
-rw-r--r--chromium/v8/tools/heap-stats/global-timeline.js4
-rw-r--r--chromium/v8/tools/ic-explorer.html1
-rw-r--r--chromium/v8/tools/lldb_commands.py15
-rw-r--r--chromium/v8/tools/map-processor.html31
-rw-r--r--chromium/v8/tools/map-processor.js41
-rw-r--r--chromium/v8/tools/release/common_includes.py2
-rwxr-xr-xchromium/v8/tools/run.py13
-rw-r--r--chromium/v8/tools/testrunner/local/statusfile.py5
-rwxr-xr-xchromium/v8/tools/testrunner/local/statusfile_unittest.py11
-rw-r--r--chromium/v8/tools/testrunner/local/testsuite.py2
-rw-r--r--chromium/v8/tools/testrunner/local/utils.py2
-rwxr-xr-xchromium/v8/tools/testrunner/num_fuzzer.py3
-rw-r--r--chromium/v8/tools/testrunner/objects/testcase.py5
-rw-r--r--chromium/v8/tools/testrunner/outproc/base.py2
-rw-r--r--chromium/v8/tools/testrunner/outproc/message.py4
-rwxr-xr-xchromium/v8/tools/testrunner/standard_runner.py3
-rw-r--r--chromium/v8/tools/testrunner/testproc/progress.py53
-rw-r--r--chromium/v8/tools/testrunner/testproc/timeout.py1
-rw-r--r--chromium/v8/tools/testrunner/testproc/util_unittest.py11
-rw-r--r--chromium/v8/tools/turbolizer/README.md7
-rw-r--r--chromium/v8/tools/turbolizer/down-arrow.pngbin0 -> 3670 bytes
-rw-r--r--chromium/v8/tools/turbolizer/index.html7
-rw-r--r--chromium/v8/tools/turbolizer/src/constants.ts3
-rw-r--r--chromium/v8/tools/turbolizer/src/graphmultiview.ts9
-rw-r--r--chromium/v8/tools/turbolizer/src/range-view.ts938
-rw-r--r--chromium/v8/tools/turbolizer/src/resizer.ts131
-rw-r--r--chromium/v8/tools/turbolizer/src/sequence-view.ts177
-rw-r--r--chromium/v8/tools/turbolizer/src/source-resolver.ts43
-rw-r--r--chromium/v8/tools/turbolizer/src/turbo-visualizer.ts5
-rw-r--r--chromium/v8/tools/turbolizer/src/util.ts7
-rw-r--r--chromium/v8/tools/turbolizer/turbo-visualizer-ranges.css238
-rw-r--r--chromium/v8/tools/turbolizer/turbo-visualizer.css28
-rw-r--r--chromium/v8/tools/turbolizer/up-arrow.pngbin0 -> 3579 bytes
-rwxr-xr-xchromium/v8/tools/v8_presubmit.py4
-rw-r--r--chromium/v8/tools/v8heapconst.py489
-rw-r--r--chromium/v8/tools/v8windbg/base/utilities.cc9
-rw-r--r--chromium/v8/tools/v8windbg/base/utilities.h2
-rw-r--r--chromium/v8/tools/v8windbg/src/object-inspection.cc73
-rw-r--r--chromium/v8/tools/v8windbg/src/object-inspection.h32
-rw-r--r--chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.cc2
-rw-r--r--chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.h2
-rw-r--r--chromium/v8/tools/v8windbg/src/v8windbg-extension.cc63
-rw-r--r--chromium/v8/tools/v8windbg/src/v8windbg-extension.h23
-rwxr-xr-xchromium/v8/tools/wasm/update-wasm-spec-tests.sh5
-rw-r--r--chromium/v8/tools/whitespace.txt2
-rw-r--r--chromium/v8/tools/zone-stats/categories.js129
-rw-r--r--chromium/v8/tools/zone-stats/details-selection-template.html146
-rw-r--r--chromium/v8/tools/zone-stats/details-selection.js365
-rw-r--r--chromium/v8/tools/zone-stats/global-timeline-template.html16
-rw-r--r--chromium/v8/tools/zone-stats/global-timeline.js323
-rw-r--r--chromium/v8/tools/zone-stats/helper.js30
-rw-r--r--chromium/v8/tools/zone-stats/index.html93
-rw-r--r--chromium/v8/tools/zone-stats/model.js92
-rw-r--r--chromium/v8/tools/zone-stats/trace-file-reader-template.html81
-rw-r--r--chromium/v8/tools/zone-stats/trace-file-reader.js294
74 files changed, 4082 insertions, 453 deletions
diff --git a/chromium/v8/tools/clusterfuzz/v8_commands.py b/chromium/v8/tools/clusterfuzz/v8_commands.py
index 1956ef2802b..58a757e7a79 100644
--- a/chromium/v8/tools/clusterfuzz/v8_commands.py
+++ b/chromium/v8/tools/clusterfuzz/v8_commands.py
@@ -40,9 +40,6 @@ JS_SUPPRESSIONS = os.path.join(BASE_PATH, 'v8_suppressions.js')
ARCH_MOCKS = os.path.join(BASE_PATH, 'v8_mock_archs.js')
WEBASSEMBLY_MOCKS = os.path.join(BASE_PATH, 'v8_mock_webassembly.js')
-# Timeout in seconds for one d8 run.
-TIMEOUT = 3
-
def _startup_files(options):
"""Default files and optional config-specific mock files."""
@@ -70,7 +67,7 @@ class Command(object):
self.files = _startup_files(options)
- def run(self, testcase, verbose=False):
+ def run(self, testcase, timeout, verbose=False):
"""Run the executable with a specific testcase."""
args = [self.executable] + self.flags + self.files + [testcase]
if verbose:
@@ -82,7 +79,7 @@ class Command(object):
return Execute(
args,
cwd=os.path.dirname(os.path.abspath(testcase)),
- timeout=TIMEOUT,
+ timeout=timeout,
)
@property
diff --git a/chromium/v8/tools/clusterfuzz/v8_foozzie.py b/chromium/v8/tools/clusterfuzz/v8_foozzie.py
index b6638cc3774..b3cb8cf94fa 100755
--- a/chromium/v8/tools/clusterfuzz/v8_foozzie.py
+++ b/chromium/v8/tools/clusterfuzz/v8_foozzie.py
@@ -94,6 +94,10 @@ RETURN_FAIL = 2
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
SANITY_CHECKS = os.path.join(BASE_PATH, 'v8_sanity_checks.js')
+# Timeout for one d8 run.
+SANITY_CHECK_TIMEOUT_SEC = 1
+TEST_TIMEOUT_SEC = 3
+
SUPPORTED_ARCHS = ['ia32', 'x64', 'arm', 'arm64']
# Output for suppressed failure case.
@@ -143,6 +147,25 @@ ORIGINAL_SOURCE_HASH_LENGTH = 3
# Placeholder string if no original source file could be determined.
ORIGINAL_SOURCE_DEFAULT = 'none'
+# Placeholder string for failures from crash tests. If a failure is found with
+# this signature, the matching sources should be moved to the mapping below.
+ORIGINAL_SOURCE_CRASHTESTS = 'placeholder for CrashTests'
+
+# Mapping from relative original source path (e.g. CrashTests/path/to/file.js)
+# to a string key. Map to the same key for duplicate issues. The key should
+# have more than 3 characters to not collide with other existing hashes.
+# If a symptom from a particular original source file is known to map to a
+# known failure, it can be added to this mapping. This should be done for all
+# failures from CrashTests, as those by default map to the placeholder above.
+KNOWN_FAILURES = {
+ # Foo.caller with asm.js: https://crbug.com/1042556
+ 'CrashTests/5712410200899584/04483.js': '.caller',
+ 'CrashTests/5703451898085376/02176.js': '.caller',
+ 'CrashTests/4846282433495040/04342.js': '.caller',
+ # Flaky issue that almost never repros.
+ 'CrashTests/5694376231632896/1033966.js': 'flaky',
+}
+
def infer_arch(d8):
"""Infer the V8 architecture from the build configuration next to the
@@ -229,7 +252,7 @@ def parse_args():
options.first = first_config_arguments.make_options(options)
options.second = second_config_arguments.make_options(options)
- # Ensure we make a sane comparison.
+ # Ensure we make a valid comparison.
if (options.first.d8 == options.second.d8 and
options.first.config == options.second.config):
parser.error('Need either executable or config difference.')
@@ -292,6 +315,7 @@ def print_difference(
else:
first_stdout = first_config_output.stdout.decode('utf-8', 'replace')
second_stdout = second_config_output.stdout.decode('utf-8', 'replace')
+ difference = difference.decode('utf-8', 'replace')
text = (FAILURE_TEMPLATE % dict(
configs='%s:%s' % (first_config_label, second_config_label),
@@ -313,6 +337,31 @@ def print_difference(
print(text.encode('utf-8', 'replace'))
+def cluster_failures(source, known_failures=None):
+ """Returns a string key for clustering duplicate failures.
+
+ Args:
+ source: The original source path where the failure happened.
+ known_failures: Mapping from original source path to failure key.
+ """
+ known_failures = known_failures or KNOWN_FAILURES
+ # No source known. Typical for manually uploaded issues. This
+ # requires also manual issue creation.
+ if not source:
+ return ORIGINAL_SOURCE_DEFAULT
+ # Source is known to produce a particular failure.
+ if source in known_failures:
+ return known_failures[source]
+ # Subsume all other sources from CrashTests under one key. Otherwise
+ # failures lead to new crash tests which in turn lead to new failures.
+ if source.startswith('CrashTests'):
+ return ORIGINAL_SOURCE_CRASHTESTS
+
+ # We map all remaining failures to a short hash of the original source.
+ long_key = hashlib.sha1(source.encode('utf-8')).hexdigest()
+ return long_key[:ORIGINAL_SOURCE_HASH_LENGTH]
+
+
def main():
options = parse_args()
@@ -342,8 +391,20 @@ def main():
# Sanity checks. Run both configurations with the sanity-checks file only and
# bail out early if different.
if not options.skip_sanity_checks:
- first_config_output = first_cmd.run(SANITY_CHECKS)
- second_config_output = second_cmd.run(SANITY_CHECKS)
+ first_config_output = first_cmd.run(
+ SANITY_CHECKS, timeout=SANITY_CHECK_TIMEOUT_SEC)
+
+ # Early bailout if first run was a timeout.
+ if timeout_bailout(first_config_output, 1):
+ return RETURN_PASS
+
+ second_config_output = second_cmd.run(
+ SANITY_CHECKS, timeout=SANITY_CHECK_TIMEOUT_SEC)
+
+ # Bailout if second run was a timeout.
+ if timeout_bailout(second_config_output, 2):
+ return RETURN_PASS
+
difference, _ = suppress.diff(first_config_output, second_config_output)
if difference:
# Special source key for sanity checks so that clusterfuzz dedupes all
@@ -354,13 +415,15 @@ def main():
first_config_output, second_config_output, difference)
return RETURN_FAIL
- first_config_output = first_cmd.run(options.testcase, verbose=True)
+ first_config_output = first_cmd.run(
+ options.testcase, timeout=TEST_TIMEOUT_SEC, verbose=True)
# Early bailout if first run was a timeout.
if timeout_bailout(first_config_output, 1):
return RETURN_PASS
- second_config_output = second_cmd.run(options.testcase, verbose=True)
+ second_config_output = second_cmd.run(
+ options.testcase, timeout=TEST_TIMEOUT_SEC, verbose=True)
# Bailout if second run was a timeout.
if timeout_bailout(second_config_output, 2):
@@ -368,12 +431,6 @@ def main():
difference, source = suppress.diff(first_config_output, second_config_output)
- if source:
- long_key = hashlib.sha1(source.encode('utf-8')).hexdigest()
- source_key = long_key[:ORIGINAL_SOURCE_HASH_LENGTH]
- else:
- source_key = ORIGINAL_SOURCE_DEFAULT
-
if difference:
# Only bail out due to suppressed output if there was a difference. If a
# suppression doesn't show up anymore in the statistics, we might want to
@@ -383,6 +440,7 @@ def main():
if fail_bailout(second_config_output, suppress.ignore_by_output2):
return RETURN_FAIL
+ source_key = cluster_failures(source)
print_difference(
options, source_key, first_cmd, second_cmd,
first_config_output, second_config_output, difference, source)
diff --git a/chromium/v8/tools/clusterfuzz/v8_foozzie_test.py b/chromium/v8/tools/clusterfuzz/v8_foozzie_test.py
index f82afc9e205..4a5affd76ea 100755
--- a/chromium/v8/tools/clusterfuzz/v8_foozzie_test.py
+++ b/chromium/v8/tools/clusterfuzz/v8_foozzie_test.py
@@ -51,8 +51,8 @@ class ConfigTest(unittest.TestCase):
assert all(map(lambda x: x[3] in KNOWN_BUILDS, EXPERIMENTS))
# Ensure we compare different configs and same d8, or same config
# to different d8.
- is_sane_comparison = lambda x: (x[1] == x[2]) == ('d8' != x[3])
- assert all(map(is_sane_comparison, EXPERIMENTS))
+ is_valid_comparison = lambda x: (x[1] == x[2]) == ('d8' != x[3])
+ assert all(map(is_valid_comparison, EXPERIMENTS))
# All flags have a probability.
first_is_float = lambda x: type(x[0]) == float
assert all(map(first_is_float, FLAGS))
@@ -101,6 +101,23 @@ class ConfigTest(unittest.TestCase):
class UnitTest(unittest.TestCase):
+ def testCluster(self):
+ crash_test_example_path = 'CrashTests/path/to/file.js'
+ self.assertEqual(
+ v8_foozzie.ORIGINAL_SOURCE_DEFAULT,
+ v8_foozzie.cluster_failures(''))
+ self.assertEqual(
+ v8_foozzie.ORIGINAL_SOURCE_CRASHTESTS,
+ v8_foozzie.cluster_failures(crash_test_example_path))
+ self.assertEqual(
+ '_o_O_',
+ v8_foozzie.cluster_failures(
+ crash_test_example_path,
+ known_failures={crash_test_example_path: '_o_O_'}))
+ self.assertEqual(
+ '980',
+ v8_foozzie.cluster_failures('v8/test/mjsunit/apply.js'))
+
def testDiff(self):
def diff_fun(one, two, skip=False):
suppress = v8_suppressions.get_suppression(
diff --git a/chromium/v8/tools/clusterfuzz/v8_mock.js b/chromium/v8/tools/clusterfuzz/v8_mock.js
index 6372a7afe2b..ca1336a0df6 100644
--- a/chromium/v8/tools/clusterfuzz/v8_mock.js
+++ b/chromium/v8/tools/clusterfuzz/v8_mock.js
@@ -149,6 +149,20 @@ Object.defineProperty(
Float64Array = mock(Float64Array);
})();
+// Mock buffer access via DataViews because of varying NaN patterns.
+(function() {
+ const origIsNaN = isNaN;
+ const deNaNify = function(value) { return origIsNaN(value) ? 1 : value; };
+ const origSetFloat32 = DataView.prototype.setFloat32;
+ DataView.prototype.setFloat32 = function(offset, value, ...rest) {
+ origSetFloat32.call(this, offset, deNaNify(value), ...rest);
+ };
+ const origSetFloat64 = DataView.prototype.setFloat64;
+ DataView.prototype.setFloat64 = function(offset, value, ...rest) {
+ origSetFloat64.call(this, offset, deNaNify(value), ...rest);
+ };
+})();
+
// Mock Worker.
(function() {
let index = 0;
diff --git a/chromium/v8/tools/debug_helper/BUILD.gn b/chromium/v8/tools/debug_helper/BUILD.gn
index a2d76d1fda7..522a0e22702 100644
--- a/chromium/v8/tools/debug_helper/BUILD.gn
+++ b/chromium/v8/tools/debug_helper/BUILD.gn
@@ -76,6 +76,7 @@ v8_component("v8_debug_helper") {
"$target_gen_dir/../../torque-generated/class-debug-readers-tq.h",
"$target_gen_dir/../../torque-generated/instance-types-tq.h",
"$target_gen_dir/heap-constants-gen.cc",
+ "compiler-types.cc",
"debug-helper-internal.cc",
"debug-helper-internal.h",
"debug-helper.h",
diff --git a/chromium/v8/tools/debug_helper/compiler-types.cc b/chromium/v8/tools/debug_helper/compiler-types.cc
new file mode 100644
index 00000000000..1d9f6eb0d3e
--- /dev/null
+++ b/chromium/v8/tools/debug_helper/compiler-types.cc
@@ -0,0 +1,31 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug-helper-internal.h"
+#include "src/compiler/types.h"
+
+namespace ic = v8::internal::compiler;
+
+extern "C" {
+V8_DEBUG_HELPER_EXPORT const char* _v8_debug_helper_BitsetName(
+ uint64_t payload) {
+ // Check if payload is a bitset and return the bitset type.
+ // This line is duplicating the logic from Type::IsBitset.
+ bool is_bit_set = payload & 1;
+ if (!is_bit_set) return nullptr;
+ ic::BitsetType::bitset bits =
+ static_cast<ic::BitsetType::bitset>(payload ^ 1u);
+ switch (bits) {
+#define RETURN_NAMED_TYPE(type, value) \
+ case ic::BitsetType::k##type: \
+ return #type;
+ PROPER_BITSET_TYPE_LIST(RETURN_NAMED_TYPE)
+ INTERNAL_BITSET_TYPE_LIST(RETURN_NAMED_TYPE)
+#undef RETURN_NAMED_TYPE
+
+ default:
+ return nullptr;
+ }
+}
+}
diff --git a/chromium/v8/tools/debug_helper/debug-helper.h b/chromium/v8/tools/debug_helper/debug-helper.h
index df850e81edb..dc08bf63469 100644
--- a/chromium/v8/tools/debug_helper/debug-helper.h
+++ b/chromium/v8/tools/debug_helper/debug-helper.h
@@ -195,6 +195,8 @@ V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
v8::debug_helper::ObjectPropertiesResult* result);
V8_DEBUG_HELPER_EXPORT const v8::debug_helper::ClassList*
_v8_debug_helper_ListObjectClasses();
+V8_DEBUG_HELPER_EXPORT const char* _v8_debug_helper_BitsetName(
+ uint64_t payload);
}
namespace v8 {
@@ -229,6 +231,12 @@ inline const ClassList* ListObjectClasses() {
return _v8_debug_helper_ListObjectClasses();
}
+// Return a bitset name for a v8::internal::compiler::Type with payload or null
+// if the payload is not a bitset.
+inline const char* BitsetName(uint64_t payload) {
+ return _v8_debug_helper_BitsetName(payload);
+}
+
} // namespace debug_helper
} // namespace v8
diff --git a/chromium/v8/tools/dev/gm.py b/chromium/v8/tools/dev/gm.py
index 9d5cbf056a2..f1c2f1fabc9 100755
--- a/chromium/v8/tools/dev/gm.py
+++ b/chromium/v8/tools/dev/gm.py
@@ -10,12 +10,15 @@ Uses Goma by default if it is detected (at output directory setup time).
Expects to be run from the root of a V8 checkout.
Usage:
- gm.py [<arch>].[<mode>[-<suffix>]].[<target>] [testname...]
+ gm.py [<arch>].[<mode>[-<suffix>]].[<target>] [testname...] [--flag]
All arguments are optional. Most combinations should work, e.g.:
gm.py ia32.debug x64.release x64.release-my-custom-opts d8
- gm.py android_arm.release.check
+ gm.py android_arm.release.check --progress=verbose
gm.py x64 mjsunit/foo cctest/test-bar/*
+
+Flags are passed unchanged to the test runner. They must start with -- and must
+not contain spaces.
"""
# See HELP below for additional documentation.
# Note on Python3 compatibility: gm.py itself is Python3 compatible, but
@@ -170,14 +173,14 @@ def _CallWithOutput(cmd):
print("# %s" % cmd)
# The following trickery is required so that the 'cmd' thinks it's running
# in a real terminal, while this script gets to intercept its output.
- master, slave = pty.openpty()
- p = subprocess.Popen(cmd, shell=True, stdin=slave, stdout=slave, stderr=slave)
- os.close(slave)
+ parent, child = pty.openpty()
+ p = subprocess.Popen(cmd, shell=True, stdin=child, stdout=child, stderr=child)
+ os.close(child)
output = []
try:
while True:
try:
- data = os.read(master, 512).decode('utf-8')
+ data = os.read(parent, 512).decode('utf-8')
except OSError as e:
if e.errno != errno.EIO: raise
break # EIO means EOF on some systems
@@ -188,7 +191,7 @@ def _CallWithOutput(cmd):
sys.stdout.flush()
output.append(data)
finally:
- os.close(master)
+ os.close(parent)
p.wait()
return p.returncode, "".join(output)
@@ -224,11 +227,12 @@ def PrepareMksnapshotCmdline(orig_cmdline, path):
return result
class Config(object):
- def __init__(self, arch, mode, targets, tests=[]):
+ def __init__(self, arch, mode, targets, tests=[], testrunner_args=[]):
self.arch = arch
self.mode = mode
self.targets = set(targets)
self.tests = set(tests)
+ self.testrunner_args = testrunner_args
def Extend(self, targets, tests=[]):
self.targets.update(targets)
@@ -303,7 +307,9 @@ class Config(object):
tests = " ".join(self.tests)
return _Call('"%s" ' % sys.executable +
os.path.join("tools", "run-tests.py") +
- " --outdir=%s %s" % (GetPath(self.arch, self.mode), tests))
+ " --outdir=%s %s %s" % (
+ GetPath(self.arch, self.mode), tests,
+ " ".join(self.testrunner_args)))
def GetTestBinary(argstring):
for suite in TESTSUITES_TARGETS:
@@ -316,13 +322,15 @@ class ArgumentParser(object):
self.global_tests = set()
self.global_actions = set()
self.configs = {}
+ self.testrunner_args = []
def PopulateConfigs(self, arches, modes, targets, tests):
for a in arches:
for m in modes:
path = GetPath(a, m)
if path not in self.configs:
- self.configs[path] = Config(a, m, targets, tests)
+ self.configs[path] = Config(a, m, targets, tests,
+ self.testrunner_args)
else:
self.configs[path].Extend(targets, tests)
@@ -348,6 +356,10 @@ class ArgumentParser(object):
# tests have names like "S15.4.4.7_A4_T1", don't split these.
if argstring.startswith("unittests/") or argstring.startswith("test262/"):
words = [argstring]
+ elif argstring.startswith("--"):
+ # Pass all other flags to test runner.
+ self.testrunner_args.append(argstring)
+ return
else:
# Assume it's a word like "x64.release" -> split at the dot.
words = argstring.split('.')
diff --git a/chromium/v8/tools/gcmole/BUILD.gn b/chromium/v8/tools/gcmole/BUILD.gn
index fc7d81e561d..9767acbf5a1 100644
--- a/chromium/v8/tools/gcmole/BUILD.gn
+++ b/chromium/v8/tools/gcmole/BUILD.gn
@@ -16,6 +16,7 @@ group("v8_run_gcmole") {
"parallel.py",
"run-gcmole.py",
"suspects.whitelist",
+ "ignored_files",
"test-expectations.txt",
# The following contains all relevant source and build files.
@@ -37,9 +38,7 @@ group("v8_run_gcmole") {
"$target_gen_dir/../../torque-generated/",
]
- deps = [
- "../../:run_torque",
- ]
+ deps = [ "../../:run_torque" ]
if (v8_gcmole) {
# This assumes gcmole tools have been fetched by a hook
diff --git a/chromium/v8/tools/gcmole/gcmole-test.cc b/chromium/v8/tools/gcmole/gcmole-test.cc
index e0e2801f777..92f7a9eda88 100644
--- a/chromium/v8/tools/gcmole/gcmole-test.cc
+++ b/chromium/v8/tools/gcmole/gcmole-test.cc
@@ -189,7 +189,7 @@ void TestGuardedDeadVarAnalysisNotOnStack(Isolate* isolate) {
void TestGuardedDeadVarAnalysisNested(JSObject raw_obj, Isolate* isolate) {
CauseGCRaw(raw_obj, isolate);
- // Shouldn't cause warning.
+ // Should cause warning.
raw_obj.Print();
}
@@ -198,6 +198,9 @@ void TestGuardedDeadVarAnalysisCaller(Isolate* isolate) {
JSObject raw_obj = *isolate->factory()->NewJSObjectWithNullProto();
TestGuardedDeadVarAnalysisNested(raw_obj, isolate);
+
+ // Shouldn't cause warning.
+ raw_obj.Print();
}
JSObject GuardedAllocation(Isolate* isolate) {
diff --git a/chromium/v8/tools/gcmole/gcmole-tools.tar.gz.sha1 b/chromium/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
index 8f7876fa519..221e2e9d293 100644
--- a/chromium/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
+++ b/chromium/v8/tools/gcmole/gcmole-tools.tar.gz.sha1
@@ -1 +1 @@
-d2f949820bf1ee7343a7b5f46987a3657aaea2e9 \ No newline at end of file
+0af04ef475bc746a501fe17d3b56ccb03fc151fc
diff --git a/chromium/v8/tools/gcmole/gcmole.cc b/chromium/v8/tools/gcmole/gcmole.cc
index 606c90bb582..cce177caf9e 100644
--- a/chromium/v8/tools/gcmole/gcmole.cc
+++ b/chromium/v8/tools/gcmole/gcmole.cc
@@ -46,6 +46,7 @@
namespace {
bool g_tracing_enabled = false;
+bool g_dead_vars_analysis = false;
#define TRACE(str) \
do { \
@@ -61,12 +62,14 @@ bool g_tracing_enabled = false;
} \
} while (false)
-#define TRACE_LLVM_DECL(str, decl) \
- do { \
- if (g_tracing_enabled) { \
- std::cout << str << std::endl; \
- decl->dump(); \
- } \
+// Node: The following is used when tracing --dead-vars
+// to provide extra info for the GC suspect.
+#define TRACE_LLVM_DECL(str, decl) \
+ do { \
+ if (g_tracing_enabled && g_dead_vars_analysis) { \
+ std::cout << str << std::endl; \
+ decl->dump(); \
+ } \
} while (false)
typedef std::string MangledName;
@@ -365,6 +368,11 @@ static bool KnownToCauseGC(clang::MangleContext* ctx,
if (!InV8Namespace(decl)) return false;
+ if (suspects_whitelist.find(decl->getNameAsString()) !=
+ suspects_whitelist.end()) {
+ return false;
+ }
+
MangledName name;
if (GetMangledName(ctx, decl, &name)) {
return gc_suspects.find(name) != gc_suspects.end();
@@ -387,6 +395,7 @@ static bool SuspectedToCauseGC(clang::MangleContext* ctx,
}
if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) {
+ TRACE_LLVM_DECL("Suspected by ", decl);
return true;
}
@@ -677,8 +686,7 @@ class FunctionAnalyzer {
clang::CXXRecordDecl* smi_decl,
clang::CXXRecordDecl* no_gc_decl,
clang::CXXRecordDecl* no_heap_access_decl,
- clang::DiagnosticsEngine& d, clang::SourceManager& sm,
- bool dead_vars_analysis)
+ clang::DiagnosticsEngine& d, clang::SourceManager& sm)
: ctx_(ctx),
object_decl_(object_decl),
maybe_object_decl_(maybe_object_decl),
@@ -687,8 +695,7 @@ class FunctionAnalyzer {
no_heap_access_decl_(no_heap_access_decl),
d_(d),
sm_(sm),
- block_(NULL),
- dead_vars_analysis_(dead_vars_analysis) {}
+ block_(NULL) {}
// --------------------------------------------------------------------------
// Expressions
@@ -984,7 +991,7 @@ class FunctionAnalyzer {
// pointers produces too many false positives in the dead variable
// analysis.
if (IsInternalPointerType(var_type) && !env.IsAlive(var_name) &&
- !HasActiveGuard() && dead_vars_analysis_) {
+ !HasActiveGuard() && g_dead_vars_analysis) {
ReportUnsafe(parent, DEAD_VAR_MSG);
}
return ExprEffect::RawUse();
@@ -1356,15 +1363,6 @@ class FunctionAnalyzer {
}
bool IsInternalPointerType(clang::QualType qtype) {
- // Not yet assigned pointers can't get moved by the GC.
- if (qtype.isNull()) {
- return false;
- }
- // nullptr can't get moved by the GC.
- if (qtype->isNullPtrType()) {
- return false;
- }
-
const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl();
bool result = IsDerivedFromInternalPointer(record);
TRACE_LLVM_TYPE("is internal " << result, qtype);
@@ -1374,6 +1372,15 @@ class FunctionAnalyzer {
// Returns weather the given type is a raw pointer or a wrapper around
// such. For V8 that means Object and MaybeObject instances.
bool RepresentsRawPointerType(clang::QualType qtype) {
+ // Not yet assigned pointers can't get moved by the GC.
+ if (qtype.isNull()) {
+ return false;
+ }
+ // nullptr can't get moved by the GC.
+ if (qtype->isNullPtrType()) {
+ return false;
+ }
+
const clang::PointerType* pointer_type =
llvm::dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull());
if (pointer_type != NULL) {
@@ -1490,7 +1497,6 @@ class FunctionAnalyzer {
clang::SourceManager& sm_;
Block* block_;
- bool dead_vars_analysis_;
struct GCGuard {
clang::CompoundStmt* stmt = NULL;
@@ -1507,10 +1513,10 @@ class ProblemsFinder : public clang::ASTConsumer,
public:
ProblemsFinder(clang::DiagnosticsEngine& d, clang::SourceManager& sm,
const std::vector<std::string>& args)
- : d_(d), sm_(sm), dead_vars_analysis_(false) {
+ : d_(d), sm_(sm) {
for (unsigned i = 0; i < args.size(); ++i) {
if (args[i] == "--dead-vars") {
- dead_vars_analysis_ = true;
+ g_dead_vars_analysis = true;
}
if (args[i] == "--verbose") {
g_tracing_enabled = true;
@@ -1518,7 +1524,29 @@ class ProblemsFinder : public clang::ASTConsumer,
}
}
+ bool TranslationUnitIgnored() {
+ if (!ignored_files_loaded_) {
+ std::ifstream fin("tools/gcmole/ignored_files");
+ std::string s;
+ while (fin >> s) ignored_files_.insert(s);
+ ignored_files_loaded_ = true;
+ }
+
+ clang::FileID main_file_id = sm_.getMainFileID();
+ std::string filename = sm_.getFileEntryForID(main_file_id)->getName().str();
+
+ bool result = ignored_files_.find(filename) != ignored_files_.end();
+ if (result) {
+ llvm::outs() << "Ignoring file " << filename << "\n";
+ }
+ return result;
+ }
+
virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
+ if (TranslationUnitIgnored()) {
+ return;
+ }
+
Resolver r(ctx);
// It is a valid situation that no_gc_decl == NULL when the
@@ -1558,10 +1586,10 @@ class ProblemsFinder : public clang::ASTConsumer,
no_heap_access_decl = no_heap_access_decl->getDefinition();
if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) {
- function_analyzer_ = new FunctionAnalyzer(
- clang::ItaniumMangleContext::create(ctx, d_), object_decl,
- maybe_object_decl, smi_decl, no_gc_decl, no_heap_access_decl, d_, sm_,
- dead_vars_analysis_);
+ function_analyzer_ =
+ new FunctionAnalyzer(clang::ItaniumMangleContext::create(ctx, d_),
+ object_decl, maybe_object_decl, smi_decl,
+ no_gc_decl, no_heap_access_decl, d_, sm_);
TraverseDecl(ctx.getTranslationUnitDecl());
} else {
if (object_decl == NULL) {
@@ -1594,7 +1622,9 @@ class ProblemsFinder : public clang::ASTConsumer,
private:
clang::DiagnosticsEngine& d_;
clang::SourceManager& sm_;
- bool dead_vars_analysis_;
+
+ bool ignored_files_loaded_ = false;
+ std::set<std::string> ignored_files_;
FunctionAnalyzer* function_analyzer_;
};
diff --git a/chromium/v8/tools/gcmole/gcmole.lua b/chromium/v8/tools/gcmole/gcmole.lua
index a09c3b61ad5..95ba0433516 100644
--- a/chromium/v8/tools/gcmole/gcmole.lua
+++ b/chromium/v8/tools/gcmole/gcmole.lua
@@ -40,9 +40,8 @@ local FLAGS = {
-- Print commands to console before executing them.
verbose = false;
- -- Perform dead variable analysis (generates many false positives).
- -- TODO add some sort of whiteliste to filter out false positives.
- dead_vars = false;
+ -- Perform dead variable analysis.
+ dead_vars = true;
-- Enable verbose tracing from the plugin itself.
verbose_trace = false;
@@ -322,6 +321,7 @@ local WHITELIST = {
-- CodeCreateEvent receives AbstractCode (a raw ptr) as an argument.
"CodeCreateEvent",
+ "WriteField",
};
local function AddCause(name, cause)
@@ -477,6 +477,17 @@ local function SafeCheckCorrectnessForArch(arch, for_test)
return errors, output
end
+-- Source: https://stackoverflow.com/a/41515925/1540248
+local function StringDifference(str1,str2)
+ for i = 1,#str1 do -- Loop over strings
+ -- If that character is not equal to its counterpart
+ if str1:sub(i,i) ~= str2:sub(i,i) then
+ return i --Return that index
+ end
+ end
+ return #str1+1 -- Return the index after where the shorter one ends as fallback.
+end
+
local function TestRun()
local errors, output = SafeCheckCorrectnessForArch('x64', true)
if not errors then
@@ -491,6 +502,16 @@ local function TestRun()
if output ~= expectations then
log("** Output mismatch from running tests. Please run them manually.")
+ local idx = StringDifference(output, expectations)
+
+ log("Difference at byte "..idx)
+ log("Expected: "..expectations:sub(idx-10,idx+10))
+ log("Actual: "..output:sub(idx-10,idx+10))
+
+ log("--- Full output ---")
+ log(output)
+ log("------")
+
return false
end
diff --git a/chromium/v8/tools/gcmole/ignored_files b/chromium/v8/tools/gcmole/ignored_files
new file mode 100644
index 00000000000..05fcd7a129e
--- /dev/null
+++ b/chromium/v8/tools/gcmole/ignored_files
@@ -0,0 +1,2 @@
+src/profiler/heap-snapshot-generator.cc
+src/execution/isolate.cc
diff --git a/chromium/v8/tools/gcmole/suspects.whitelist b/chromium/v8/tools/gcmole/suspects.whitelist
index 01db7401f22..1ac855f2f76 100644
--- a/chromium/v8/tools/gcmole/suspects.whitelist
+++ b/chromium/v8/tools/gcmole/suspects.whitelist
@@ -2,3 +2,5 @@ IsConstructor
IsEval
IsAsync
IsPromiseAll
+IsPromiseAny
+VisitRootPointers
diff --git a/chromium/v8/tools/gcmole/test-expectations.txt b/chromium/v8/tools/gcmole/test-expectations.txt
index 82134743f6b..780cea91811 100644
--- a/chromium/v8/tools/gcmole/test-expectations.txt
+++ b/chromium/v8/tools/gcmole/test-expectations.txt
@@ -1,4 +1,7 @@
+tools/gcmole/gcmole-test.cc:27:10: warning: Possibly dead variable.
+ return obj;
+ ^
tools/gcmole/gcmole-test.cc:45:3: warning: Possible problem with evaluation order.
TwoArgumentsFunction(*CauseGC(obj1, isolate), *CauseGC(obj2, isolate));
^
@@ -20,4 +23,13 @@ tools/gcmole/gcmole-test.cc:130:14: warning: Possible problem with evaluation or
tools/gcmole/gcmole-test.cc:151:14: warning: Possible problem with evaluation order.
so_handle->Method(*SomeClass::StaticCauseGC(obj1, isolate));
^
-7 warnings generated.
+tools/gcmole/gcmole-test.cc:161:3: warning: Possibly dead variable.
+ raw_obj.Print();
+ ^
+tools/gcmole/gcmole-test.cc:193:3: warning: Possibly dead variable.
+ raw_obj.Print();
+ ^
+tools/gcmole/gcmole-test.cc:216:3: warning: Possibly dead variable.
+ raw_obj.Print();
+ ^
+11 warnings generated.
diff --git a/chromium/v8/tools/gen-postmortem-metadata.py b/chromium/v8/tools/gen-postmortem-metadata.py
index b36cd20221a..84b9ad49905 100644
--- a/chromium/v8/tools/gen-postmortem-metadata.py
+++ b/chromium/v8/tools/gen-postmortem-metadata.py
@@ -223,9 +223,6 @@ consts_misc = [
'value': 'SimpleNumberDictionaryShape::kEntrySize' },
{ 'name': 'type_JSError__JS_ERROR_TYPE', 'value': 'JS_ERROR_TYPE' },
-
- { 'name': 'class_SharedFunctionInfo__function_data__Object',
- 'value': 'SharedFunctionInfo::kFunctionDataOffset' },
];
#
@@ -263,6 +260,7 @@ extras_accessors = [
'ExternalString, resource, Object, kResourceOffset',
'SeqOneByteString, chars, char, kHeaderSize',
'SeqTwoByteString, chars, char, kHeaderSize',
+ 'UncompiledData, inferred_name, String, kInferredNameOffset',
'UncompiledData, start_position, int32_t, kStartPositionOffset',
'UncompiledData, end_position, int32_t, kEndPositionOffset',
'SharedFunctionInfo, raw_function_token_offset, int16_t, kFunctionTokenOffsetOffset',
diff --git a/chromium/v8/tools/heap-stats/details-selection-template.html b/chromium/v8/tools/heap-stats/details-selection-template.html
index cd429bf1a5d..9f12bde4066 100644
--- a/chromium/v8/tools/heap-stats/details-selection-template.html
+++ b/chromium/v8/tools/heap-stats/details-selection-template.html
@@ -86,6 +86,17 @@ found in the LICENSE file. -->
width: 50px;
}
+.categorySelectionButtons {
+ float: right;
+}
+.categoryLabels {
+ float: left;
+ min-wdith: 200px;
+}
+.categoryContent {
+ clear: both;
+}
+
</style>
<section id="dataSelectionSection">
<h2>Data selection</h2>
diff --git a/chromium/v8/tools/heap-stats/details-selection.js b/chromium/v8/tools/heap-stats/details-selection.js
index f7e32733d97..7130e19f408 100644
--- a/chromium/v8/tools/heap-stats/details-selection.js
+++ b/chromium/v8/tools/heap-stats/details-selection.js
@@ -30,6 +30,8 @@ defineCustomElement('details-selection', (templateText) =>
.addEventListener('click', e => this.filterCurrentSelection(e));
this.$('#category-auto-filter-btn')
.addEventListener('click', e => this.filterTop20Categories(e));
+ this._data = undefined;
+ this.selection = undefined;
}
connectedCallback() {
@@ -38,6 +40,14 @@ defineCustomElement('details-selection', (templateText) =>
}
}
+ dataChanged() {
+ this.selection = {categories: {}};
+ this.resetUI(true);
+ this.populateIsolateSelect();
+ this.handleIsolateChange();
+ this.$('#dataSelectionSection').style.display = 'block';
+ }
+
set data(value) {
this._data = value;
this.dataChanged();
@@ -85,45 +95,60 @@ defineCustomElement('details-selection', (templateText) =>
const div = document.createElement('div');
div.id = name;
div.classList.add('box');
- const ul = document.createElement('ul');
+
+ let ul = document.createElement('ul');
+ ul.className = 'categoryLabels'
+ {
+ const name_li = document.createElement('li');
+ name_li.textContent = CATEGORY_NAMES.get(name);
+ ul.appendChild(name_li);
+
+ const percent_li = document.createElement('li');
+ percent_li.textContent = '0%';
+ percent_li.id = name + 'PercentContent';
+ ul.appendChild(percent_li);
+ }
+ div.appendChild(ul);
+
+ ul = document.createElement('ul');
+ ul.className = 'categorySelectionButtons'
+ {
+ const all_li = document.createElement('li');
+ const all_button = document.createElement('button');
+ all_button.textContent = 'All';
+ all_button.addEventListener('click', e => this.selectCategory(name));
+ all_li.appendChild(all_button);
+ ul.appendChild(all_li);
+
+ const top_li = document.createElement('li');
+ const top_button = document.createElement('button');
+ top_button.textContent = 'Top 10';
+ top_button.addEventListener(
+ 'click', e => this.selectCategoryTopEntries(name));
+ top_li.appendChild(top_button);
+ ul.appendChild(top_li);
+
+ const none_li = document.createElement('li');
+ const none_button = document.createElement('button');
+ none_button.textContent = 'None';
+ none_button.addEventListener('click', e => this.unselectCategory(name));
+ none_li.appendChild(none_button);
+ ul.appendChild(none_li);
+ }
div.appendChild(ul);
- const name_li = document.createElement('li');
- ul.appendChild(name_li);
- name_li.innerHTML = CATEGORY_NAMES.get(name);
- const percent_li = document.createElement('li');
- ul.appendChild(percent_li);
- percent_li.innerHTML = '0%';
- percent_li.id = name + 'PercentContent';
- const all_li = document.createElement('li');
- ul.appendChild(all_li);
- const all_button = document.createElement('button');
- all_li.appendChild(all_button);
- all_button.innerHTML = 'All';
- all_button.addEventListener('click', e => this.selectCategory(name));
- const none_li = document.createElement('li');
- ul.appendChild(none_li);
- const none_button = document.createElement('button');
- none_li.appendChild(none_button);
- none_button.innerHTML = 'None';
- none_button.addEventListener('click', e => this.unselectCategory(name));
+
const innerDiv = document.createElement('div');
- div.appendChild(innerDiv);
innerDiv.id = name + 'Content';
+ innerDiv.className = 'categoryContent';
+ div.appendChild(innerDiv);
+
const percentDiv = document.createElement('div');
- div.appendChild(percentDiv);
percentDiv.className = 'percentBackground';
percentDiv.id = name + 'PercentBackground';
+ div.appendChild(percentDiv);
return div;
}
- dataChanged() {
- this.selection = {categories: {}};
- this.resetUI(true);
- this.populateIsolateSelect();
- this.handleIsolateChange();
- this.$('#dataSelectionSection').style.display = 'block';
- }
-
populateIsolateSelect() {
let isolates = Object.entries(this.data);
// Sorty by peak heap memory consumption.
@@ -259,7 +284,7 @@ defineCustomElement('details-selection', (templateText) =>
});
Object.entries(overalls).forEach(([category, category_overall]) => {
let percents = category_overall / overall * 100;
- this.$(`#${category}PercentContent`).innerHTML =
+ this.$(`#${category}PercentContent`).textContent =
`${percents.toFixed(1)}%`;
this.$('#' + category + 'PercentBackground').style.left = percents + '%';
});
@@ -324,7 +349,7 @@ defineCustomElement('details-selection', (templateText) =>
populateCategories() {
this.clearCategories();
- const categories = {};
+ const categories = {__proto__:null};
for (let cat of CATEGORIES.keys()) {
categories[cat] = [];
}
@@ -354,6 +379,23 @@ defineCustomElement('details-selection', (templateText) =>
this.notifySelectionChanged();
}
+ selectCategoryTopEntries(category) {
+ // unselect all checkboxes in this category.
+ this.querySelectorAll('input[name=' + category + 'Checkbox]')
+ .forEach(checkbox => checkbox.checked = false);
+ const data = this.selectedData.instance_type_data;
+
+ // Get the max values for instance_types in this category
+ const categoryInstanceTypes = Array.from(CATEGORIES.get(category));
+ categoryInstanceTypes.filter(each => each in data)
+ .sort((a,b) => {
+ return data[b].overall - data[a].overall;
+ }).slice(0, 10).forEach((category) => {
+ this.$('#' + category + 'Checkbox').checked = true;
+ });
+ this.notifySelectionChanged();
+ }
+
createCheckBox(instance_type, category) {
const div = document.createElement('div');
div.classList.add('instanceTypeSelectBox');
diff --git a/chromium/v8/tools/heap-stats/global-timeline.js b/chromium/v8/tools/heap-stats/global-timeline.js
index 2f16b1bdfbd..05c69f558e6 100644
--- a/chromium/v8/tools/heap-stats/global-timeline.js
+++ b/chromium/v8/tools/heap-stats/global-timeline.js
@@ -209,6 +209,10 @@ defineCustomElement('global-timeline', (templateText) =>
}
drawChart() {
+ setTimeout(() => this._drawChart(), 10);
+ }
+
+ _drawChart() {
console.assert(this.data, 'invalid data');
console.assert(this.selection, 'invalid selection');
diff --git a/chromium/v8/tools/ic-explorer.html b/chromium/v8/tools/ic-explorer.html
index 4c725163c5f..4ca552d6c97 100644
--- a/chromium/v8/tools/ic-explorer.html
+++ b/chromium/v8/tools/ic-explorer.html
@@ -339,6 +339,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
function initGroupKeySelect() {
let select = document.getElementById("group-key");
+ select.options.length = 0;
for (let i in properties) {
let option = document.createElement("option");
option.text = properties[i];
diff --git a/chromium/v8/tools/lldb_commands.py b/chromium/v8/tools/lldb_commands.py
index 2884cd60b01..dc96e5747d0 100644
--- a/chromium/v8/tools/lldb_commands.py
+++ b/chromium/v8/tools/lldb_commands.py
@@ -21,8 +21,17 @@ def current_frame(debugger):
return current_thread(debugger).GetSelectedFrame()
def no_arg_cmd(debugger, cmd):
- current_frame(debugger).EvaluateExpression(cmd)
- print("")
+ evaluate_result = current_frame(debugger).EvaluateExpression(cmd)
+ # When a void function is called the return value type is 0x1001 which
+ # is specified in http://tiny.cc/bigskz. This does not indicate
+ # an error so we check for that value below.
+ kNoResult = 0x1001
+ error = evaluate_result.GetError()
+ if error.fail and error.value != kNoResult:
+ print("Failed to evaluate command {} :".format(cmd))
+ print(error.description)
+ else:
+ print("")
def ptr_arg_cmd(debugger, name, param, cmd):
if not param:
@@ -41,7 +50,7 @@ def job(debugger, param, *args):
def jlh(debugger, param, *args):
"""Print v8::Local handle value"""
ptr_arg_cmd(debugger, 'jlh', param,
- "_v8_internal_Print_Object(*(v8::internal::Object**)(*{}))")
+ "_v8_internal_Print_Object(*(v8::internal::Object**)({}.val_))")
def jco(debugger, param, *args):
"""Print the code object at the given pc (default: current pc)"""
diff --git a/chromium/v8/tools/map-processor.html b/chromium/v8/tools/map-processor.html
index 77e0e7b19c7..b2bc3d57df4 100644
--- a/chromium/v8/tools/map-processor.html
+++ b/chromium/v8/tools/map-processor.html
@@ -369,6 +369,10 @@ dd {
z-index: 100;
display: none;
}
+#searchBarInput {
+ width: 200px;
+}
+
</style>
<script src="./splaytree.js"></script>
<script src="./codemap.js"></script>
@@ -503,8 +507,23 @@ define(Array.prototype, "histogram", function(mapFn) {
return histogram;
});
+
// =========================================================================
// EventHandlers
+function handleSearchBar(){
+ let searchBar = $('searchBarInput');
+ let searchBarInput = searchBar.value;
+ let selectedMap = V8Map.get(searchBarInput);
+ //removeAllChildren($('mapIdList'));
+ if(selectedMap){
+ let map = selectedMap;
+ document.state.map = map;
+ searchBar.className = "green";
+ } else {
+ searchBar.className = "red";
+ }
+}
+
function handleBodyLoad() {
let upload = $('fileReader');
upload.onclick = (e) => $("file").click();
@@ -1253,13 +1272,23 @@ function transitionTypeToColor(type) {
<section id="transitionView"></section>
<br/>
+
+ <h2>Search Map by Address</h2>
+ <section id="searchBar"></section>
+ <input type="search" id="searchBarInput" placeholder="Search maps by address..">
+ <button onclick="handleSearchBar()">Search</button>
+ <ul id="mapIdList" title="Map Id List">
+ </ul>
+
+
<h2>Selected Map</h2>
<section id="mapDetails"></section>
</div>
<section>
<h2>Instructions</h2>
- <p>Visualize Map trees that have been gathered using <code>--trace-maps</code>.</p>
+ <p>Visualize Map trees that have been gathered using <code>path/to/d8 $FILE --trace-maps</code>.</p>
+ <p>You can inspect the transition tree in DevTools by looking at <code>document.state.timeline.values</code>.
<h3>Keyboard Shortcuts</h3>
<dl>
<dt><kbd>SHIFT</kbd> + <kbd>Arrow Up</kbd></dt>
diff --git a/chromium/v8/tools/map-processor.js b/chromium/v8/tools/map-processor.js
index d743cba383a..9b261c7d1b8 100644
--- a/chromium/v8/tools/map-processor.js
+++ b/chromium/v8/tools/map-processor.js
@@ -43,17 +43,17 @@ class MapProcessor extends LogReader {
processor: this.processFunctionMove
},
'map-create': {
- parsers: [parseInt, parseInt, parseString],
+ parsers: [parseInt, parseString],
processor: this.processMapCreate
},
'map': {
- parsers: [parseString, parseInt, parseInt, parseInt, parseInt, parseInt,
+ parsers: [parseString, parseInt, parseString, parseString, parseInt, parseInt,
parseString, parseString, parseString
],
processor: this.processMap
},
'map-details': {
- parsers: [parseInt, parseInt, parseString],
+ parsers: [parseInt, parseString, parseString],
processor: this.processMapDetails
}
};
@@ -183,19 +183,16 @@ class MapProcessor extends LogReader {
this.getExistingMap(id, time).deprecate();
}
- processMapCreate(time, id, string) {
+ processMapCreate(time, id) {
// map-create events might override existing maps if the addresses get
- // rcycled. Hence we do not check for existing maps.
+ // recycled. Hence we do not check for existing maps.
let map = this.createMap(id, time);
- map.description = string;
}
processMapDetails(time, id, string) {
//TODO(cbruni): fix initial map logging.
let map = this.getExistingMap(id, time);
- if (!map.description) {
- //map.description = string;
- }
+ map.description = string;
}
createMap(id, time) {
@@ -205,8 +202,8 @@ class MapProcessor extends LogReader {
}
getExistingMap(id, time) {
- if (id === 0) return undefined;
- let map = V8Map.get(id);
+ if (id === "0x000000000000") return undefined;
+ let map = V8Map.get(id, time);
if (map === undefined) {
console.error("No map details provided: id=" + id);
// Manually patch in a map to continue running.
@@ -334,18 +331,34 @@ class V8Map {
return parents;
}
- static get(id) {
- return this.cache.get(id);
+
+ static get(id, time = undefined) {
+ let maps = this.cache.get(id);
+ if(maps){
+ for (let i = 0; i < maps.length; i++) {
+ //TODO: Implement time based map search
+ if(maps[i].time === time){
+ return maps[i];
+ }
+ }
+ // default return the latest
+ return maps[maps.length-1];
+ }
}
static set(id, map) {
- this.cache.set(id, map);
+ if(this.cache.has(id)){
+ this.cache.get(id).push(map);
+ } else {
+ this.cache.set(id, [map]);
+ }
}
}
V8Map.cache = new Map();
+
// ===========================================================================
class Edge {
constructor(type, name, reason, time, from, to) {
diff --git a/chromium/v8/tools/release/common_includes.py b/chromium/v8/tools/release/common_includes.py
index dbb3ba5f24b..fd69075872f 100644
--- a/chromium/v8/tools/release/common_includes.py
+++ b/chromium/v8/tools/release/common_includes.py
@@ -511,7 +511,7 @@ class Step(GitRecipesMixin):
def WaitForLGTM(self):
print ("Please wait for an LGTM, then type \"LGTM<Return>\" to commit "
"your change. (If you need to iterate on the patch or double check "
- "that it's sane, do so in another shell, but remember to not "
+ "that it's sensible, do so in another shell, but remember to not "
"change the headline of the uploaded CL.")
answer = ""
while answer != "LGTM":
diff --git a/chromium/v8/tools/run.py b/chromium/v8/tools/run.py
index 5a656e19b59..59b3c15e682 100755
--- a/chromium/v8/tools/run.py
+++ b/chromium/v8/tools/run.py
@@ -6,7 +6,18 @@
"""This program wraps an arbitrary command since gn currently can only execute
scripts."""
+from __future__ import print_function
+
import subprocess
import sys
-sys.exit(subprocess.call(sys.argv[1:]))
+result = subprocess.call(sys.argv[1:])
+if result != 0:
+ # Windows error codes such as 0xC0000005 and 0xC0000409 are much easier
+ # to recognize and differentiate in hex.
+ if result < -100:
+ # Print negative hex numbers as positive by adding 2^32.
+ print('Return code is %08X' % (result + 2**32))
+ else:
+ print('Return code is %d' % result)
+sys.exit(result)
diff --git a/chromium/v8/tools/testrunner/local/statusfile.py b/chromium/v8/tools/testrunner/local/statusfile.py
index f99941eb991..c8a1b307e41 100644
--- a/chromium/v8/tools/testrunner/local/statusfile.py
+++ b/chromium/v8/tools/testrunner/local/statusfile.py
@@ -27,12 +27,13 @@
# for py2/py3 compatibility
from __future__ import print_function
+from __future__ import absolute_import
import os
import re
-from variants import ALL_VARIANTS
-from utils import Freeze
+from .variants import ALL_VARIANTS
+from .utils import Freeze
# Possible outcomes
FAIL = "FAIL"
diff --git a/chromium/v8/tools/testrunner/local/statusfile_unittest.py b/chromium/v8/tools/testrunner/local/statusfile_unittest.py
index e8d5ff99cd1..3e2493c0ce6 100755
--- a/chromium/v8/tools/testrunner/local/statusfile_unittest.py
+++ b/chromium/v8/tools/testrunner/local/statusfile_unittest.py
@@ -4,10 +4,17 @@
# found in the LICENSE file.
+from __future__ import absolute_import
+import os
+import sys
import unittest
-import statusfile
-from utils import Freeze
+TOOLS_PATH = os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__))))
+sys.path.append(TOOLS_PATH)
+
+from testrunner.local import statusfile
+from testrunner.local.utils import Freeze
TEST_VARIABLES = {
diff --git a/chromium/v8/tools/testrunner/local/testsuite.py b/chromium/v8/tools/testrunner/local/testsuite.py
index 864d7346fca..a72ef4be610 100644
--- a/chromium/v8/tools/testrunner/local/testsuite.py
+++ b/chromium/v8/tools/testrunner/local/testsuite.py
@@ -223,7 +223,7 @@ class TestGenerator(object):
return self
def __next__(self):
- return self.next()
+ return next(self)
def next(self):
return next(self._iterator)
diff --git a/chromium/v8/tools/testrunner/local/utils.py b/chromium/v8/tools/testrunner/local/utils.py
index 9128c433a04..b1c3575621c 100644
--- a/chromium/v8/tools/testrunner/local/utils.py
+++ b/chromium/v8/tools/testrunner/local/utils.py
@@ -35,7 +35,7 @@ import os
import platform
import re
import subprocess
-import urllib2
+import urllib
### Exit codes and their meaning.
diff --git a/chromium/v8/tools/testrunner/num_fuzzer.py b/chromium/v8/tools/testrunner/num_fuzzer.py
index d4e92a61e80..7777f4c66d8 100755
--- a/chromium/v8/tools/testrunner/num_fuzzer.py
+++ b/chromium/v8/tools/testrunner/num_fuzzer.py
@@ -5,13 +5,14 @@
# found in the LICENSE file.
# for py2/py3 compatibility
+from __future__ import absolute_import
from __future__ import print_function
import random
import sys
# Adds testrunner to the path hence it has to be imported at the beggining.
-import base_runner
+from . import base_runner
from testrunner.local import utils
diff --git a/chromium/v8/tools/testrunner/objects/testcase.py b/chromium/v8/tools/testrunner/objects/testcase.py
index 2a75cf60c45..ac4defd2d78 100644
--- a/chromium/v8/tools/testrunner/objects/testcase.py
+++ b/chromium/v8/tools/testrunner/objects/testcase.py
@@ -161,6 +161,11 @@ class TestCase(object):
statusfile.CRASH not in self._statusfile_outcomes)
@property
+ def is_fail(self):
+ return (statusfile.FAIL in self._statusfile_outcomes and
+ statusfile.PASS not in self._statusfile_outcomes)
+
+ @property
def only_standard_variant(self):
return statusfile.NO_VARIANTS in self._statusfile_outcomes
diff --git a/chromium/v8/tools/testrunner/outproc/base.py b/chromium/v8/tools/testrunner/outproc/base.py
index 9b65ca564a9..847b2242ffa 100644
--- a/chromium/v8/tools/testrunner/outproc/base.py
+++ b/chromium/v8/tools/testrunner/outproc/base.py
@@ -193,6 +193,8 @@ class ExpectedOutProc(OutProc):
line.startswith('**') or
line.startswith('ANDROID') or
line.startswith('###') or
+ # Android linker warning.
+ line.startswith('WARNING: linker:') or
# FIXME(machenbach): The test driver shouldn't try to use slow
# asserts if they weren't compiled. This fails in optdebug=2.
line == 'Warning: unknown flag --enable-slow-asserts.' or
diff --git a/chromium/v8/tools/testrunner/outproc/message.py b/chromium/v8/tools/testrunner/outproc/message.py
index f196cfd614b..c253b6f8e06 100644
--- a/chromium/v8/tools/testrunner/outproc/message.py
+++ b/chromium/v8/tools/testrunner/outproc/message.py
@@ -59,5 +59,7 @@ class OutProc(base.OutProc):
not string.strip() or
string.startswith("==") or
string.startswith("**") or
- string.startswith("ANDROID")
+ string.startswith("ANDROID") or
+ # Android linker warning.
+ string.startswith('WARNING: linker:')
)
diff --git a/chromium/v8/tools/testrunner/standard_runner.py b/chromium/v8/tools/testrunner/standard_runner.py
index 10545fa5f24..9790e463437 100755
--- a/chromium/v8/tools/testrunner/standard_runner.py
+++ b/chromium/v8/tools/testrunner/standard_runner.py
@@ -5,6 +5,7 @@
# found in the LICENSE file.
# for py2/py3 compatibility
+from __future__ import absolute_import
from __future__ import print_function
from functools import reduce
@@ -15,7 +16,7 @@ import sys
import tempfile
# Adds testrunner to the path hence it has to be imported at the beggining.
-import base_runner
+from . import base_runner
from testrunner.local import utils
from testrunner.local.variants import ALL_VARIANTS
diff --git a/chromium/v8/tools/testrunner/testproc/progress.py b/chromium/v8/tools/testrunner/testproc/progress.py
index 671eebb9222..7a47c1b692a 100644
--- a/chromium/v8/tools/testrunner/testproc/progress.py
+++ b/chromium/v8/tools/testrunner/testproc/progress.py
@@ -4,6 +4,7 @@
# for py2/py3 compatibility
from __future__ import print_function
+from __future__ import absolute_import
import datetime
import json
@@ -12,7 +13,7 @@ import platform
import subprocess
import sys
import time
-import util
+from . import util
from . import base
@@ -243,15 +244,22 @@ class CompactProgressIndicator(ProgressIndicator):
self._clear_line(self._last_status_length)
print_failure_header(test)
if len(stdout):
- print(self._templates['stdout'] % stdout)
+ self.printFormatted('stdout', stdout)
if len(stderr):
- print(self._templates['stderr'] % stderr)
- print("Command: %s" % result.cmd.to_string(relative=True))
+ self.printFormatted('stderr', stderr)
+ self.printFormatted(
+ 'command', "Command: %s" % result.cmd.to_string(relative=True))
if output.HasCrashed():
- print("exit code: %s" % output.exit_code_string)
- print("--- CRASHED ---")
- if output.HasTimedOut():
- print("--- TIMEOUT ---")
+ self.printFormatted(
+ 'failure', "exit code: %s" % output.exit_code_string)
+ self.printFormatted('failure', "--- CRASHED ---")
+ elif output.HasTimedOut():
+ self.printFormatted('failure', "--- TIMEOUT ---")
+ else:
+ if test.is_fail:
+ self.printFormatted('failure', "--- UNEXPECTED PASS ---")
+ else:
+ self.printFormatted('failure', "--- FAILED ---")
def finished(self):
self._print_progress('Done')
@@ -272,12 +280,12 @@ class CompactProgressIndicator(ProgressIndicator):
'mins': int(elapsed) // 60,
'secs': int(elapsed) % 60
}
- status = self._truncate(status, 78)
+ status = self._truncateStatusLine(status, 78)
self._last_status_length = len(status)
print(status, end='')
sys.stdout.flush()
- def _truncate(self, string, length):
+ def _truncateStatusLine(self, string, length):
if length and len(string) > (length - 3):
return string[:(length - 3)] + "..."
else:
@@ -296,22 +304,33 @@ class ColorProgressIndicator(CompactProgressIndicator):
"\033[31m-%(failed) 4d\033[0m]: %(test)s"),
'stdout': "\033[1m%s\033[0m",
'stderr': "\033[31m%s\033[0m",
+ 'failure': "\033[1;31m%s\033[0m",
+ 'command': "\033[33m%s\033[0m",
}
super(ColorProgressIndicator, self).__init__(templates)
+ def printFormatted(self, format, string):
+ print(self._templates[format] % string)
+
+ def _truncateStatusLine(self, string, length):
+ # Add some slack for the color control chars
+ return super(ColorProgressIndicator, self)._truncateStatusLine(
+ string, length + 3*9)
+
def _clear_line(self, last_length):
print("\033[1K\r", end='')
class MonochromeProgressIndicator(CompactProgressIndicator):
def __init__(self):
- templates = {
- 'status_line': ("[%(mins)02i:%(secs)02i|%%%(progress) 4d|"
- "+%(passed) 4d|-%(failed) 4d]: %(test)s"),
- 'stdout': '%s',
- 'stderr': '%s',
- }
- super(MonochromeProgressIndicator, self).__init__(templates)
+ templates = {
+ 'status_line': ("[%(mins)02i:%(secs)02i|%%%(progress) 4d|"
+ "+%(passed) 4d|-%(failed) 4d]: %(test)s"),
+ }
+ super(MonochromeProgressIndicator, self).__init__(templates)
+
+ def printFormatted(self, format, string):
+ print(string)
def _clear_line(self, last_length):
print(("\r" + (" " * last_length) + "\r"), end='')
diff --git a/chromium/v8/tools/testrunner/testproc/timeout.py b/chromium/v8/tools/testrunner/testproc/timeout.py
index 9a4e88c8f05..026ba02cd97 100644
--- a/chromium/v8/tools/testrunner/testproc/timeout.py
+++ b/chromium/v8/tools/testrunner/testproc/timeout.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
# Copyright 2018 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/chromium/v8/tools/testrunner/testproc/util_unittest.py b/chromium/v8/tools/testrunner/testproc/util_unittest.py
index 243bf9789a7..5bf6a6e79ad 100644
--- a/chromium/v8/tools/testrunner/testproc/util_unittest.py
+++ b/chromium/v8/tools/testrunner/testproc/util_unittest.py
@@ -3,9 +3,18 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-from util import FixedSizeTopList
+from __future__ import absolute_import
+
+import os
+import sys
import unittest
+TOOLS_PATH = os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__))))
+sys.path.append(TOOLS_PATH)
+
+from testrunner.testproc.util import FixedSizeTopList
+
class TestOrderedFixedSizeList(unittest.TestCase):
def test_empty(self):
ofsl = FixedSizeTopList(3)
diff --git a/chromium/v8/tools/turbolizer/README.md b/chromium/v8/tools/turbolizer/README.md
index c5ee729d64d..fa804f65e94 100644
--- a/chromium/v8/tools/turbolizer/README.md
+++ b/chromium/v8/tools/turbolizer/README.md
@@ -74,7 +74,6 @@ well as '--cpu' to specify which CPU to sample.
Turbolizer build process
------------------------
-Turbolizer is currently migrating to TypeScript. The typescript sources reside in
-tools/turbolizer/src, and the typescript compiler will put the JavaScript output
-into tools/turbolizer/build/. The index.html file is set up to load the JavaScript
-from that directory.
+The typescript sources reside in tools/turbolizer/src, and the typescript
+compiler will put the JavaScript output into tools/turbolizer/build/. The
+index.html file is set up to load the JavaScript from that directory.
diff --git a/chromium/v8/tools/turbolizer/down-arrow.png b/chromium/v8/tools/turbolizer/down-arrow.png
new file mode 100644
index 00000000000..39339f289a3
--- /dev/null
+++ b/chromium/v8/tools/turbolizer/down-arrow.png
Binary files differ
diff --git a/chromium/v8/tools/turbolizer/index.html b/chromium/v8/tools/turbolizer/index.html
index 268e51e0200..ea1b0b74d27 100644
--- a/chromium/v8/tools/turbolizer/index.html
+++ b/chromium/v8/tools/turbolizer/index.html
@@ -8,6 +8,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
<meta charset="utf-8">
<title>V8 Turbolizer</title>
<link rel="stylesheet" href="turbo-visualizer.css">
+ <link rel="stylesheet" href="turbo-visualizer-ranges.css">
<link rel="stylesheet" href="tabs.css">
<link rel="icon" type="image/png" href="turbolizer.png">
</head>
@@ -21,6 +22,12 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
<input id="upload-helper" type="file">
<input id="upload" type="image" title="load graph" class="button-input" src="upload-icon.png" alt="upload graph">
</div>
+ <div id="resizer-ranges" class="resizer" style="visibility:hidden;"></div>
+ <div id="ranges" class="content" style="visibility:hidden;"></div>
+ <div id="show-hide-ranges" class="show-hide-pane" style="visibility: hidden">
+ <input id="ranges-expand" type="image" title="show ranges" src="up-arrow.png" class="button-input invisible">
+ <input id="ranges-shrink" type="image" title="hide ranges" src="down-arrow.png" class="button-input">
+ </div>
</div>
<div id="resizer-right" class="resizer"></div>
<div id="right" class="content"></div>
diff --git a/chromium/v8/tools/turbolizer/src/constants.ts b/chromium/v8/tools/turbolizer/src/constants.ts
index ada39ae6b3b..47dee8547ff 100644
--- a/chromium/v8/tools/turbolizer/src/constants.ts
+++ b/chromium/v8/tools/turbolizer/src/constants.ts
@@ -14,6 +14,9 @@ export const GENERATED_PANE_ID = 'right';
export const DISASSEMBLY_PANE_ID = 'disassembly';
export const DISASSEMBLY_COLLAPSE_ID = 'disassembly-shrink';
export const DISASSEMBLY_EXPAND_ID = 'disassembly-expand';
+export const RANGES_PANE_ID = "ranges";
+export const RANGES_COLLAPSE_ID = "ranges-shrink";
+export const RANGES_EXPAND_ID = "ranges-expand";
export const UNICODE_BLOCK = '&#9611;';
export const PROF_COLS = [
{ perc: 0, col: { r: 255, g: 255, b: 255 } },
diff --git a/chromium/v8/tools/turbolizer/src/graphmultiview.ts b/chromium/v8/tools/turbolizer/src/graphmultiview.ts
index 380f7df77db..4f8f6339199 100644
--- a/chromium/v8/tools/turbolizer/src/graphmultiview.ts
+++ b/chromium/v8/tools/turbolizer/src/graphmultiview.ts
@@ -38,6 +38,11 @@ export class GraphMultiView extends View {
return pane;
}
+ hide() {
+ this.hideCurrentPhase();
+ super.hide();
+ }
+
constructor(id, selectionBroker, sourceResolver) {
super(id);
const view = this;
@@ -86,7 +91,9 @@ export class GraphMultiView extends View {
}
show() {
- super.show();
+ // Insert before is used so that the display is inserted before the
+ // resizer for the RangeView.
+ this.container.insertBefore(this.divNode, this.container.firstChild);
this.initializeSelect();
const lastPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase");
const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex);
diff --git a/chromium/v8/tools/turbolizer/src/range-view.ts b/chromium/v8/tools/turbolizer/src/range-view.ts
new file mode 100644
index 00000000000..17058e4f3b2
--- /dev/null
+++ b/chromium/v8/tools/turbolizer/src/range-view.ts
@@ -0,0 +1,938 @@
+// Copyright 2020 the V8 project 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 { createElement } from "../src/util";
+import { SequenceView } from "../src/sequence-view";
+import { RegisterAllocation, Range, ChildRange, Interval } from "../src/source-resolver";
+
+class Constants {
+ // Determines how many rows each div group holds for the purposes of
+ // hiding by syncHidden.
+ static readonly ROW_GROUP_SIZE = 20;
+ static readonly POSITIONS_PER_INSTRUCTION = 4;
+ static readonly FIXED_REGISTER_LABEL_WIDTH = 6;
+
+ static readonly INTERVAL_TEXT_FOR_NONE = "none";
+ static readonly INTERVAL_TEXT_FOR_CONST = "const";
+ static readonly INTERVAL_TEXT_FOR_STACK = "stack:";
+}
+
+// This class holds references to the HTMLElements that represent each cell.
+class Grid {
+ elements: Array<Array<HTMLElement>>;
+
+ constructor() {
+ this.elements = [];
+ }
+
+ setRow(row: number, elementsRow: Array<HTMLElement>) {
+ this.elements[row] = elementsRow;
+ }
+
+ getCell(row: number, column: number) {
+ return this.elements[row][column];
+ }
+
+ getInterval(row: number, column: number) {
+ // The cell is within an inner wrapper div which is within the interval div.
+ return this.getCell(row, column).parentElement.parentElement;
+ }
+}
+
+// This class is used as a wrapper to hide the switch between the
+// two different Grid objects used, one for each phase,
+// before and after register allocation.
+class GridAccessor {
+ sequenceView: SequenceView;
+ grids: Map<number, Grid>;
+
+ constructor(sequenceView: SequenceView) {
+ this.sequenceView = sequenceView;
+ this.grids = new Map<number, Grid>();
+ }
+
+ private currentGrid() {
+ return this.grids.get(this.sequenceView.currentPhaseIndex);
+ }
+
+ getAnyGrid() {
+ return this.grids.values().next().value;
+ }
+
+ hasGrid() {
+ return this.grids.has(this.sequenceView.currentPhaseIndex);
+ }
+
+ addGrid(grid: Grid) {
+ if (this.hasGrid()) console.warn("Overwriting existing Grid.");
+ this.grids.set(this.sequenceView.currentPhaseIndex, grid);
+ }
+
+ getCell(row: number, column: number) {
+ return this.currentGrid().getCell(row, column);
+ }
+
+ getInterval(row: number, column: number) {
+ return this.currentGrid().getInterval(row, column);
+ }
+}
+
+// This class is used as a wrapper to access the interval HTMLElements
+class IntervalElementsAccessor {
+ sequenceView: SequenceView;
+ map: Map<number, Array<HTMLElement>>;
+
+ constructor(sequenceView: SequenceView) {
+ this.sequenceView = sequenceView;
+ this.map = new Map<number, Array<HTMLElement>>();
+ }
+
+ private currentIntervals() {
+ const intervals = this.map.get(this.sequenceView.currentPhaseIndex);
+ if (intervals == undefined) {
+ this.map.set(this.sequenceView.currentPhaseIndex, new Array<HTMLElement>());
+ return this.currentIntervals();
+ }
+ return intervals;
+ }
+
+ addInterval(interval: HTMLElement) {
+ this.currentIntervals().push(interval);
+ }
+
+ forEachInterval(callback: (phase: number, interval: HTMLElement) => void) {
+ for (const phase of this.map.keys()) {
+ for (const interval of this.map.get(phase)) {
+ callback(phase, interval);
+ }
+ }
+ }
+}
+
+// A simple class used to hold two Range objects. This is used to allow the two fixed register live
+// ranges of normal and deferred to be easily combined into a single row.
+class RangePair {
+ ranges: [Range, Range];
+
+ constructor(ranges: [Range, Range]) {
+ this.ranges = ranges;
+ }
+
+ forEachRange(callback: (range: Range) => void) {
+ this.ranges.forEach((range: Range) => { if (range) callback(range); });
+ }
+}
+
+// A number of css variables regarding dimensions of HTMLElements are required by RangeView.
+class CSSVariables {
+ positionWidth: number;
+ blockBorderWidth: number;
+
+ constructor() {
+ const getNumberValue = varName => {
+ return parseFloat(getComputedStyle(document.body)
+ .getPropertyValue(varName).match(/[+-]?\d+(\.\d+)?/g)[0]);
+ };
+ this.positionWidth = getNumberValue("--range-position-width");
+ this.blockBorderWidth = getNumberValue("--range-block-border");
+ }
+}
+
+// Store the required data from the blocks JSON.
+class BlocksData {
+ blockBorders: Set<number>;
+ blockInstructionCountMap: Map<number, number>;
+
+ constructor(blocks: Array<any>) {
+ this.blockBorders = new Set<number>();
+ this.blockInstructionCountMap = new Map<number, number>();
+ for (const block of blocks) {
+ this.blockInstructionCountMap.set(block.id, block.instructions.length);
+ const maxInstructionInBlock = block.instructions[block.instructions.length - 1].id;
+ this.blockBorders.add(maxInstructionInBlock);
+ }
+ }
+
+ isInstructionBorder(position: number) {
+ return ((position + 1) % Constants.POSITIONS_PER_INSTRUCTION) == 0;
+ }
+
+ isBlockBorder(position: number) {
+ return this.isInstructionBorder(position)
+ && this.blockBorders.has(Math.floor(position / Constants.POSITIONS_PER_INSTRUCTION));
+ }
+}
+
+class Divs {
+ // Already existing.
+ container: HTMLElement;
+ resizerBar: HTMLElement;
+ snapper: HTMLElement;
+
+ // Created by constructor.
+ content: HTMLElement;
+ // showOnLoad contains all content that may change depending on the JSON.
+ showOnLoad: HTMLElement;
+ xAxisLabel: HTMLElement;
+ yAxisLabel: HTMLElement;
+ registerHeaders: HTMLElement;
+ registers: HTMLElement;
+
+ // Assigned from RangeView.
+ wholeHeader: HTMLElement;
+ positionHeaders: HTMLElement;
+ yAxis: HTMLElement;
+ grid: HTMLElement;
+
+ constructor() {
+ this.container = document.getElementById("ranges");
+ this.resizerBar = document.getElementById("resizer-ranges");
+ this.snapper = document.getElementById("show-hide-ranges");
+
+ this.content = document.createElement("div");
+ this.content.appendChild(this.elementForTitle());
+
+ this.showOnLoad = document.createElement("div");
+ this.showOnLoad.style.visibility = "hidden";
+ this.content.appendChild(this.showOnLoad);
+
+ this.xAxisLabel = createElement("div", "range-header-label-x");
+ this.xAxisLabel.innerText = "Blocks, Instructions, and Positions";
+ this.showOnLoad.appendChild(this.xAxisLabel);
+ this.yAxisLabel = createElement("div", "range-header-label-y");
+ this.yAxisLabel.innerText = "Registers";
+ this.showOnLoad.appendChild(this.yAxisLabel);
+
+ this.registerHeaders = createElement("div", "range-register-labels");
+ this.registers = createElement("div", "range-registers");
+ this.registerHeaders.appendChild(this.registers);
+ }
+
+ elementForTitle() {
+ const titleEl = createElement("div", "range-title-div");
+ const titleBar = createElement("div", "range-title");
+ titleBar.appendChild(createElement("div", "", "Live Ranges"));
+ const titleHelp = createElement("div", "range-title-help", "?");
+ titleHelp.title = "Each row represents a single TopLevelLiveRange (or two if deferred exists)."
+ + "\nEach interval belongs to a LiveRange contained within that row's TopLevelLiveRange."
+ + "\nAn interval is identified by i, the index of the LiveRange within the TopLevelLiveRange,"
+ + "\nand j, the index of the interval within the LiveRange, to give i:j.";
+ titleEl.appendChild(titleBar);
+ titleEl.appendChild(titleHelp);
+ return titleEl;
+ }
+}
+
+class Helper {
+ static virtualRegisterName(registerIndex: string) {
+ return "v" + registerIndex;
+ }
+
+ static fixedRegisterName(range: Range) {
+ return range.child_ranges[0].op.text;
+ }
+
+ static getPositionElementsFromInterval(interval: HTMLElement) {
+ return interval.children[1].children;
+ }
+
+ static forEachFixedRange(source: RegisterAllocation, row: number,
+ callback: (registerIndex: string, row: number, registerName: string,
+ ranges: RangePair) => void) {
+
+ const forEachRangeInMap = (rangeMap: Map<string, Range>) => {
+ // There are two fixed live ranges for each register, one for normal, another for deferred.
+ // These are combined into a single row.
+ const fixedRegisterMap = new Map<string, {ranges: [Range, Range], registerIndex: number}>();
+ for (const [registerIndex, range] of rangeMap) {
+ const registerName = this.fixedRegisterName(range);
+ if (fixedRegisterMap.has(registerName)) {
+ const entry = fixedRegisterMap.get(registerName);
+ entry.ranges[1] = range;
+ // Only use the deferred register index if no normal index exists.
+ if (!range.is_deferred) {
+ entry.registerIndex = parseInt(registerIndex, 10);
+ }
+ } else {
+ fixedRegisterMap.set(registerName, {ranges: [range, undefined],
+ registerIndex: parseInt(registerIndex, 10)});
+ }
+ }
+ // Sort the registers by number.
+ const sortedMap = new Map([...fixedRegisterMap.entries()].sort(([nameA, _], [nameB, __]) => {
+ // Larger numbers create longer strings.
+ if (nameA.length > nameB.length) return 1;
+ if (nameA.length < nameB.length) return -1;
+ // Sort lexicographically if same length.
+ if (nameA > nameB) return 1;
+ if (nameA < nameB) return -1;
+ return 0;
+ }));
+ for (const [registerName, {ranges, registerIndex}] of sortedMap) {
+ callback("" + (-registerIndex - 1), row, registerName, new RangePair(ranges));
+ ++row;
+ }
+ };
+
+ forEachRangeInMap(source.fixedLiveRanges);
+ forEachRangeInMap(source.fixedDoubleLiveRanges);
+
+ return row;
+ }
+}
+
+class RowConstructor {
+ view: RangeView;
+
+ constructor(view: RangeView) {
+ this.view = view;
+ }
+
+ // Constructs the row of HTMLElements for grid while providing a callback for each position
+ // depending on whether that position is the start of an interval or not.
+ // RangePair is used to allow the two fixed register live ranges of normal and deferred to be
+ // easily combined into a single row.
+ construct(grid: Grid, row: number, registerIndex: string, ranges: RangePair,
+ getElementForEmptyPosition: (position: number) => HTMLElement,
+ callbackForInterval: (position: number, interval: HTMLElement) => void) {
+ const positionArray = new Array<HTMLElement>(this.view.numPositions);
+ // Construct all of the new intervals.
+ const intervalMap = this.elementsForIntervals(registerIndex, ranges);
+ for (let position = 0; position < this.view.numPositions; ++position) {
+ const interval = intervalMap.get(position);
+ if (interval == undefined) {
+ positionArray[position] = getElementForEmptyPosition(position);
+ } else {
+ callbackForInterval(position, interval);
+ this.view.intervalsAccessor.addInterval(interval);
+ const intervalPositionElements = Helper.getPositionElementsFromInterval(interval);
+ for (let j = 0; j < intervalPositionElements.length; ++j) {
+ // Point positionsArray to the new elements.
+ positionArray[position + j] = (intervalPositionElements[j] as HTMLElement);
+ }
+ position += intervalPositionElements.length - 1;
+ }
+ }
+ grid.setRow(row, positionArray);
+ ranges.forEachRange((range: Range) => this.setUses(grid, row, range));
+ }
+
+ // This is the main function used to build new intervals.
+ // Returns a map of LifeTimePositions to intervals.
+ private elementsForIntervals(registerIndex: string, ranges: RangePair) {
+ const intervalMap = new Map<number, HTMLElement>();
+ let tooltip = "";
+ ranges.forEachRange((range: Range) => {
+ for (const childRange of range.child_ranges) {
+ switch (childRange.type) {
+ case "none":
+ tooltip = Constants.INTERVAL_TEXT_FOR_NONE;
+ break;
+ case "spill_range":
+ tooltip = Constants.INTERVAL_TEXT_FOR_STACK + registerIndex;
+ break;
+ default:
+ if (childRange.op.type == "constant") {
+ tooltip = Constants.INTERVAL_TEXT_FOR_CONST;
+ } else {
+ if (childRange.op.text) {
+ tooltip = childRange.op.text;
+ } else {
+ tooltip = childRange.op;
+ }
+ }
+ break;
+ }
+ childRange.intervals.forEach((intervalNums, index) => {
+ const interval = new Interval(intervalNums);
+ const intervalEl = this.elementForInterval(childRange, interval, tooltip,
+ index, range.is_deferred);
+ intervalMap.set(interval.start, intervalEl);
+ });
+ }
+ });
+ return intervalMap;
+ }
+
+ private elementForInterval(childRange: ChildRange, interval: Interval,
+ tooltip: string, index: number, isDeferred: boolean): HTMLElement {
+ const intervalEl = createElement("div", "range-interval");
+ const title = childRange.id + ":" + index + " " + tooltip;
+ intervalEl.setAttribute("title", isDeferred ? "deferred: " + title : title);
+ this.setIntervalColor(intervalEl, tooltip);
+ const intervalInnerWrapper = createElement("div", "range-interval-wrapper");
+ intervalEl.style.gridColumn = (interval.start + 1) + " / " + (interval.end + 1);
+ intervalInnerWrapper.style.gridTemplateColumns = "repeat(" + (interval.end - interval.start)
+ + ",calc(" + this.view.cssVariables.positionWidth + "ch + "
+ + this.view.cssVariables.blockBorderWidth + "px)";
+ const intervalTextEl = this.elementForIntervalString(tooltip, interval.end - interval.start);
+ intervalEl.appendChild(intervalTextEl);
+ for (let i = interval.start; i < interval.end; ++i) {
+ const classes = "range-position range-interval-position range-empty"
+ + (this.view.blocksData.isBlockBorder(i) ? " range-block-border" :
+ this.view.blocksData.isInstructionBorder(i) ? " range-instr-border" : "");
+ const positionEl = createElement("div", classes, "_");
+ positionEl.style.gridColumn = (i - interval.start + 1) + "";
+ intervalInnerWrapper.appendChild(positionEl);
+ }
+ intervalEl.appendChild(intervalInnerWrapper);
+ return intervalEl;
+ }
+
+ private setIntervalColor(interval: HTMLElement, tooltip: string) {
+ if (tooltip.includes(Constants.INTERVAL_TEXT_FOR_NONE)) return;
+ if (tooltip.includes(Constants.INTERVAL_TEXT_FOR_STACK + "-")) {
+ interval.style.backgroundColor = "rgb(250, 158, 168)";
+ } else if (tooltip.includes(Constants.INTERVAL_TEXT_FOR_STACK)) {
+ interval.style.backgroundColor = "rgb(250, 158, 100)";
+ } else if (tooltip.includes(Constants.INTERVAL_TEXT_FOR_CONST)) {
+ interval.style.backgroundColor = "rgb(153, 158, 230)";
+ } else {
+ interval.style.backgroundColor = "rgb(153, 220, 168)";
+ }
+ }
+
+ private elementForIntervalString(tooltip: string, numCells: number) {
+ const spanEl = createElement("span", "range-interval-text");
+ this.setIntervalString(spanEl, tooltip, numCells);
+ return spanEl;
+ }
+
+ // Each interval displays a string of information about it.
+ private setIntervalString(spanEl: HTMLElement, tooltip: string, numCells: number) {
+ const spacePerCell = this.view.cssVariables.positionWidth;
+ // One character space is removed to accommodate for padding.
+ const spaceAvailable = (numCells * spacePerCell) - 0.5;
+ let str = tooltip + "";
+ const length = tooltip.length;
+ spanEl.style.width = null;
+ let paddingLeft = null;
+ // Add padding if possible
+ if (length <= spaceAvailable) {
+ paddingLeft = (length == spaceAvailable) ? "0.5ch" : "1ch";
+ } else {
+ str = "";
+ }
+ spanEl.style.paddingTop = null;
+ spanEl.style.paddingLeft = paddingLeft;
+ spanEl.innerHTML = str;
+ }
+
+ private setUses(grid: Grid, row: number, range: Range) {
+ for (const liveRange of range.child_ranges) {
+ if (liveRange.uses) {
+ for (const use of liveRange.uses) {
+ grid.getCell(row, use).classList.toggle("range-use", true);
+ }
+ }
+ }
+ }
+}
+
+class RangeViewConstructor {
+ view: RangeView;
+ gridTemplateColumns: string;
+ grid: Grid;
+
+ // Group the rows in divs to make hiding/showing divs more efficient.
+ currentGroup: HTMLElement;
+ currentPlaceholderGroup: HTMLElement;
+
+ constructor(rangeView: RangeView) {
+ this.view = rangeView;
+ }
+
+ construct() {
+ this.gridTemplateColumns = "repeat(" + this.view.numPositions
+ + ",calc(" + this.view.cssVariables.positionWidth + "ch + "
+ + this.view.cssVariables.blockBorderWidth + "px)";
+
+ this.grid = new Grid();
+ this.view.gridAccessor.addGrid(this.grid);
+
+ this.view.divs.wholeHeader = this.elementForHeader();
+ this.view.divs.showOnLoad.appendChild(this.view.divs.wholeHeader);
+
+ const gridContainer = document.createElement("div");
+ this.view.divs.grid = this.elementForGrid();
+ this.view.divs.yAxis = createElement("div", "range-y-axis");
+ this.view.divs.yAxis.appendChild(this.view.divs.registerHeaders);
+ this.view.divs.yAxis.onscroll = () => {
+ this.view.scrollHandler.syncScroll(ToSync.TOP, this.view.divs.yAxis, this.view.divs.grid);
+ this.view.scrollHandler.saveScroll();
+ };
+ gridContainer.appendChild(this.view.divs.yAxis);
+ gridContainer.appendChild(this.view.divs.grid);
+ this.view.divs.showOnLoad.appendChild(gridContainer);
+
+ this.resetGroups();
+ let row = 0;
+ row = this.addVirtualRanges(row);
+ this.addFixedRanges(row);
+ }
+
+ // The following three functions are for constructing the groups which the rows are contained
+ // within and which make up the grid. This is so as to allow groups of rows to easily be displayed
+ // and hidden for performance reasons. As rows are constructed, they are added to the currentGroup
+ // div. Each row in currentGroup is matched with an equivalent placeholder row in
+ // currentPlaceholderGroup that will be shown when currentGroup is hidden so as to maintain the
+ // dimensions and scroll positions of the grid.
+
+ private resetGroups () {
+ this.currentGroup = createElement("div", "range-positions-group range-hidden");
+ this.currentPlaceholderGroup = createElement("div", "range-positions-group");
+ }
+
+ private appendGroupsToGrid() {
+ this.view.divs.grid.appendChild(this.currentPlaceholderGroup);
+ this.view.divs.grid.appendChild(this.currentGroup);
+ }
+
+ private addRowToGroup(row: number, rowEl: HTMLElement) {
+ this.currentGroup.appendChild(rowEl);
+ this.currentPlaceholderGroup
+ .appendChild(createElement("div", "range-positions range-positions-placeholder", "_"));
+ if ((row + 1) % Constants.ROW_GROUP_SIZE == 0) {
+ this.appendGroupsToGrid();
+ this.resetGroups();
+ }
+ }
+
+ private addVirtualRanges(row: number) {
+ const source = this.view.sequenceView.sequence.register_allocation;
+ for (const [registerIndex, range] of source.liveRanges) {
+ const registerName = Helper.virtualRegisterName(registerIndex);
+ const registerEl = this.elementForVirtualRegister(registerName);
+ this.addRowToGroup(row, this.elementForRow(row, registerIndex,
+ new RangePair([range, undefined])));
+ this.view.divs.registers.appendChild(registerEl);
+ ++row;
+ }
+ return row;
+ }
+
+ private addFixedRanges(row: number) {
+ row = Helper.forEachFixedRange(this.view.sequenceView.sequence.register_allocation, row,
+ (registerIndex: string, row: number,
+ registerName: string, ranges: RangePair) => {
+ const registerEl = this.elementForFixedRegister(registerName);
+ this.addRowToGroup(row, this.elementForRow(row, registerIndex, ranges));
+ this.view.divs.registers.appendChild(registerEl);
+ });
+ if (row % Constants.ROW_GROUP_SIZE != 0) {
+ this.appendGroupsToGrid();
+ }
+ }
+
+ // Each row of positions and intervals associated with a register is contained in a single
+ // HTMLElement. RangePair is used to allow the two fixed register live ranges of normal and
+ // deferred to be easily combined into a single row.
+ private elementForRow(row: number, registerIndex: string, ranges: RangePair) {
+ const rowEl = createElement("div", "range-positions");
+ rowEl.style.gridTemplateColumns = this.gridTemplateColumns;
+
+ const getElementForEmptyPosition = (position: number) => {
+ const blockBorder = this.view.blocksData.isBlockBorder(position);
+ const classes = "range-position range-empty "
+ + (blockBorder ? "range-block-border" :
+ this.view.blocksData.isInstructionBorder(position) ? "range-instr-border"
+ : "range-position-border");
+ const positionEl = createElement("div", classes, "_");
+ positionEl.style.gridColumn = (position + 1) + "";
+ rowEl.appendChild(positionEl);
+ return positionEl;
+ };
+
+ const callbackForInterval = (_, interval: HTMLElement) => {
+ rowEl.appendChild(interval);
+ };
+
+ this.view.rowConstructor.construct(this.grid, row, registerIndex, ranges,
+ getElementForEmptyPosition, callbackForInterval);
+ return rowEl;
+ }
+
+ private elementForVirtualRegister(registerName: string) {
+ const regEl = createElement("div", "range-reg", registerName);
+ regEl.setAttribute("title", registerName);
+ return regEl;
+ }
+
+ private elementForFixedRegister(registerName: string) {
+ let text = registerName;
+ const span = "".padEnd(Constants.FIXED_REGISTER_LABEL_WIDTH - text.length, "_");
+ text = "HW - <span class='range-transparent'>" + span + "</span>" + text;
+ const regEl = createElement("div", "range-reg");
+ regEl.innerHTML = text;
+ regEl.setAttribute("title", registerName);
+ return regEl;
+ }
+
+ // The header element contains the three headers for the LifeTimePosition axis.
+ private elementForHeader() {
+ const headerEl = createElement("div", "range-header");
+ this.view.divs.positionHeaders = createElement("div", "range-position-labels");
+
+ this.view.divs.positionHeaders.appendChild(this.elementForBlockHeader());
+ this.view.divs.positionHeaders.appendChild(this.elementForInstructionHeader());
+ this.view.divs.positionHeaders.appendChild(this.elementForPositionHeader());
+
+ headerEl.appendChild(this.view.divs.positionHeaders);
+ headerEl.onscroll = () => {
+ this.view.scrollHandler.syncScroll(ToSync.LEFT,
+ this.view.divs.wholeHeader, this.view.divs.grid);
+ this.view.scrollHandler.saveScroll();
+ };
+ return headerEl;
+ }
+
+ // The LifeTimePosition axis shows three headers, for positions, instructions, and blocks.
+
+ private elementForBlockHeader() {
+ const headerEl = createElement("div", "range-block-ids");
+ headerEl.style.gridTemplateColumns = this.gridTemplateColumns;
+
+ const elementForBlockIndex = (index: number, firstInstruction: number, instrCount: number) => {
+ const str = "B" + index;
+ const element =
+ createElement("div", "range-block-id range-header-element range-block-border", str);
+ element.setAttribute("title", str);
+ const firstGridCol = (firstInstruction * Constants.POSITIONS_PER_INSTRUCTION) + 1;
+ const lastGridCol = firstGridCol + (instrCount * Constants.POSITIONS_PER_INSTRUCTION);
+ element.style.gridColumn = firstGridCol + " / " + lastGridCol;
+ return element;
+ };
+
+ let blockIndex = 0;
+ for (let i = 0; i < this.view.sequenceView.numInstructions;) {
+ const instrCount = this.view.blocksData.blockInstructionCountMap.get(blockIndex);
+ headerEl.appendChild(elementForBlockIndex(blockIndex, i, instrCount));
+ ++blockIndex;
+ i += instrCount;
+ }
+ return headerEl;
+ }
+
+ private elementForInstructionHeader() {
+ const headerEl = createElement("div", "range-instruction-ids");
+ headerEl.style.gridTemplateColumns = this.gridTemplateColumns;
+
+ const elementForInstructionIndex = (index: number, isBlockBorder: boolean) => {
+ const classes = "range-instruction-id range-header-element "
+ + (isBlockBorder ? "range-block-border" : "range-instr-border");
+ const element = createElement("div", classes, "" + index);
+ element.setAttribute("title", "" + index);
+ const firstGridCol = (index * Constants.POSITIONS_PER_INSTRUCTION) + 1;
+ element.style.gridColumn = firstGridCol + " / "
+ + (firstGridCol + Constants.POSITIONS_PER_INSTRUCTION);
+ return element;
+ };
+
+ for (let i = 0; i < this.view.sequenceView.numInstructions; ++i) {
+ const blockBorder = this.view.blocksData.blockBorders.has(i);
+ headerEl.appendChild(elementForInstructionIndex(i, blockBorder));
+ }
+ return headerEl;
+ }
+
+ private elementForPositionHeader() {
+ const headerEl = createElement("div", "range-positions range-positions-header");
+ headerEl.style.gridTemplateColumns = this.gridTemplateColumns;
+
+ const elementForPositionIndex = (index: number, isBlockBorder: boolean) => {
+ const classes = "range-position range-header-element " +
+ (isBlockBorder ? "range-block-border"
+ : this.view.blocksData.isInstructionBorder(index) ? "range-instr-border"
+ : "range-position-border");
+ const element = createElement("div", classes, "" + index);
+ element.setAttribute("title", "" + index);
+ return element;
+ };
+
+ for (let i = 0; i < this.view.numPositions; ++i) {
+ headerEl.appendChild(elementForPositionIndex(i, this.view.blocksData.isBlockBorder(i)));
+ }
+ return headerEl;
+ }
+
+ private elementForGrid() {
+ const gridEl = createElement("div", "range-grid");
+ gridEl.onscroll = () => {
+ this.view.scrollHandler.syncScroll(ToSync.TOP, this.view.divs.grid, this.view.divs.yAxis);
+ this.view.scrollHandler.syncScroll(ToSync.LEFT,
+ this.view.divs.grid, this.view.divs.wholeHeader);
+ this.view.scrollHandler.saveScroll();
+ };
+ return gridEl;
+ }
+}
+
+// Handles the work required when the phase is changed.
+// Between before and after register allocation for example.
+class PhaseChangeHandler {
+ view: RangeView;
+
+ constructor(view: RangeView) {
+ this.view = view;
+ }
+
+ // Called when the phase view is switched between before and after register allocation.
+ phaseChange() {
+ if (!this.view.gridAccessor.hasGrid()) {
+ // If this phase view has not been seen yet then the intervals need to be constructed.
+ this.addNewIntervals();
+ }
+ // Show all intervals pertaining to the current phase view.
+ this.view.intervalsAccessor.forEachInterval((phase, interval) => {
+ interval.classList.toggle("range-hidden", phase != this.view.sequenceView.currentPhaseIndex);
+ });
+ }
+
+ private addNewIntervals() {
+ // All Grids should point to the same HTMLElement for empty cells in the grid,
+ // so as to avoid duplication. The current Grid is used to retrieve these elements.
+ const currentGrid = this.view.gridAccessor.getAnyGrid();
+ const newGrid = new Grid();
+ this.view.gridAccessor.addGrid(newGrid);
+ const source = this.view.sequenceView.sequence.register_allocation;
+ let row = 0;
+ for (const [registerIndex, range] of source.liveRanges) {
+ this.addnewIntervalsInRange(currentGrid, newGrid, row, registerIndex,
+ new RangePair([range, undefined]));
+ ++row;
+ }
+ Helper.forEachFixedRange(this.view.sequenceView.sequence.register_allocation, row,
+ (registerIndex, row, _, ranges) => {
+ this.addnewIntervalsInRange(currentGrid, newGrid, row, registerIndex, ranges);
+ });
+ }
+
+ private addnewIntervalsInRange(currentGrid: Grid, newGrid: Grid, row: number,
+ registerIndex: string, ranges: RangePair) {
+ const numReplacements = new Map<HTMLElement, number>();
+
+ const getElementForEmptyPosition = (position: number) => {
+ return currentGrid.getCell(row, position);
+ };
+
+ // Inserts new interval beside existing intervals.
+ const callbackForInterval = (position: number, interval: HTMLElement) => {
+ // Overlapping intervals are placed beside each other and the relevant ones displayed.
+ let currentInterval = currentGrid.getInterval(row, position);
+ // The number of intervals already inserted is tracked so that the inserted intervals
+ // are ordered correctly.
+ const intervalsAlreadyInserted = numReplacements.get(currentInterval);
+ numReplacements.set(currentInterval, intervalsAlreadyInserted ? intervalsAlreadyInserted + 1
+ : 1);
+ if (intervalsAlreadyInserted) {
+ for (let j = 0; j < intervalsAlreadyInserted; ++j) {
+ currentInterval = (currentInterval.nextElementSibling as HTMLElement);
+ }
+ }
+ interval.classList.add("range-hidden");
+ currentInterval.insertAdjacentElement('afterend', interval);
+ };
+
+ this.view.rowConstructor.construct(newGrid, row, registerIndex, ranges,
+ getElementForEmptyPosition, callbackForInterval);
+ }
+}
+
+enum ToSync { LEFT, TOP }
+
+// Handles saving and syncing the scroll positions of the grid.
+class ScrollHandler {
+ divs: Divs;
+ scrollTop: number;
+ scrollLeft: number;
+ scrollTopTimeout: NodeJS.Timeout;
+ scrollLeftTimeout: NodeJS.Timeout;
+ scrollTopFunc: (this: GlobalEventHandlers, ev: Event) => any;
+ scrollLeftFunc: (this: GlobalEventHandlers, ev: Event) => any;
+
+ constructor(divs: Divs) {
+ this.divs = divs;
+ }
+
+ // This function is used to hide the rows which are not currently in view and
+ // so reduce the performance cost of things like hit tests and scrolling.
+ syncHidden() {
+
+ const getOffset = (rowEl: HTMLElement, placeholderRowEl: HTMLElement, isHidden: boolean) => {
+ return isHidden ? placeholderRowEl.offsetTop : rowEl.offsetTop;
+ };
+
+ const toHide = new Array<[HTMLElement, HTMLElement]>();
+
+ const sampleCell = this.divs.registers.children[1] as HTMLElement;
+ const buffer = 2 * sampleCell.clientHeight;
+ const min = this.divs.grid.offsetTop + this.divs.grid.scrollTop - buffer;
+ const max = min + this.divs.grid.clientHeight + buffer;
+
+ // The rows are grouped by being contained within a group div. This is so as to allow
+ // groups of rows to easily be displayed and hidden with less of a performance cost.
+ // Each row in the mainGroup div is matched with an equivalent placeholder row in
+ // the placeholderGroup div that will be shown when mainGroup is hidden so as to maintain
+ // the dimensions and scroll positions of the grid.
+
+ const rangeGroups = this.divs.grid.children;
+ for (let i = 1; i < rangeGroups.length; i += 2) {
+ const mainGroup = rangeGroups[i] as HTMLElement;
+ const placeholderGroup = rangeGroups[i - 1] as HTMLElement;
+ const isHidden = mainGroup.classList.contains("range-hidden");
+ // The offsets are used to calculate whether the group is in view.
+ const offsetMin = getOffset(mainGroup.firstChild as HTMLElement,
+ placeholderGroup.firstChild as HTMLElement, isHidden);
+ const offsetMax = getOffset(mainGroup.lastChild as HTMLElement,
+ placeholderGroup.lastChild as HTMLElement, isHidden);
+ if (offsetMax > min && offsetMin < max) {
+ if (isHidden) {
+ // Show the rows, hide the placeholders.
+ mainGroup.classList.toggle("range-hidden", false);
+ placeholderGroup.classList.toggle("range-hidden", true);
+ }
+ } else if (!isHidden) {
+ // Only hide the rows once the new rows are shown so that scrollLeft is not lost.
+ toHide.push([mainGroup, placeholderGroup]);
+ }
+ }
+ for (const [mainGroup, placeholderGroup] of toHide) {
+ // Hide the rows, show the placeholders.
+ mainGroup.classList.toggle("range-hidden", true);
+ placeholderGroup.classList.toggle("range-hidden", false);
+ }
+ }
+
+ // This function is required to keep the axes labels in line with the grid
+ // content when scrolling.
+ syncScroll(toSync: ToSync, source: HTMLElement, target: HTMLElement) {
+ // Continually delay timeout until scrolling has stopped.
+ toSync == ToSync.TOP ? clearTimeout(this.scrollTopTimeout)
+ : clearTimeout(this.scrollLeftTimeout);
+ if (target.onscroll) {
+ if (toSync == ToSync.TOP) this.scrollTopFunc = target.onscroll;
+ else this.scrollLeftFunc = target.onscroll;
+ }
+ // Clear onscroll to prevent the target syncing back with the source.
+ target.onscroll = null;
+
+ if (toSync == ToSync.TOP) target.scrollTop = source.scrollTop;
+ else target.scrollLeft = source.scrollLeft;
+
+ // Only show / hide the grid content once scrolling has stopped.
+ if (toSync == ToSync.TOP) {
+ this.scrollTopTimeout = setTimeout(() => {
+ target.onscroll = this.scrollTopFunc;
+ this.syncHidden();
+ }, 500);
+ } else {
+ this.scrollLeftTimeout = setTimeout(() => {
+ target.onscroll = this.scrollLeftFunc;
+ this.syncHidden();
+ }, 500);
+ }
+ }
+
+ saveScroll() {
+ this.scrollLeft = this.divs.grid.scrollLeft;
+ this.scrollTop = this.divs.grid.scrollTop;
+ }
+
+ restoreScroll() {
+ if (this.scrollLeft) {
+ this.divs.grid.scrollLeft = this.scrollLeft;
+ this.divs.grid.scrollTop = this.scrollTop;
+ }
+ }
+}
+
+// RangeView displays the live range data as passed in by SequenceView.
+// The data is displayed in a grid format, with the fixed and virtual registers
+// along one axis, and the LifeTimePositions along the other. Each LifeTimePosition
+// is part of an Instruction in SequenceView, which itself is part of an Instruction
+// Block. The live ranges are displayed as intervals, each belonging to a register,
+// and spanning across a certain range of LifeTimePositions.
+// When the phase being displayed changes between before register allocation and
+// after register allocation, only the intervals need to be changed.
+export class RangeView {
+ sequenceView: SequenceView;
+
+ initialized: boolean;
+ isShown: boolean;
+ numPositions: number;
+ cssVariables: CSSVariables;
+ divs: Divs;
+ rowConstructor: RowConstructor;
+ phaseChangeHandler: PhaseChangeHandler;
+ scrollHandler: ScrollHandler;
+ blocksData: BlocksData;
+ intervalsAccessor: IntervalElementsAccessor;
+ gridAccessor: GridAccessor;
+
+ constructor(sequence: SequenceView) {
+ this.initialized = false;
+ this.isShown = false;
+ this.sequenceView = sequence;
+ }
+
+ initializeContent(blocks: Array<any>) {
+ if (!this.initialized) {
+ this.gridAccessor = new GridAccessor(this.sequenceView);
+ this.intervalsAccessor = new IntervalElementsAccessor(this.sequenceView);
+ this.cssVariables = new CSSVariables();
+ this.blocksData = new BlocksData(blocks);
+ this.divs = new Divs();
+ this.scrollHandler = new ScrollHandler(this.divs);
+ this.numPositions = this.sequenceView.numInstructions * Constants.POSITIONS_PER_INSTRUCTION;
+ this.rowConstructor = new RowConstructor(this);
+ const constructor = new RangeViewConstructor(this);
+ constructor.construct();
+ this.phaseChangeHandler = new PhaseChangeHandler(this);
+ this.initialized = true;
+ } else {
+ // If the RangeView has already been initialized then the phase must have
+ // been changed.
+ this.phaseChangeHandler.phaseChange();
+ }
+ }
+
+ show() {
+ if (!this.isShown) {
+ this.isShown = true;
+ this.divs.container.appendChild(this.divs.content);
+ this.divs.resizerBar.style.visibility = "visible";
+ this.divs.container.style.visibility = "visible";
+ this.divs.snapper.style.visibility = "visible";
+ // Dispatch a resize event to ensure that the
+ // panel is shown.
+ window.dispatchEvent(new Event('resize'));
+
+ setTimeout(() => {
+ this.scrollHandler.restoreScroll();
+ this.scrollHandler.syncHidden();
+ this.divs.showOnLoad.style.visibility = "visible";
+ }, 100);
+ }
+ }
+
+ hide() {
+ if (this.initialized) {
+ this.isShown = false;
+ this.divs.container.removeChild(this.divs.content);
+ this.divs.resizerBar.style.visibility = "hidden";
+ this.divs.container.style.visibility = "hidden";
+ this.divs.snapper.style.visibility = "hidden";
+ this.divs.showOnLoad.style.visibility = "hidden";
+ } else {
+ window.document.getElementById('ranges').style.visibility = "hidden";
+ }
+ // Dispatch a resize event to ensure that the
+ // panel is hidden.
+ window.dispatchEvent(new Event('resize'));
+ }
+
+ onresize() {
+ if (this.isShown) this.scrollHandler.syncHidden();
+ }
+}
diff --git a/chromium/v8/tools/turbolizer/src/resizer.ts b/chromium/v8/tools/turbolizer/src/resizer.ts
index 4bd771f7313..ce0519398bb 100644
--- a/chromium/v8/tools/turbolizer/src/resizer.ts
+++ b/chromium/v8/tools/turbolizer/src/resizer.ts
@@ -11,6 +11,8 @@ class Snapper {
sourceCollapse: HTMLElement;
disassemblyExpand: HTMLElement;
disassemblyCollapse: HTMLElement;
+ rangesExpand: HTMLElement;
+ rangesCollapse: HTMLElement;
constructor(resizer: Resizer) {
this.resizer = resizer;
@@ -18,6 +20,8 @@ class Snapper {
this.sourceCollapse = document.getElementById(C.SOURCE_COLLAPSE_ID);
this.disassemblyExpand = document.getElementById(C.DISASSEMBLY_EXPAND_ID);
this.disassemblyCollapse = document.getElementById(C.DISASSEMBLY_COLLAPSE_ID);
+ this.rangesExpand = document.getElementById(C.RANGES_EXPAND_ID);
+ this.rangesCollapse = document.getElementById(C.RANGES_COLLAPSE_ID);
document.getElementById("show-hide-source").addEventListener("click", () => {
this.resizer.resizerLeft.classed("snapped", !this.resizer.resizerLeft.classed("snapped"));
@@ -29,13 +33,20 @@ class Snapper {
this.setDisassemblyExpanded(!this.disassemblyExpand.classList.contains("invisible"));
this.resizer.updatePanes();
});
+ document.getElementById("show-hide-ranges").addEventListener("click", () => {
+ this.resizer.resizerRanges.classed("snapped", !this.resizer.resizerRanges.classed("snapped"));
+ this.setRangesExpanded(!this.rangesExpand.classList.contains("invisible"));
+ this.resizer.updatePanes();
+ });
}
restoreExpandedState(): void {
this.resizer.resizerLeft.classed("snapped", window.sessionStorage.getItem("expandedState-source") == "false");
this.resizer.resizerRight.classed("snapped", window.sessionStorage.getItem("expandedState-disassembly") == "false");
+ this.resizer.resizerRanges.classed("snapped", window.sessionStorage.getItem("expandedState-ranges") == "false");
this.setSourceExpanded(this.getLastExpandedState("source", true));
this.setDisassemblyExpanded(this.getLastExpandedState("disassembly", true));
+ this.setRangesExpanded(this.getLastExpandedState("ranges", true));
}
getLastExpandedState(type: string, defaultState: boolean): boolean {
@@ -48,6 +59,7 @@ class Snapper {
window.sessionStorage.setItem("expandedState-source", `${isSourceExpanded}`);
this.sourceExpand.classList.toggle("invisible", isSourceExpanded);
this.sourceCollapse.classList.toggle("invisible", !isSourceExpanded);
+ document.getElementById("show-hide-ranges").style.marginLeft = isSourceExpanded ? null : "40px";
}
setSourceExpanded(isSourceExpanded: boolean): void {
@@ -65,30 +77,53 @@ class Snapper {
this.disassemblyUpdate(isDisassemblyExpanded);
this.resizer.updateRightWidth();
}
+
+ rangesUpdate(isRangesExpanded: boolean): void {
+ window.sessionStorage.setItem("expandedState-ranges", `${isRangesExpanded}`);
+ this.rangesExpand.classList.toggle("invisible", isRangesExpanded);
+ this.rangesCollapse.classList.toggle("invisible", !isRangesExpanded);
+ }
+
+ setRangesExpanded(isRangesExpanded: boolean): void {
+ this.rangesUpdate(isRangesExpanded);
+ this.resizer.updateRanges();
+ }
}
export class Resizer {
snapper: Snapper;
deadWidth: number;
+ deadHeight: number;
left: HTMLElement;
right: HTMLElement;
+ ranges: HTMLElement;
+ middle: HTMLElement;
sepLeft: number;
sepRight: number;
+ sepRangesHeight: number;
panesUpdatedCallback: () => void;
resizerRight: d3.Selection<HTMLDivElement, any, any, any>;
resizerLeft: d3.Selection<HTMLDivElement, any, any, any>;
+ resizerRanges: d3.Selection<HTMLDivElement, any, any, any>;
private readonly SOURCE_PANE_DEFAULT_PERCENT = 1 / 4;
private readonly DISASSEMBLY_PANE_DEFAULT_PERCENT = 3 / 4;
+ private readonly RANGES_PANE_HEIGHT_DEFAULT_PERCENT = 3 / 4;
+ private readonly RESIZER_RANGES_HEIGHT_BUFFER_PERCENTAGE = 5;
+ private readonly RESIZER_SIZE = document.getElementById("resizer-ranges").offsetHeight;
- constructor(panesUpdatedCallback: () => void, deadWidth: number) {
+ constructor(panesUpdatedCallback: () => void, deadWidth: number, deadHeight: number) {
const resizer = this;
resizer.panesUpdatedCallback = panesUpdatedCallback;
resizer.deadWidth = deadWidth;
+ resizer.deadHeight = deadHeight;
resizer.left = document.getElementById(C.SOURCE_PANE_ID);
resizer.right = document.getElementById(C.GENERATED_PANE_ID);
+ resizer.ranges = document.getElementById(C.RANGES_PANE_ID);
+ resizer.middle = document.getElementById("middle");
resizer.resizerLeft = d3.select('#resizer-left');
resizer.resizerRight = d3.select('#resizer-right');
+ resizer.resizerRanges = d3.select('#resizer-ranges');
// Set default sizes, if they weren't set.
if (window.sessionStorage.getItem("source-pane-percent") === null) {
window.sessionStorage.setItem("source-pane-percent", `${this.SOURCE_PANE_DEFAULT_PERCENT}`);
@@ -96,8 +131,11 @@ export class Resizer {
if (window.sessionStorage.getItem("disassembly-pane-percent") === null) {
window.sessionStorage.setItem("disassembly-pane-percent", `${this.DISASSEMBLY_PANE_DEFAULT_PERCENT}`);
}
+ if (window.sessionStorage.getItem("ranges-pane-height-percent") === null) {
+ window.sessionStorage.setItem("ranges-pane-height-percent", `${this.RANGES_PANE_HEIGHT_DEFAULT_PERCENT}`);
+ }
- this.updateWidths();
+ this.updateSizes();
const dragResizeLeft = d3.drag()
.on('drag', function () {
@@ -151,8 +189,35 @@ export class Resizer {
resizer.resizerRight.classed("dragged", false);
});
resizer.resizerRight.call(dragResizeRight);
+
+ const dragResizeRanges = d3.drag()
+ .on('drag', function () {
+ const y = d3.mouse(this.parentElement)[1];
+ resizer.sepRangesHeight = Math.max(100, Math.min(y, window.innerHeight) - resizer.RESIZER_RANGES_HEIGHT_BUFFER_PERCENTAGE);
+ resizer.updatePanes();
+ })
+ .on('start', function () {
+ resizer.resizerRanges.classed("dragged", true);
+ })
+ .on('end', function () {
+ // If the panel is close enough to the bottom, treat it as if it was pulled all the way to the bottom.
+ const y = d3.mouse(this.parentElement)[1];
+ if (y >= (window.innerHeight - deadHeight)) {
+ resizer.sepRangesHeight = window.innerHeight;
+ resizer.updatePanes();
+ }
+ // Snap if dragged all the way to the bottom.
+ resizer.resizerRanges.classed("snapped", resizer.sepRangesHeight >= window.innerHeight - 1);
+ if (!resizer.isRangesSnapped()) {
+ window.sessionStorage.setItem("ranges-pane-height-percent", `${resizer.sepRangesHeight / window.innerHeight}`);
+ }
+ resizer.snapper.setRangesExpanded(!resizer.isRangesSnapped());
+ resizer.resizerRanges.classed("dragged", false);
+ });
+ resizer.resizerRanges.call(dragResizeRanges);
+
window.onresize = function () {
- resizer.updateWidths();
+ resizer.updateSizes();
resizer.updatePanes();
};
resizer.snapper = new Snapper(resizer);
@@ -167,15 +232,70 @@ export class Resizer {
return this.resizerRight.classed("snapped");
}
+ isRangesSnapped() {
+ return this.resizerRanges.classed("snapped");
+ }
+
+ updateRangesPane() {
+ const clientHeight = window.innerHeight;
+ const rangesIsHidden = this.ranges.style.visibility == "hidden";
+ let resizerSize = this.RESIZER_SIZE;
+ if (rangesIsHidden) {
+ resizerSize = 0;
+ this.sepRangesHeight = clientHeight;
+ }
+
+ const rangeHeight = clientHeight - this.sepRangesHeight;
+ this.ranges.style.height = rangeHeight + 'px';
+ const panelWidth = this.sepRight - this.sepLeft - (2 * resizerSize);
+ this.ranges.style.width = panelWidth + 'px';
+ const multiview = document.getElementById("multiview");
+ if (multiview && multiview.style) {
+ multiview.style.height = (this.sepRangesHeight - resizerSize) + 'px';
+ multiview.style.width = panelWidth + 'px';
+ }
+
+ // Resize the range grid and labels.
+ const rangeGrid = (this.ranges.getElementsByClassName("range-grid")[0] as HTMLElement);
+ if (rangeGrid) {
+ const yAxis = (this.ranges.getElementsByClassName("range-y-axis")[0] as HTMLElement);
+ const rangeHeader = (this.ranges.getElementsByClassName("range-header")[0] as HTMLElement);
+
+ const gridWidth = panelWidth - yAxis.clientWidth;
+ rangeGrid.style.width = Math.floor(gridWidth - 1) + 'px';
+ // Take live ranges' right scrollbar into account.
+ rangeHeader.style.width = (gridWidth - rangeGrid.offsetWidth + rangeGrid.clientWidth - 1) + 'px';
+ // Set resizer to horizontal.
+ this.resizerRanges.style('width', panelWidth + 'px');
+
+ const rangeTitle = (this.ranges.getElementsByClassName("range-title-div")[0] as HTMLElement);
+ const rangeHeaderLabel = (this.ranges.getElementsByClassName("range-header-label-x")[0] as HTMLElement);
+ const gridHeight = rangeHeight - rangeHeader.clientHeight - rangeTitle.clientHeight - rangeHeaderLabel.clientHeight;
+ rangeGrid.style.height = gridHeight + 'px';
+ // Take live ranges' bottom scrollbar into account.
+ yAxis.style.height = (gridHeight - rangeGrid.offsetHeight + rangeGrid.clientHeight) + 'px';
+ }
+ this.resizerRanges.style('ranges', this.ranges.style.height);
+ }
+
updatePanes() {
this.left.style.width = this.sepLeft + 'px';
this.resizerLeft.style('left', this.sepLeft + 'px');
this.right.style.width = (document.body.getBoundingClientRect().width - this.sepRight) + 'px';
this.resizerRight.style('right', (document.body.getBoundingClientRect().width - this.sepRight - 1) + 'px');
-
+ this.updateRangesPane();
this.panesUpdatedCallback();
}
+ updateRanges() {
+ if (this.isRangesSnapped()) {
+ this.sepRangesHeight = window.innerHeight;
+ } else {
+ const sepRangesHeight = window.sessionStorage.getItem("ranges-pane-height-percent");
+ this.sepRangesHeight = window.innerHeight * Number.parseFloat(sepRangesHeight);
+ }
+ }
+
updateLeftWidth() {
if (this.isLeftSnapped()) {
this.sepLeft = 0;
@@ -194,8 +314,9 @@ export class Resizer {
}
}
- updateWidths() {
+ updateSizes() {
this.updateLeftWidth();
this.updateRightWidth();
+ this.updateRanges();
}
}
diff --git a/chromium/v8/tools/turbolizer/src/sequence-view.ts b/chromium/v8/tools/turbolizer/src/sequence-view.ts
index 49b7e9f7b2a..187b162b1cd 100644
--- a/chromium/v8/tools/turbolizer/src/sequence-view.ts
+++ b/chromium/v8/tools/turbolizer/src/sequence-view.ts
@@ -3,12 +3,21 @@
// found in the LICENSE file.
import { Sequence } from "../src/source-resolver";
-import { isIterable } from "../src/util";
+import { createElement } from "../src/util";
import { TextView } from "../src/text-view";
+import { RangeView } from "../src/range-view";
export class SequenceView extends TextView {
sequence: Sequence;
searchInfo: Array<any>;
+ phaseSelect: HTMLSelectElement;
+ numInstructions: number;
+ currentPhaseIndex: number;
+ phaseIndexes: Set<number>;
+ isShown: boolean;
+ rangeView: RangeView;
+ showRangeView: boolean;
+ toggleRangeViewEl: HTMLElement;
createViewElement() {
const pane = document.createElement('div');
@@ -20,6 +29,12 @@ export class SequenceView extends TextView {
constructor(parentId, broker) {
super(parentId, broker);
+ this.numInstructions = 0;
+ this.phaseIndexes = new Set<number>();
+ this.isShown = false;
+ this.showRangeView = false;
+ this.rangeView = null;
+ this.toggleRangeViewEl = this.elementForToggleRangeView();
}
attachSelection(s) {
@@ -37,34 +52,58 @@ export class SequenceView extends TextView {
return this.selection.detachSelection();
}
+ show() {
+ this.currentPhaseIndex = this.phaseSelect.selectedIndex;
+ if (!this.isShown) {
+ this.isShown = true;
+ this.phaseIndexes.add(this.currentPhaseIndex);
+ this.container.appendChild(this.divNode);
+ this.container.getElementsByClassName("graph-toolbox")[0].appendChild(this.toggleRangeViewEl);
+ }
+ if (this.showRangeView) this.rangeView.show();
+ }
+
+ hide() {
+ // A single SequenceView object is used for two phases (i.e before and after
+ // register allocation), tracking the indexes lets the redundant hides and
+ // shows be avoided when switching between the two.
+ this.currentPhaseIndex = this.phaseSelect.selectedIndex;
+ if (!this.phaseIndexes.has(this.currentPhaseIndex)) {
+ this.isShown = false;
+ this.container.removeChild(this.divNode);
+ this.container.getElementsByClassName("graph-toolbox")[0].removeChild(this.toggleRangeViewEl);
+ if (this.showRangeView) this.rangeView.hide();
+ }
+ }
+
+ onresize() {
+ if (this.showRangeView) this.rangeView.onresize();
+ }
+
initializeContent(data, rememberedSelection) {
this.divNode.innerHTML = '';
this.sequence = data.sequence;
this.searchInfo = [];
- this.divNode.addEventListener('click', (e: MouseEvent) => {
+ this.divNode.onclick = (e: MouseEvent) => {
if (!(e.target instanceof HTMLElement)) return;
const instructionId = Number.parseInt(e.target.dataset.instructionId, 10);
if (!instructionId) return;
if (!e.shiftKey) this.broker.broadcastClear(null);
this.broker.broadcastInstructionSelect(null, [instructionId], true);
- });
+ };
+ this.phaseSelect = (document.getElementById('phase-select') as HTMLSelectElement);
+ this.currentPhaseIndex = this.phaseSelect.selectedIndex;
+
this.addBlocks(this.sequence.blocks);
+ const lastBlock = this.sequence.blocks[this.sequence.blocks.length - 1];
+ this.numInstructions = lastBlock.instructions[lastBlock.instructions.length - 1].id + 1;
+ this.addRangeView();
this.attachSelection(rememberedSelection);
this.show();
}
elementForBlock(block) {
const view = this;
- function createElement(tag: string, cls: string | Array<string>, content?: string) {
- const el = document.createElement(tag);
- if (isIterable(cls)) {
- for (const c of cls) el.classList.add(c);
- } else {
- el.classList.add(cls);
- }
- if (content != undefined) el.innerHTML = content;
- return el;
- }
function mkLinkHandler(id, handler) {
return function (e) {
@@ -84,16 +123,33 @@ export class SequenceView extends TextView {
return mkLinkHandler(text, view.selectionHandler);
}
+ function elementForOperandWithSpan(span, text, searchInfo, isVirtual) {
+ const selectionText = isVirtual ? "virt_" + text : text;
+ span.onclick = mkOperandLinkHandler(selectionText);
+ searchInfo.push(text);
+ view.addHtmlElementForNodeId(selectionText, span);
+ const container = createElement("div", "");
+ container.appendChild(span);
+ return container;
+ }
+
function elementForOperand(operand, searchInfo) {
- const text = operand.text;
- const operandEl = createElement("div", ["parameter", "tag", "clickable", operand.type], text);
+ let isVirtual = false;
+ let className = "parameter tag clickable " + operand.type;
+ if (operand.text[0] == 'v' && !(operand.tooltip && operand.tooltip.includes("Float"))) {
+ isVirtual = true;
+ className += " virtual-reg";
+ }
+ const span = createElement("span", className, operand.text);
if (operand.tooltip) {
- operandEl.setAttribute("title", operand.tooltip);
+ span.setAttribute("title", operand.tooltip);
}
- operandEl.onclick = mkOperandLinkHandler(text);
- searchInfo.push(text);
- view.addHtmlElementForNodeId(text, operandEl);
- return operandEl;
+ return elementForOperandWithSpan(span, operand.text, searchInfo, isVirtual);
+ }
+
+ function elementForPhiOperand(text, searchInfo) {
+ const span = createElement("span", "parameter tag clickable virtual-reg", text);
+ return elementForOperandWithSpan(span, text, searchInfo, true);
}
function elementForInstruction(instruction, searchInfo) {
@@ -115,7 +171,7 @@ export class SequenceView extends TextView {
const gapEl = createElement("div", "gap", "gap");
let hasGaps = false;
for (const gap of instruction.gaps) {
- const moves = createElement("div", ["comma-sep-list", "gap-move"]);
+ const moves = createElement("div", "comma-sep-list gap-move");
for (const move of gap) {
hasGaps = true;
const moveEl = createElement("div", "move");
@@ -137,7 +193,7 @@ export class SequenceView extends TextView {
instContentsEl.appendChild(instEl);
if (instruction.outputs.length > 0) {
- const outputs = createElement("div", ["comma-sep-list", "input-output-list"]);
+ const outputs = createElement("div", "comma-sep-list input-output-list");
for (const output of instruction.outputs) {
const outputEl = elementForOperand(output, searchInfo);
outputs.appendChild(outputEl);
@@ -147,8 +203,8 @@ export class SequenceView extends TextView {
instEl.appendChild(assignEl);
}
- let text = instruction.opcode + instruction.flags;
- const instLabel = createElement("div", "node-label", text)
+ const text = instruction.opcode + instruction.flags;
+ const instLabel = createElement("div", "node-label", text);
if (instruction.opcode == "ArchNop" && instruction.outputs.length == 1 && instruction.outputs[0].tooltip) {
instLabel.innerText = instruction.outputs[0].tooltip;
}
@@ -158,7 +214,7 @@ export class SequenceView extends TextView {
instEl.appendChild(instLabel);
if (instruction.inputs.length > 0) {
- const inputs = createElement("div", ["comma-sep-list", "input-output-list"]);
+ const inputs = createElement("div", "comma-sep-list input-output-list");
for (const input of instruction.inputs) {
const inputEl = elementForOperand(input, searchInfo);
inputs.appendChild(inputEl);
@@ -167,7 +223,7 @@ export class SequenceView extends TextView {
}
if (instruction.temps.length > 0) {
- const temps = createElement("div", ["comma-sep-list", "input-output-list", "temps"]);
+ const temps = createElement("div", "comma-sep-list input-output-list temps");
for (const temp of instruction.temps) {
const tempEl = elementForOperand(temp, searchInfo);
temps.appendChild(tempEl);
@@ -181,12 +237,12 @@ export class SequenceView extends TextView {
const sequenceBlock = createElement("div", "schedule-block");
sequenceBlock.classList.toggle("deferred", block.deferred);
- const blockId = createElement("div", ["block-id", "com", "clickable"], block.id);
+ const blockId = createElement("div", "block-id com clickable", block.id);
blockId.onclick = mkBlockLinkHandler(block.id);
sequenceBlock.appendChild(blockId);
- const blockPred = createElement("div", ["predecessor-list", "block-list", "comma-sep-list"]);
+ const blockPred = createElement("div", "predecessor-list block-list comma-sep-list");
for (const pred of block.predecessors) {
- const predEl = createElement("div", ["block-id", "com", "clickable"], pred);
+ const predEl = createElement("div", "block-id com clickable", pred);
predEl.onclick = mkBlockLinkHandler(pred);
blockPred.appendChild(predEl);
}
@@ -211,7 +267,7 @@ export class SequenceView extends TextView {
phiEl.appendChild(assignEl);
for (const input of phi.operands) {
- const inputEl = createElement("div", ["parameter", "tag", "clickable"], input);
+ const inputEl = elementForPhiOperand(input, this.searchInfo);
phiEl.appendChild(inputEl);
}
}
@@ -221,9 +277,9 @@ export class SequenceView extends TextView {
instructions.appendChild(elementForInstruction(instruction, this.searchInfo));
}
sequenceBlock.appendChild(instructions);
- const blockSucc = createElement("div", ["successor-list", "block-list", "comma-sep-list"]);
+ const blockSucc = createElement("div", "successor-list block-list comma-sep-list");
for (const succ of block.successors) {
- const succEl = createElement("div", ["block-id", "com", "clickable"], succ);
+ const succEl = createElement("div", "block-id com clickable", succ);
succEl.onclick = mkBlockLinkHandler(succ);
blockSucc.appendChild(succEl);
}
@@ -239,6 +295,63 @@ export class SequenceView extends TextView {
}
}
+ addRangeView() {
+ const preventRangeView = reason => {
+ const toggleRangesInput = this.toggleRangeViewEl.firstChild as HTMLInputElement;
+ if (this.rangeView) {
+ toggleRangesInput.checked = false;
+ this.toggleRangeView(toggleRangesInput);
+ }
+ toggleRangesInput.disabled = true;
+ this.toggleRangeViewEl.style.textDecoration = "line-through";
+ this.toggleRangeViewEl.setAttribute("title", reason);
+ };
+
+ if (this.sequence.register_allocation) {
+ if (!this.rangeView) {
+ this.rangeView = new RangeView(this);
+ }
+ const source = this.sequence.register_allocation;
+ if (source.fixedLiveRanges.size == 0 && source.liveRanges.size == 0) {
+ preventRangeView("No live ranges to show");
+ } else if (this.numInstructions >= 249) {
+ // This is due to the css grid-column being limited to 1000 columns.
+ // Performance issues would otherwise impose some limit.
+ // TODO(george.wort@arm.com): Allow the user to specify an instruction range
+ // to display that spans less than 249 instructions.
+ preventRangeView(
+ "Live range display is only supported for sequences with less than 249 instructions");
+ }
+ if (this.showRangeView) {
+ this.rangeView.initializeContent(this.sequence.blocks);
+ }
+ } else {
+ preventRangeView("No live range data provided");
+ }
+ }
+
+ elementForToggleRangeView() {
+ const toggleRangeViewEl = createElement("label", "", "show live ranges");
+ const toggleRangesInput = createElement("input", "range-toggle-show") as HTMLInputElement;
+ toggleRangesInput.setAttribute("type", "checkbox");
+ toggleRangesInput.oninput = () => this.toggleRangeView(toggleRangesInput);
+ toggleRangeViewEl.insertBefore(toggleRangesInput, toggleRangeViewEl.firstChild);
+ return toggleRangeViewEl;
+ }
+
+ toggleRangeView(toggleRangesInput: HTMLInputElement) {
+ toggleRangesInput.disabled = true;
+ this.showRangeView = toggleRangesInput.checked;
+ if (this.showRangeView) {
+ this.rangeView.initializeContent(this.sequence.blocks);
+ this.rangeView.show();
+ } else {
+ this.rangeView.hide();
+ }
+ window.dispatchEvent(new Event('resize'));
+ toggleRangesInput.disabled = false;
+ }
+
searchInputAction(searchBar, e) {
e.stopPropagation();
this.selectionHandler.clear();
diff --git a/chromium/v8/tools/turbolizer/src/source-resolver.ts b/chromium/v8/tools/turbolizer/src/source-resolver.ts
index 588eea5b995..085b44f3a75 100644
--- a/chromium/v8/tools/turbolizer/src/source-resolver.ts
+++ b/chromium/v8/tools/turbolizer/src/source-resolver.ts
@@ -83,7 +83,7 @@ interface InstructionsPhase {
instructionOffsetToPCOffset?: any;
blockIdtoInstructionRange?: any;
nodeIdToInstructionRange?: any;
- codeOffsetsInfo?: CodeOffsetsInfo
+ codeOffsetsInfo?: CodeOffsetsInfo;
}
interface GraphPhase {
@@ -100,8 +100,44 @@ export interface Schedule {
nodes: Array<any>;
}
+export class Interval {
+ start: number;
+ end: number;
+
+ constructor(numbers: [number, number]) {
+ this.start = numbers[0];
+ this.end = numbers[1];
+ }
+}
+
+export interface ChildRange {
+ id: string;
+ type: string;
+ op: any;
+ intervals: Array<[number, number]>;
+ uses: Array<number>;
+}
+
+export interface Range {
+ child_ranges: Array<ChildRange>;
+ is_deferred: boolean;
+}
+
+export class RegisterAllocation {
+ fixedDoubleLiveRanges: Map<string, Range>;
+ fixedLiveRanges: Map<string, Range>;
+ liveRanges: Map<string, Range>;
+
+ constructor(registerAllocation) {
+ this.fixedDoubleLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_double_live_ranges));
+ this.fixedLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_live_ranges));
+ this.liveRanges = new Map<string, Range>(Object.entries(registerAllocation.live_ranges));
+ }
+}
+
export interface Sequence {
blocks: Array<any>;
+ register_allocation: RegisterAllocation;
}
class CodeOffsetsInfo {
@@ -720,8 +756,11 @@ export class SourceResolver {
phase.schedule = state;
return phase;
}
+
parseSequence(phase) {
- phase.sequence = { blocks: phase.blocks };
+ phase.sequence = { blocks: phase.blocks,
+ register_allocation: phase.register_allocation ? new RegisterAllocation(phase.register_allocation)
+ : undefined };
return phase;
}
}
diff --git a/chromium/v8/tools/turbolizer/src/turbo-visualizer.ts b/chromium/v8/tools/turbolizer/src/turbo-visualizer.ts
index 22753cdda50..2dd01c28f74 100644
--- a/chromium/v8/tools/turbolizer/src/turbo-visualizer.ts
+++ b/chromium/v8/tools/turbolizer/src/turbo-visualizer.ts
@@ -18,7 +18,7 @@ window.onload = function () {
let sourceViews: Array<CodeView> = [];
let selectionBroker: SelectionBroker = null;
let sourceResolver: SourceResolver = null;
- const resizer = new Resizer(panesUpdatedCallback, 75);
+ const resizer = new Resizer(panesUpdatedCallback, 75, 75);
const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID);
const sourceTabs = new Tabs(sourceTabsContainer);
sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
@@ -48,6 +48,9 @@ window.onload = function () {
sourceViews.forEach(sv => sv.hide());
if (multiview) multiview.hide();
multiview = null;
+ document.getElementById("ranges").innerHTML = '';
+ document.getElementById('ranges').style.visibility = "hidden";
+ document.getElementById('show-hide-ranges').style.visibility = "hidden";
if (disassemblyView) disassemblyView.hide();
sourceViews = [];
sourceResolver = new SourceResolver();
diff --git a/chromium/v8/tools/turbolizer/src/util.ts b/chromium/v8/tools/turbolizer/src/util.ts
index d9c8dcdce05..8d2fc845115 100644
--- a/chromium/v8/tools/turbolizer/src/util.ts
+++ b/chromium/v8/tools/turbolizer/src/util.ts
@@ -91,3 +91,10 @@ export function measureText(text: string) {
export function interpolate(val: number, max: number, start: number, end: number) {
return start + (end - start) * (val / max);
}
+
+export function createElement(tag: string, cls: string, content?: string) {
+ const el = document.createElement(tag);
+ el.className = cls;
+ if (content != undefined) el.innerText = content;
+ return el;
+}
diff --git a/chromium/v8/tools/turbolizer/turbo-visualizer-ranges.css b/chromium/v8/tools/turbolizer/turbo-visualizer-ranges.css
new file mode 100644
index 00000000000..03976e2ec54
--- /dev/null
+++ b/chromium/v8/tools/turbolizer/turbo-visualizer-ranges.css
@@ -0,0 +1,238 @@
+/* CSS specific to the live ranges div associated with
+ the RangeView typescript class in src/range-view.ts. */
+
+:root {
+ --range-y-axis-width: 18ch;
+ --range-position-width: 3.5ch;
+ --range-block-border: 6px;
+ --range-instr-border: 3px;
+ --range-position-border: 1px;
+}
+
+.range-bold {
+ font-weight: bold;
+ color: black;
+}
+
+#ranges {
+ font-family: monospace;
+ min-height: auto;
+ overflow: hidden;
+}
+
+#resizer-ranges {
+ height: 10px;
+}
+
+.range-title-div {
+ padding: 2ch 2ch 2ch 2ch;
+ white-space: nowrap;
+ overflow: auto;
+}
+
+.range-title {
+ text-decoration: underline;
+ font-weight: bold;
+ font-size: large;
+ display: inline-block;
+}
+
+.range-title-help {
+ margin-left: 2ch;
+ width: 1ch;
+ padding: 0 0.25ch;
+ border: 1px dotted black;
+ color: slategray;
+ display: inline-block;
+}
+
+input.range-toggle-show {
+ vertical-align: middle;
+}
+
+.range-header-label-x {
+ text-align: center;
+ margin-left: 13ch;
+}
+
+.range-header-label-y {
+ width: 11ch;
+ float: left;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ margin-left: 6ch;
+ margin-top: 4ch;
+}
+
+.range-y-axis {
+ display: inline-block;
+ width: var(--range-y-axis-width);
+ overflow: hidden;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+.range-header {
+ display: flex;
+ overflow: hidden;
+ height: 8ch;
+ margin-left: var(--range-y-axis-width);
+}
+
+.range-position-labels,
+.range-register-labels {
+ background-color: lightgray;
+}
+
+.range-register-labels {
+ float: right;
+}
+
+.range-position-labels {
+ margin-top: auto;
+}
+
+.range-registers {
+ float: right;
+ overflow: hidden;
+ text-align: right;
+}
+
+.range-positions-header,
+.range-instruction-ids,
+.range-block-ids {
+ overflow: hidden;
+ white-space: nowrap;
+ display: grid;
+ grid-gap: 0;
+}
+
+.range-reg {
+ width: 13ch;
+ text-align: right;
+}
+
+.range-reg::after {
+ content: ":";
+}
+
+.range-grid {
+ overflow: auto;
+ display: inline-block;
+ white-space: nowrap;
+}
+
+.range-block-id {
+ display: inline-block;
+ text-align: center;
+}
+
+.range-instruction-id {
+ display: inline-block;
+ text-align: center;
+}
+
+.range-position {
+ display: inline-block;
+ text-align: center;
+ z-index: 1;
+}
+
+.range-transparent,
+.range-position.range-empty {
+ color: transparent;
+}
+
+.range-block-id:hover,
+.range-instruction-id:hover,
+.range-reg:hover,
+.range-position:hover {
+ background-color: rgba(0, 0, 255, 0.10);
+}
+
+.range-position.range-header-element {
+ border-bottom: 2px solid rgb(109, 107, 107);
+}
+
+.range-block-id,
+.range-instruction-id,
+.range-reg,
+.range-interval,
+.range-position {
+ position: relative;
+ border: var(--range-position-border) solid rgb(109, 107, 107);
+}
+
+.range-block-id,
+.range-instruction-id,
+.range-interval,
+.range-position {
+ border-left: 0;
+}
+
+.range-block-ids > .range-block-id:first-child,
+.range-instruction-ids > .range-instruction-id:first-child,
+.range-positions > .range-position:first-child {
+ border-left: var(--range-position-border) solid rgb(109, 107, 107);
+}
+
+.range-position.range-interval-position {
+ border: none;
+}
+
+.range-interval-text {
+ position: absolute;
+ padding-left: 0.5ch;
+ z-index: 2;
+ pointer-events: none
+}
+
+.range-position.range-use {
+ border-left: var(--range-instr-border) solid red;
+}
+
+.range-block-border,
+.range-block-border.range-position.range-interval-position:last-child {
+ border-right: var(--range-block-border) solid rgb(109, 107, 107);
+}
+
+.range-block-border.range-position.range-interval-position {
+ border-right: var(--range-block-border) solid transparent;
+}
+
+.range-instr-border,
+.range-instr-border.range-position.range-interval-position:last-child {
+ border-right: var(--range-instr-border) solid rgb(109, 107, 107);
+}
+
+.range-instr-border.range-position.range-interval-position {
+ border-right: var(--range-instr-border) solid transparent;
+}
+
+.range,
+.range-interval,
+.range-interval-wrapper,
+.range-positions {
+ white-space: nowrap;
+ display: inline-block;
+}
+
+.range-interval-wrapper,
+.range-positions {
+ display: grid;
+ grid-gap: 0;
+}
+
+.range-interval {
+ background-color: rgb(153, 158, 168);
+}
+
+.range-hidden {
+ display: none !important;
+}
+
+.range-positions-placeholder {
+ width: 100%;
+ border: var(--range-position-border) solid transparent;
+ color: transparent;
+} \ No newline at end of file
diff --git a/chromium/v8/tools/turbolizer/turbo-visualizer.css b/chromium/v8/tools/turbolizer/turbo-visualizer.css
index 6fb6da3b794..c7da769eb5d 100644
--- a/chromium/v8/tools/turbolizer/turbo-visualizer.css
+++ b/chromium/v8/tools/turbolizer/turbo-visualizer.css
@@ -342,6 +342,13 @@ input:hover,
background-color: #F8F8F8;
user-select: none;
flex: 1;
+ z-index: 7;
+}
+
+#middle.display-inline-flex,
+#middle.display-inline-flex #multiview,
+#middle.display-inline-flex #ranges {
+ display: inline-flex;
}
.viewpane {
@@ -351,11 +358,6 @@ input:hover,
flex-direction: column;
}
-.multiview {
- width: 100%;
-}
-
-
#show-hide-disassembly {
right: 0;
}
@@ -423,6 +425,10 @@ text {
dominant-baseline: text-before-edge;
}
+.tab-content {
+ z-index: 6;
+}
+
.resizer {
z-index: 10;
width: 10px;
@@ -595,6 +601,10 @@ text {
padding-right: .5ex;
}
+.instruction span {
+ padding-right: 0;
+}
+
.phi-label,
.instruction-id {
display: inline-block;
@@ -626,6 +636,10 @@ text {
display: inline-block;
}
+.phi span {
+ padding-right: 0;
+}
+
.gap .gap-move {
padding-left: .5ex;
padding-right: .5ex;
@@ -639,6 +653,10 @@ text {
content: ")";
}
+.virtual-reg {
+ outline: 1px dotted blue;
+}
+
.parameter.constant {
outline: 1px dotted red;
}
diff --git a/chromium/v8/tools/turbolizer/up-arrow.png b/chromium/v8/tools/turbolizer/up-arrow.png
new file mode 100644
index 00000000000..68cb14e80b3
--- /dev/null
+++ b/chromium/v8/tools/turbolizer/up-arrow.png
Binary files differ
diff --git a/chromium/v8/tools/v8_presubmit.py b/chromium/v8/tools/v8_presubmit.py
index 40677b3a0a3..587f8082a5f 100755
--- a/chromium/v8/tools/v8_presubmit.py
+++ b/chromium/v8/tools/v8_presubmit.py
@@ -59,12 +59,16 @@ from testrunner.local import utils
# We now run our own header guard check in PRESUBMIT.py.
# build/include_what_you_use: Started giving false positives for variables
# named "string" and "map" assuming that you needed to include STL headers.
+# runtime/references: As of May 2020 the C++ style guide suggests using
+# references for out parameters, see
+# https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs.
LINT_RULES = """
-build/header_guard
-build/include_what_you_use
-readability/fn_size
-readability/multiline_comment
+-runtime/references
-whitespace/comments
""".split()
diff --git a/chromium/v8/tools/v8heapconst.py b/chromium/v8/tools/v8heapconst.py
index ac69cfb836b..57b1cf26bbd 100644
--- a/chromium/v8/tools/v8heapconst.py
+++ b/chromium/v8/tools/v8heapconst.py
@@ -72,31 +72,31 @@ INSTANCE_TYPES = {
108: "TEMPLATE_OBJECT_DESCRIPTION_TYPE",
109: "TUPLE2_TYPE",
110: "WASM_CAPI_FUNCTION_DATA_TYPE",
- 111: "WASM_DEBUG_INFO_TYPE",
- 112: "WASM_EXCEPTION_TAG_TYPE",
- 113: "WASM_EXPORTED_FUNCTION_DATA_TYPE",
- 114: "WASM_INDIRECT_FUNCTION_TABLE_TYPE",
- 115: "WASM_JS_FUNCTION_DATA_TYPE",
- 116: "WASM_VALUE_TYPE",
- 117: "FIXED_ARRAY_TYPE",
- 118: "HASH_TABLE_TYPE",
- 119: "EPHEMERON_HASH_TABLE_TYPE",
- 120: "GLOBAL_DICTIONARY_TYPE",
- 121: "NAME_DICTIONARY_TYPE",
- 122: "NUMBER_DICTIONARY_TYPE",
- 123: "ORDERED_HASH_MAP_TYPE",
- 124: "ORDERED_HASH_SET_TYPE",
- 125: "ORDERED_NAME_DICTIONARY_TYPE",
- 126: "SIMPLE_NUMBER_DICTIONARY_TYPE",
- 127: "STRING_TABLE_TYPE",
- 128: "CLOSURE_FEEDBACK_CELL_ARRAY_TYPE",
- 129: "OBJECT_BOILERPLATE_DESCRIPTION_TYPE",
- 130: "SCOPE_INFO_TYPE",
- 131: "SCRIPT_CONTEXT_TABLE_TYPE",
- 132: "BYTE_ARRAY_TYPE",
- 133: "BYTECODE_ARRAY_TYPE",
- 134: "FIXED_DOUBLE_ARRAY_TYPE",
- 135: "INTERNAL_CLASS_WITH_SMI_ELEMENTS_TYPE",
+ 111: "WASM_EXCEPTION_TAG_TYPE",
+ 112: "WASM_EXPORTED_FUNCTION_DATA_TYPE",
+ 113: "WASM_INDIRECT_FUNCTION_TABLE_TYPE",
+ 114: "WASM_JS_FUNCTION_DATA_TYPE",
+ 115: "WASM_VALUE_TYPE",
+ 116: "FIXED_ARRAY_TYPE",
+ 117: "HASH_TABLE_TYPE",
+ 118: "EPHEMERON_HASH_TABLE_TYPE",
+ 119: "GLOBAL_DICTIONARY_TYPE",
+ 120: "NAME_DICTIONARY_TYPE",
+ 121: "NUMBER_DICTIONARY_TYPE",
+ 122: "ORDERED_HASH_MAP_TYPE",
+ 123: "ORDERED_HASH_SET_TYPE",
+ 124: "ORDERED_NAME_DICTIONARY_TYPE",
+ 125: "SIMPLE_NUMBER_DICTIONARY_TYPE",
+ 126: "STRING_TABLE_TYPE",
+ 127: "CLOSURE_FEEDBACK_CELL_ARRAY_TYPE",
+ 128: "OBJECT_BOILERPLATE_DESCRIPTION_TYPE",
+ 129: "SCOPE_INFO_TYPE",
+ 130: "SCRIPT_CONTEXT_TABLE_TYPE",
+ 131: "BYTE_ARRAY_TYPE",
+ 132: "BYTECODE_ARRAY_TYPE",
+ 133: "FIXED_DOUBLE_ARRAY_TYPE",
+ 134: "INTERNAL_CLASS_WITH_SMI_ELEMENTS_TYPE",
+ 135: "SLOPPY_ARGUMENTS_ELEMENTS_TYPE",
136: "AWAIT_CONTEXT_TYPE",
137: "BLOCK_CONTEXT_TYPE",
138: "CATCH_CONTEXT_TYPE",
@@ -107,46 +107,48 @@ INSTANCE_TYPES = {
143: "NATIVE_CONTEXT_TYPE",
144: "SCRIPT_CONTEXT_TYPE",
145: "WITH_CONTEXT_TYPE",
- 146: "SMALL_ORDERED_HASH_MAP_TYPE",
- 147: "SMALL_ORDERED_HASH_SET_TYPE",
- 148: "SMALL_ORDERED_NAME_DICTIONARY_TYPE",
- 149: "EXPORTED_SUB_CLASS_BASE_TYPE",
- 150: "EXPORTED_SUB_CLASS_TYPE",
- 151: "SOURCE_TEXT_MODULE_TYPE",
- 152: "SYNTHETIC_MODULE_TYPE",
- 153: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE",
- 154: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE",
- 155: "WEAK_FIXED_ARRAY_TYPE",
- 156: "TRANSITION_ARRAY_TYPE",
- 157: "CELL_TYPE",
- 158: "CODE_TYPE",
- 159: "CODE_DATA_CONTAINER_TYPE",
- 160: "COVERAGE_INFO_TYPE",
- 161: "DESCRIPTOR_ARRAY_TYPE",
- 162: "EMBEDDER_DATA_ARRAY_TYPE",
- 163: "FEEDBACK_METADATA_TYPE",
- 164: "FEEDBACK_VECTOR_TYPE",
- 165: "FILLER_TYPE",
- 166: "FREE_SPACE_TYPE",
- 167: "INTERNAL_CLASS_TYPE",
- 168: "INTERNAL_CLASS_WITH_STRUCT_ELEMENTS_TYPE",
- 169: "MAP_TYPE",
- 170: "PREPARSE_DATA_TYPE",
- 171: "PROPERTY_ARRAY_TYPE",
- 172: "PROPERTY_CELL_TYPE",
- 173: "SHARED_FUNCTION_INFO_TYPE",
- 174: "SMI_BOX_TYPE",
- 175: "SMI_PAIR_TYPE",
- 176: "SORT_STATE_TYPE",
- 177: "WASM_ARRAY_TYPE",
- 178: "WASM_STRUCT_TYPE",
- 179: "WEAK_ARRAY_LIST_TYPE",
- 180: "WEAK_CELL_TYPE",
- 181: "JS_PROXY_TYPE",
+ 146: "EXPORTED_SUB_CLASS_BASE_TYPE",
+ 147: "EXPORTED_SUB_CLASS_TYPE",
+ 148: "EXPORTED_SUB_CLASS2_TYPE",
+ 149: "SMALL_ORDERED_HASH_MAP_TYPE",
+ 150: "SMALL_ORDERED_HASH_SET_TYPE",
+ 151: "SMALL_ORDERED_NAME_DICTIONARY_TYPE",
+ 152: "SOURCE_TEXT_MODULE_TYPE",
+ 153: "SYNTHETIC_MODULE_TYPE",
+ 154: "UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE",
+ 155: "UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE",
+ 156: "WEAK_FIXED_ARRAY_TYPE",
+ 157: "TRANSITION_ARRAY_TYPE",
+ 158: "CELL_TYPE",
+ 159: "CODE_TYPE",
+ 160: "CODE_DATA_CONTAINER_TYPE",
+ 161: "COVERAGE_INFO_TYPE",
+ 162: "DESCRIPTOR_ARRAY_TYPE",
+ 163: "EMBEDDER_DATA_ARRAY_TYPE",
+ 164: "FEEDBACK_METADATA_TYPE",
+ 165: "FEEDBACK_VECTOR_TYPE",
+ 166: "FILLER_TYPE",
+ 167: "FREE_SPACE_TYPE",
+ 168: "INTERNAL_CLASS_TYPE",
+ 169: "INTERNAL_CLASS_WITH_STRUCT_ELEMENTS_TYPE",
+ 170: "MAP_TYPE",
+ 171: "ON_HEAP_BASIC_BLOCK_PROFILER_DATA_TYPE",
+ 172: "PREPARSE_DATA_TYPE",
+ 173: "PROPERTY_ARRAY_TYPE",
+ 174: "PROPERTY_CELL_TYPE",
+ 175: "SHARED_FUNCTION_INFO_TYPE",
+ 176: "SMI_BOX_TYPE",
+ 177: "SMI_PAIR_TYPE",
+ 178: "SORT_STATE_TYPE",
+ 179: "WASM_ARRAY_TYPE",
+ 180: "WASM_STRUCT_TYPE",
+ 181: "WEAK_ARRAY_LIST_TYPE",
+ 182: "WEAK_CELL_TYPE",
+ 183: "JS_PROXY_TYPE",
1057: "JS_OBJECT_TYPE",
- 182: "JS_GLOBAL_OBJECT_TYPE",
- 183: "JS_GLOBAL_PROXY_TYPE",
- 184: "JS_MODULE_NAMESPACE_TYPE",
+ 184: "JS_GLOBAL_OBJECT_TYPE",
+ 185: "JS_GLOBAL_PROXY_TYPE",
+ 186: "JS_MODULE_NAMESPACE_TYPE",
1040: "JS_SPECIAL_API_OBJECT_TYPE",
1041: "JS_PRIMITIVE_WRAPPER_TYPE",
1042: "JS_MAP_KEY_ITERATOR_TYPE",
@@ -164,195 +166,196 @@ INSTANCE_TYPES = {
1054: "JS_WEAK_MAP_TYPE",
1055: "JS_WEAK_SET_TYPE",
1056: "JS_API_OBJECT_TYPE",
- 1058: "JS_AGGREGATE_ERROR_TYPE",
- 1059: "JS_ARGUMENTS_OBJECT_TYPE",
- 1060: "JS_ARRAY_TYPE",
- 1061: "JS_ARRAY_BUFFER_TYPE",
- 1062: "JS_ARRAY_ITERATOR_TYPE",
- 1063: "JS_ASYNC_FROM_SYNC_ITERATOR_TYPE",
- 1064: "JS_COLLATOR_TYPE",
- 1065: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
- 1066: "JS_DATE_TYPE",
- 1067: "JS_DATE_TIME_FORMAT_TYPE",
- 1068: "JS_DISPLAY_NAMES_TYPE",
- 1069: "JS_ERROR_TYPE",
- 1070: "JS_FINALIZATION_REGISTRY_TYPE",
- 1071: "JS_LIST_FORMAT_TYPE",
- 1072: "JS_LOCALE_TYPE",
- 1073: "JS_MESSAGE_OBJECT_TYPE",
- 1074: "JS_NUMBER_FORMAT_TYPE",
- 1075: "JS_PLURAL_RULES_TYPE",
- 1076: "JS_PROMISE_TYPE",
- 1077: "JS_REG_EXP_TYPE",
- 1078: "JS_REG_EXP_STRING_ITERATOR_TYPE",
- 1079: "JS_RELATIVE_TIME_FORMAT_TYPE",
- 1080: "JS_SEGMENT_ITERATOR_TYPE",
- 1081: "JS_SEGMENTER_TYPE",
- 1082: "JS_STRING_ITERATOR_TYPE",
- 1083: "JS_V8_BREAK_ITERATOR_TYPE",
- 1084: "JS_WEAK_REF_TYPE",
- 1085: "WASM_EXCEPTION_OBJECT_TYPE",
- 1086: "WASM_GLOBAL_OBJECT_TYPE",
- 1087: "WASM_INSTANCE_OBJECT_TYPE",
- 1088: "WASM_MEMORY_OBJECT_TYPE",
- 1089: "WASM_MODULE_OBJECT_TYPE",
- 1090: "WASM_TABLE_OBJECT_TYPE",
- 1091: "JS_BOUND_FUNCTION_TYPE",
- 1092: "JS_FUNCTION_TYPE",
+ 1058: "JS_ARGUMENTS_OBJECT_TYPE",
+ 1059: "JS_ARRAY_TYPE",
+ 1060: "JS_ARRAY_BUFFER_TYPE",
+ 1061: "JS_ARRAY_ITERATOR_TYPE",
+ 1062: "JS_ASYNC_FROM_SYNC_ITERATOR_TYPE",
+ 1063: "JS_COLLATOR_TYPE",
+ 1064: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
+ 1065: "JS_DATE_TYPE",
+ 1066: "JS_DATE_TIME_FORMAT_TYPE",
+ 1067: "JS_DISPLAY_NAMES_TYPE",
+ 1068: "JS_ERROR_TYPE",
+ 1069: "JS_FINALIZATION_REGISTRY_TYPE",
+ 1070: "JS_LIST_FORMAT_TYPE",
+ 1071: "JS_LOCALE_TYPE",
+ 1072: "JS_MESSAGE_OBJECT_TYPE",
+ 1073: "JS_NUMBER_FORMAT_TYPE",
+ 1074: "JS_PLURAL_RULES_TYPE",
+ 1075: "JS_PROMISE_TYPE",
+ 1076: "JS_REG_EXP_TYPE",
+ 1077: "JS_REG_EXP_STRING_ITERATOR_TYPE",
+ 1078: "JS_RELATIVE_TIME_FORMAT_TYPE",
+ 1079: "JS_SEGMENT_ITERATOR_TYPE",
+ 1080: "JS_SEGMENTER_TYPE",
+ 1081: "JS_STRING_ITERATOR_TYPE",
+ 1082: "JS_V8_BREAK_ITERATOR_TYPE",
+ 1083: "JS_WEAK_REF_TYPE",
+ 1084: "WASM_EXCEPTION_OBJECT_TYPE",
+ 1085: "WASM_GLOBAL_OBJECT_TYPE",
+ 1086: "WASM_INSTANCE_OBJECT_TYPE",
+ 1087: "WASM_MEMORY_OBJECT_TYPE",
+ 1088: "WASM_MODULE_OBJECT_TYPE",
+ 1089: "WASM_TABLE_OBJECT_TYPE",
+ 1090: "JS_BOUND_FUNCTION_TYPE",
+ 1091: "JS_FUNCTION_TYPE",
}
# List of known V8 maps.
KNOWN_MAPS = {
- ("read_only_space", 0x00121): (166, "FreeSpaceMap"),
- ("read_only_space", 0x00149): (169, "MetaMap"),
+ ("read_only_space", 0x00121): (167, "FreeSpaceMap"),
+ ("read_only_space", 0x00149): (170, "MetaMap"),
("read_only_space", 0x0018d): (67, "NullMap"),
- ("read_only_space", 0x001c5): (161, "DescriptorArrayMap"),
- ("read_only_space", 0x001f5): (155, "WeakFixedArrayMap"),
- ("read_only_space", 0x0021d): (165, "OnePointerFillerMap"),
- ("read_only_space", 0x00245): (165, "TwoPointerFillerMap"),
+ ("read_only_space", 0x001c5): (162, "DescriptorArrayMap"),
+ ("read_only_space", 0x001f5): (156, "WeakFixedArrayMap"),
+ ("read_only_space", 0x0021d): (166, "OnePointerFillerMap"),
+ ("read_only_space", 0x00245): (166, "TwoPointerFillerMap"),
("read_only_space", 0x00289): (67, "UninitializedMap"),
("read_only_space", 0x002cd): (8, "OneByteInternalizedStringMap"),
("read_only_space", 0x00329): (67, "UndefinedMap"),
("read_only_space", 0x0035d): (66, "HeapNumberMap"),
("read_only_space", 0x003a1): (67, "TheHoleMap"),
("read_only_space", 0x00401): (67, "BooleanMap"),
- ("read_only_space", 0x00489): (132, "ByteArrayMap"),
- ("read_only_space", 0x004b1): (117, "FixedArrayMap"),
- ("read_only_space", 0x004d9): (117, "FixedCOWArrayMap"),
- ("read_only_space", 0x00501): (118, "HashTableMap"),
+ ("read_only_space", 0x00489): (131, "ByteArrayMap"),
+ ("read_only_space", 0x004b1): (116, "FixedArrayMap"),
+ ("read_only_space", 0x004d9): (116, "FixedCOWArrayMap"),
+ ("read_only_space", 0x00501): (117, "HashTableMap"),
("read_only_space", 0x00529): (64, "SymbolMap"),
("read_only_space", 0x00551): (40, "OneByteStringMap"),
- ("read_only_space", 0x00579): (130, "ScopeInfoMap"),
- ("read_only_space", 0x005a1): (173, "SharedFunctionInfoMap"),
- ("read_only_space", 0x005c9): (158, "CodeMap"),
- ("read_only_space", 0x005f1): (157, "CellMap"),
- ("read_only_space", 0x00619): (172, "GlobalPropertyCellMap"),
+ ("read_only_space", 0x00579): (129, "ScopeInfoMap"),
+ ("read_only_space", 0x005a1): (175, "SharedFunctionInfoMap"),
+ ("read_only_space", 0x005c9): (159, "CodeMap"),
+ ("read_only_space", 0x005f1): (158, "CellMap"),
+ ("read_only_space", 0x00619): (174, "GlobalPropertyCellMap"),
("read_only_space", 0x00641): (70, "ForeignMap"),
- ("read_only_space", 0x00669): (156, "TransitionArrayMap"),
+ ("read_only_space", 0x00669): (157, "TransitionArrayMap"),
("read_only_space", 0x00691): (45, "ThinOneByteStringMap"),
- ("read_only_space", 0x006b9): (164, "FeedbackVectorMap"),
+ ("read_only_space", 0x006b9): (165, "FeedbackVectorMap"),
("read_only_space", 0x0070d): (67, "ArgumentsMarkerMap"),
("read_only_space", 0x0076d): (67, "ExceptionMap"),
("read_only_space", 0x007c9): (67, "TerminationExceptionMap"),
("read_only_space", 0x00831): (67, "OptimizedOutMap"),
("read_only_space", 0x00891): (67, "StaleRegisterMap"),
- ("read_only_space", 0x008d5): (131, "ScriptContextTableMap"),
- ("read_only_space", 0x008fd): (128, "ClosureFeedbackCellArrayMap"),
- ("read_only_space", 0x00925): (163, "FeedbackMetadataArrayMap"),
- ("read_only_space", 0x0094d): (117, "ArrayListMap"),
+ ("read_only_space", 0x008d5): (130, "ScriptContextTableMap"),
+ ("read_only_space", 0x008fd): (127, "ClosureFeedbackCellArrayMap"),
+ ("read_only_space", 0x00925): (164, "FeedbackMetadataArrayMap"),
+ ("read_only_space", 0x0094d): (116, "ArrayListMap"),
("read_only_space", 0x00975): (65, "BigIntMap"),
- ("read_only_space", 0x0099d): (129, "ObjectBoilerplateDescriptionMap"),
- ("read_only_space", 0x009c5): (133, "BytecodeArrayMap"),
- ("read_only_space", 0x009ed): (159, "CodeDataContainerMap"),
- ("read_only_space", 0x00a15): (160, "CoverageInfoMap"),
- ("read_only_space", 0x00a3d): (134, "FixedDoubleArrayMap"),
- ("read_only_space", 0x00a65): (120, "GlobalDictionaryMap"),
+ ("read_only_space", 0x0099d): (128, "ObjectBoilerplateDescriptionMap"),
+ ("read_only_space", 0x009c5): (132, "BytecodeArrayMap"),
+ ("read_only_space", 0x009ed): (160, "CodeDataContainerMap"),
+ ("read_only_space", 0x00a15): (161, "CoverageInfoMap"),
+ ("read_only_space", 0x00a3d): (133, "FixedDoubleArrayMap"),
+ ("read_only_space", 0x00a65): (119, "GlobalDictionaryMap"),
("read_only_space", 0x00a8d): (96, "ManyClosuresCellMap"),
- ("read_only_space", 0x00ab5): (117, "ModuleInfoMap"),
- ("read_only_space", 0x00add): (121, "NameDictionaryMap"),
+ ("read_only_space", 0x00ab5): (116, "ModuleInfoMap"),
+ ("read_only_space", 0x00add): (120, "NameDictionaryMap"),
("read_only_space", 0x00b05): (96, "NoClosuresCellMap"),
- ("read_only_space", 0x00b2d): (122, "NumberDictionaryMap"),
+ ("read_only_space", 0x00b2d): (121, "NumberDictionaryMap"),
("read_only_space", 0x00b55): (96, "OneClosureCellMap"),
- ("read_only_space", 0x00b7d): (123, "OrderedHashMapMap"),
- ("read_only_space", 0x00ba5): (124, "OrderedHashSetMap"),
- ("read_only_space", 0x00bcd): (125, "OrderedNameDictionaryMap"),
- ("read_only_space", 0x00bf5): (170, "PreparseDataMap"),
- ("read_only_space", 0x00c1d): (171, "PropertyArrayMap"),
+ ("read_only_space", 0x00b7d): (122, "OrderedHashMapMap"),
+ ("read_only_space", 0x00ba5): (123, "OrderedHashSetMap"),
+ ("read_only_space", 0x00bcd): (124, "OrderedNameDictionaryMap"),
+ ("read_only_space", 0x00bf5): (172, "PreparseDataMap"),
+ ("read_only_space", 0x00c1d): (173, "PropertyArrayMap"),
("read_only_space", 0x00c45): (92, "SideEffectCallHandlerInfoMap"),
("read_only_space", 0x00c6d): (92, "SideEffectFreeCallHandlerInfoMap"),
("read_only_space", 0x00c95): (92, "NextCallSideEffectFreeCallHandlerInfoMap"),
- ("read_only_space", 0x00cbd): (126, "SimpleNumberDictionaryMap"),
- ("read_only_space", 0x00ce5): (117, "SloppyArgumentsElementsMap"),
- ("read_only_space", 0x00d0d): (146, "SmallOrderedHashMapMap"),
- ("read_only_space", 0x00d35): (147, "SmallOrderedHashSetMap"),
- ("read_only_space", 0x00d5d): (148, "SmallOrderedNameDictionaryMap"),
- ("read_only_space", 0x00d85): (151, "SourceTextModuleMap"),
- ("read_only_space", 0x00dad): (127, "StringTableMap"),
- ("read_only_space", 0x00dd5): (152, "SyntheticModuleMap"),
- ("read_only_space", 0x00dfd): (154, "UncompiledDataWithoutPreparseDataMap"),
- ("read_only_space", 0x00e25): (153, "UncompiledDataWithPreparseDataMap"),
- ("read_only_space", 0x00e4d): (179, "WeakArrayListMap"),
- ("read_only_space", 0x00e75): (119, "EphemeronHashTableMap"),
- ("read_only_space", 0x00e9d): (162, "EmbedderDataArrayMap"),
- ("read_only_space", 0x00ec5): (180, "WeakCellMap"),
- ("read_only_space", 0x00eed): (32, "StringMap"),
- ("read_only_space", 0x00f15): (41, "ConsOneByteStringMap"),
- ("read_only_space", 0x00f3d): (33, "ConsStringMap"),
- ("read_only_space", 0x00f65): (37, "ThinStringMap"),
- ("read_only_space", 0x00f8d): (35, "SlicedStringMap"),
- ("read_only_space", 0x00fb5): (43, "SlicedOneByteStringMap"),
- ("read_only_space", 0x00fdd): (34, "ExternalStringMap"),
- ("read_only_space", 0x01005): (42, "ExternalOneByteStringMap"),
- ("read_only_space", 0x0102d): (50, "UncachedExternalStringMap"),
- ("read_only_space", 0x01055): (0, "InternalizedStringMap"),
- ("read_only_space", 0x0107d): (2, "ExternalInternalizedStringMap"),
- ("read_only_space", 0x010a5): (10, "ExternalOneByteInternalizedStringMap"),
- ("read_only_space", 0x010cd): (18, "UncachedExternalInternalizedStringMap"),
- ("read_only_space", 0x010f5): (26, "UncachedExternalOneByteInternalizedStringMap"),
- ("read_only_space", 0x0111d): (58, "UncachedExternalOneByteStringMap"),
- ("read_only_space", 0x01145): (67, "SelfReferenceMarkerMap"),
+ ("read_only_space", 0x00cbd): (125, "SimpleNumberDictionaryMap"),
+ ("read_only_space", 0x00ce5): (149, "SmallOrderedHashMapMap"),
+ ("read_only_space", 0x00d0d): (150, "SmallOrderedHashSetMap"),
+ ("read_only_space", 0x00d35): (151, "SmallOrderedNameDictionaryMap"),
+ ("read_only_space", 0x00d5d): (152, "SourceTextModuleMap"),
+ ("read_only_space", 0x00d85): (126, "StringTableMap"),
+ ("read_only_space", 0x00dad): (153, "SyntheticModuleMap"),
+ ("read_only_space", 0x00dd5): (155, "UncompiledDataWithoutPreparseDataMap"),
+ ("read_only_space", 0x00dfd): (154, "UncompiledDataWithPreparseDataMap"),
+ ("read_only_space", 0x00e25): (181, "WeakArrayListMap"),
+ ("read_only_space", 0x00e4d): (118, "EphemeronHashTableMap"),
+ ("read_only_space", 0x00e75): (163, "EmbedderDataArrayMap"),
+ ("read_only_space", 0x00e9d): (182, "WeakCellMap"),
+ ("read_only_space", 0x00ec5): (32, "StringMap"),
+ ("read_only_space", 0x00eed): (41, "ConsOneByteStringMap"),
+ ("read_only_space", 0x00f15): (33, "ConsStringMap"),
+ ("read_only_space", 0x00f3d): (37, "ThinStringMap"),
+ ("read_only_space", 0x00f65): (35, "SlicedStringMap"),
+ ("read_only_space", 0x00f8d): (43, "SlicedOneByteStringMap"),
+ ("read_only_space", 0x00fb5): (34, "ExternalStringMap"),
+ ("read_only_space", 0x00fdd): (42, "ExternalOneByteStringMap"),
+ ("read_only_space", 0x01005): (50, "UncachedExternalStringMap"),
+ ("read_only_space", 0x0102d): (0, "InternalizedStringMap"),
+ ("read_only_space", 0x01055): (2, "ExternalInternalizedStringMap"),
+ ("read_only_space", 0x0107d): (10, "ExternalOneByteInternalizedStringMap"),
+ ("read_only_space", 0x010a5): (18, "UncachedExternalInternalizedStringMap"),
+ ("read_only_space", 0x010cd): (26, "UncachedExternalOneByteInternalizedStringMap"),
+ ("read_only_space", 0x010f5): (58, "UncachedExternalOneByteStringMap"),
+ ("read_only_space", 0x0111d): (67, "SelfReferenceMarkerMap"),
+ ("read_only_space", 0x01145): (67, "BasicBlockCountersMarkerMap"),
("read_only_space", 0x01179): (95, "EnumCacheMap"),
("read_only_space", 0x011c9): (86, "ArrayBoilerplateDescriptionMap"),
- ("read_only_space", 0x012c5): (98, "InterceptorInfoMap"),
- ("read_only_space", 0x03335): (71, "PromiseFulfillReactionJobTaskMap"),
- ("read_only_space", 0x0335d): (72, "PromiseRejectReactionJobTaskMap"),
- ("read_only_space", 0x03385): (73, "CallableTaskMap"),
- ("read_only_space", 0x033ad): (74, "CallbackTaskMap"),
- ("read_only_space", 0x033d5): (75, "PromiseResolveThenableJobTaskMap"),
- ("read_only_space", 0x033fd): (78, "FunctionTemplateInfoMap"),
- ("read_only_space", 0x03425): (79, "ObjectTemplateInfoMap"),
- ("read_only_space", 0x0344d): (80, "AccessCheckInfoMap"),
- ("read_only_space", 0x03475): (81, "AccessorInfoMap"),
- ("read_only_space", 0x0349d): (82, "AccessorPairMap"),
- ("read_only_space", 0x034c5): (83, "AliasedArgumentsEntryMap"),
- ("read_only_space", 0x034ed): (84, "AllocationMementoMap"),
- ("read_only_space", 0x03515): (87, "AsmWasmDataMap"),
- ("read_only_space", 0x0353d): (88, "AsyncGeneratorRequestMap"),
- ("read_only_space", 0x03565): (89, "BreakPointMap"),
- ("read_only_space", 0x0358d): (90, "BreakPointInfoMap"),
- ("read_only_space", 0x035b5): (91, "CachedTemplateObjectMap"),
- ("read_only_space", 0x035dd): (93, "ClassPositionsMap"),
- ("read_only_space", 0x03605): (94, "DebugInfoMap"),
- ("read_only_space", 0x0362d): (97, "FunctionTemplateRareDataMap"),
- ("read_only_space", 0x03655): (99, "InterpreterDataMap"),
- ("read_only_space", 0x0367d): (100, "PromiseCapabilityMap"),
- ("read_only_space", 0x036a5): (101, "PromiseReactionMap"),
- ("read_only_space", 0x036cd): (102, "PropertyDescriptorObjectMap"),
- ("read_only_space", 0x036f5): (103, "PrototypeInfoMap"),
- ("read_only_space", 0x0371d): (104, "ScriptMap"),
- ("read_only_space", 0x03745): (105, "SourceTextModuleInfoEntryMap"),
- ("read_only_space", 0x0376d): (106, "StackFrameInfoMap"),
- ("read_only_space", 0x03795): (107, "StackTraceFrameMap"),
- ("read_only_space", 0x037bd): (108, "TemplateObjectDescriptionMap"),
- ("read_only_space", 0x037e5): (109, "Tuple2Map"),
- ("read_only_space", 0x0380d): (110, "WasmCapiFunctionDataMap"),
- ("read_only_space", 0x03835): (111, "WasmDebugInfoMap"),
- ("read_only_space", 0x0385d): (112, "WasmExceptionTagMap"),
- ("read_only_space", 0x03885): (113, "WasmExportedFunctionDataMap"),
- ("read_only_space", 0x038ad): (114, "WasmIndirectFunctionTableMap"),
- ("read_only_space", 0x038d5): (115, "WasmJSFunctionDataMap"),
- ("read_only_space", 0x038fd): (116, "WasmValueMap"),
- ("read_only_space", 0x03925): (167, "InternalClassMap"),
- ("read_only_space", 0x0394d): (175, "SmiPairMap"),
- ("read_only_space", 0x03975): (174, "SmiBoxMap"),
- ("read_only_space", 0x0399d): (149, "ExportedSubClassBaseMap"),
- ("read_only_space", 0x039c5): (150, "ExportedSubClassMap"),
- ("read_only_space", 0x039ed): (68, "AbstractInternalClassSubclass1Map"),
- ("read_only_space", 0x03a15): (69, "AbstractInternalClassSubclass2Map"),
- ("read_only_space", 0x03a3d): (135, "InternalClassWithSmiElementsMap"),
- ("read_only_space", 0x03a65): (168, "InternalClassWithStructElementsMap"),
- ("read_only_space", 0x03a8d): (176, "SortStateMap"),
- ("read_only_space", 0x03ab5): (85, "AllocationSiteWithWeakNextMap"),
- ("read_only_space", 0x03add): (85, "AllocationSiteWithoutWeakNextMap"),
- ("read_only_space", 0x03b05): (76, "LoadHandler1Map"),
- ("read_only_space", 0x03b2d): (76, "LoadHandler2Map"),
- ("read_only_space", 0x03b55): (76, "LoadHandler3Map"),
- ("read_only_space", 0x03b7d): (77, "StoreHandler0Map"),
- ("read_only_space", 0x03ba5): (77, "StoreHandler1Map"),
- ("read_only_space", 0x03bcd): (77, "StoreHandler2Map"),
- ("read_only_space", 0x03bf5): (77, "StoreHandler3Map"),
+ ("read_only_space", 0x012b5): (98, "InterceptorInfoMap"),
+ ("read_only_space", 0x03369): (71, "PromiseFulfillReactionJobTaskMap"),
+ ("read_only_space", 0x03391): (72, "PromiseRejectReactionJobTaskMap"),
+ ("read_only_space", 0x033b9): (73, "CallableTaskMap"),
+ ("read_only_space", 0x033e1): (74, "CallbackTaskMap"),
+ ("read_only_space", 0x03409): (75, "PromiseResolveThenableJobTaskMap"),
+ ("read_only_space", 0x03431): (78, "FunctionTemplateInfoMap"),
+ ("read_only_space", 0x03459): (79, "ObjectTemplateInfoMap"),
+ ("read_only_space", 0x03481): (80, "AccessCheckInfoMap"),
+ ("read_only_space", 0x034a9): (81, "AccessorInfoMap"),
+ ("read_only_space", 0x034d1): (82, "AccessorPairMap"),
+ ("read_only_space", 0x034f9): (83, "AliasedArgumentsEntryMap"),
+ ("read_only_space", 0x03521): (84, "AllocationMementoMap"),
+ ("read_only_space", 0x03549): (87, "AsmWasmDataMap"),
+ ("read_only_space", 0x03571): (88, "AsyncGeneratorRequestMap"),
+ ("read_only_space", 0x03599): (89, "BreakPointMap"),
+ ("read_only_space", 0x035c1): (90, "BreakPointInfoMap"),
+ ("read_only_space", 0x035e9): (91, "CachedTemplateObjectMap"),
+ ("read_only_space", 0x03611): (93, "ClassPositionsMap"),
+ ("read_only_space", 0x03639): (94, "DebugInfoMap"),
+ ("read_only_space", 0x03661): (97, "FunctionTemplateRareDataMap"),
+ ("read_only_space", 0x03689): (99, "InterpreterDataMap"),
+ ("read_only_space", 0x036b1): (100, "PromiseCapabilityMap"),
+ ("read_only_space", 0x036d9): (101, "PromiseReactionMap"),
+ ("read_only_space", 0x03701): (102, "PropertyDescriptorObjectMap"),
+ ("read_only_space", 0x03729): (103, "PrototypeInfoMap"),
+ ("read_only_space", 0x03751): (104, "ScriptMap"),
+ ("read_only_space", 0x03779): (105, "SourceTextModuleInfoEntryMap"),
+ ("read_only_space", 0x037a1): (106, "StackFrameInfoMap"),
+ ("read_only_space", 0x037c9): (107, "StackTraceFrameMap"),
+ ("read_only_space", 0x037f1): (108, "TemplateObjectDescriptionMap"),
+ ("read_only_space", 0x03819): (109, "Tuple2Map"),
+ ("read_only_space", 0x03841): (110, "WasmCapiFunctionDataMap"),
+ ("read_only_space", 0x03869): (111, "WasmExceptionTagMap"),
+ ("read_only_space", 0x03891): (112, "WasmExportedFunctionDataMap"),
+ ("read_only_space", 0x038b9): (113, "WasmIndirectFunctionTableMap"),
+ ("read_only_space", 0x038e1): (114, "WasmJSFunctionDataMap"),
+ ("read_only_space", 0x03909): (115, "WasmValueMap"),
+ ("read_only_space", 0x03931): (135, "SloppyArgumentsElementsMap"),
+ ("read_only_space", 0x03959): (171, "OnHeapBasicBlockProfilerDataMap"),
+ ("read_only_space", 0x03981): (168, "InternalClassMap"),
+ ("read_only_space", 0x039a9): (177, "SmiPairMap"),
+ ("read_only_space", 0x039d1): (176, "SmiBoxMap"),
+ ("read_only_space", 0x039f9): (146, "ExportedSubClassBaseMap"),
+ ("read_only_space", 0x03a21): (147, "ExportedSubClassMap"),
+ ("read_only_space", 0x03a49): (68, "AbstractInternalClassSubclass1Map"),
+ ("read_only_space", 0x03a71): (69, "AbstractInternalClassSubclass2Map"),
+ ("read_only_space", 0x03a99): (134, "InternalClassWithSmiElementsMap"),
+ ("read_only_space", 0x03ac1): (169, "InternalClassWithStructElementsMap"),
+ ("read_only_space", 0x03ae9): (148, "ExportedSubClass2Map"),
+ ("read_only_space", 0x03b11): (178, "SortStateMap"),
+ ("read_only_space", 0x03b39): (85, "AllocationSiteWithWeakNextMap"),
+ ("read_only_space", 0x03b61): (85, "AllocationSiteWithoutWeakNextMap"),
+ ("read_only_space", 0x03b89): (76, "LoadHandler1Map"),
+ ("read_only_space", 0x03bb1): (76, "LoadHandler2Map"),
+ ("read_only_space", 0x03bd9): (76, "LoadHandler3Map"),
+ ("read_only_space", 0x03c01): (77, "StoreHandler0Map"),
+ ("read_only_space", 0x03c29): (77, "StoreHandler1Map"),
+ ("read_only_space", 0x03c51): (77, "StoreHandler2Map"),
+ ("read_only_space", 0x03c79): (77, "StoreHandler3Map"),
("map_space", 0x00121): (1057, "ExternalMap"),
- ("map_space", 0x00149): (1073, "JSMessageObjectMap"),
+ ("map_space", 0x00149): (1072, "JSMessageObjectMap"),
}
# List of known V8 objects.
@@ -381,26 +384,26 @@ KNOWN_OBJECTS = {
("read_only_space", 0x011b1): "EmptyObjectBoilerplateDescription",
("read_only_space", 0x011bd): "EmptyArrayBoilerplateDescription",
("read_only_space", 0x011f1): "EmptyClosureFeedbackCellArray",
- ("read_only_space", 0x011f9): "EmptySloppyArgumentsElements",
- ("read_only_space", 0x01209): "EmptySlowElementDictionary",
- ("read_only_space", 0x0122d): "EmptyOrderedHashMap",
- ("read_only_space", 0x01241): "EmptyOrderedHashSet",
- ("read_only_space", 0x01255): "EmptyFeedbackMetadata",
- ("read_only_space", 0x01261): "EmptyPropertyCell",
- ("read_only_space", 0x01275): "EmptyPropertyDictionary",
- ("read_only_space", 0x0129d): "NoOpInterceptorInfo",
- ("read_only_space", 0x012ed): "EmptyWeakArrayList",
- ("read_only_space", 0x012f9): "InfinityValue",
- ("read_only_space", 0x01305): "MinusZeroValue",
- ("read_only_space", 0x01311): "MinusInfinityValue",
- ("read_only_space", 0x0131d): "SelfReferenceMarker",
- ("read_only_space", 0x0135d): "OffHeapTrampolineRelocationInfo",
- ("read_only_space", 0x01369): "TrampolineTrivialCodeDataContainer",
- ("read_only_space", 0x01375): "TrampolinePromiseRejectionCodeDataContainer",
- ("read_only_space", 0x01381): "GlobalThisBindingScopeInfo",
- ("read_only_space", 0x013b9): "EmptyFunctionScopeInfo",
- ("read_only_space", 0x013e1): "NativeScopeInfo",
- ("read_only_space", 0x013fd): "HashSeed",
+ ("read_only_space", 0x011f9): "EmptySlowElementDictionary",
+ ("read_only_space", 0x0121d): "EmptyOrderedHashMap",
+ ("read_only_space", 0x01231): "EmptyOrderedHashSet",
+ ("read_only_space", 0x01245): "EmptyFeedbackMetadata",
+ ("read_only_space", 0x01251): "EmptyPropertyCell",
+ ("read_only_space", 0x01265): "EmptyPropertyDictionary",
+ ("read_only_space", 0x0128d): "NoOpInterceptorInfo",
+ ("read_only_space", 0x012dd): "EmptyWeakArrayList",
+ ("read_only_space", 0x012e9): "InfinityValue",
+ ("read_only_space", 0x012f5): "MinusZeroValue",
+ ("read_only_space", 0x01301): "MinusInfinityValue",
+ ("read_only_space", 0x0130d): "SelfReferenceMarker",
+ ("read_only_space", 0x0134d): "BasicBlockCountersMarker",
+ ("read_only_space", 0x01391): "OffHeapTrampolineRelocationInfo",
+ ("read_only_space", 0x0139d): "TrampolineTrivialCodeDataContainer",
+ ("read_only_space", 0x013a9): "TrampolinePromiseRejectionCodeDataContainer",
+ ("read_only_space", 0x013b5): "GlobalThisBindingScopeInfo",
+ ("read_only_space", 0x013ed): "EmptyFunctionScopeInfo",
+ ("read_only_space", 0x01415): "NativeScopeInfo",
+ ("read_only_space", 0x01431): "HashSeed",
("old_space", 0x00121): "ArgumentsIteratorAccessor",
("old_space", 0x00165): "ArrayLengthAccessor",
("old_space", 0x001a9): "BoundFunctionLengthAccessor",
diff --git a/chromium/v8/tools/v8windbg/base/utilities.cc b/chromium/v8/tools/v8windbg/base/utilities.cc
index a59e95f46fd..1f0e2bc6708 100644
--- a/chromium/v8/tools/v8windbg/base/utilities.cc
+++ b/chromium/v8/tools/v8windbg/base/utilities.cc
@@ -133,6 +133,15 @@ HRESULT UnboxULong64(IModelObject* object, ULONG64* value, bool convert) {
return S_OK;
}
+HRESULT GetInt32(IDebugHostConstant* object, int* value) {
+ variant_t variant;
+ RETURN_IF_FAIL(object->GetValue(&variant));
+
+ if (variant.vt != VT_I4) return E_FAIL;
+ *value = variant.ullVal;
+ return S_OK;
+}
+
HRESULT CreateInt32(int value, IModelObject** pp_int) {
HRESULT hr = S_OK;
*pp_int = nullptr;
diff --git a/chromium/v8/tools/v8windbg/base/utilities.h b/chromium/v8/tools/v8windbg/base/utilities.h
index e26bb287804..06af6c35875 100644
--- a/chromium/v8/tools/v8windbg/base/utilities.h
+++ b/chromium/v8/tools/v8windbg/base/utilities.h
@@ -55,6 +55,8 @@ HRESULT CreateULong64(ULONG64 value, IModelObject** pp_int);
HRESULT UnboxULong64(IModelObject* object, ULONG64* value,
bool convert = false);
+HRESULT GetInt32(IDebugHostConstant* object, int* value);
+
HRESULT CreateInt32(int value, IModelObject** pp_int);
HRESULT CreateUInt32(uint32_t value, IModelObject** pp_int);
diff --git a/chromium/v8/tools/v8windbg/src/object-inspection.cc b/chromium/v8/tools/v8windbg/src/object-inspection.cc
index ce0370a697f..6f90614bd5c 100644
--- a/chromium/v8/tools/v8windbg/src/object-inspection.cc
+++ b/chromium/v8/tools/v8windbg/src/object-inspection.cc
@@ -585,6 +585,79 @@ IFACEMETHODIMP V8LocalValueProperty::SetValue(
return E_NOTIMPL;
}
+IFACEMETHODIMP V8InternalCompilerNodeIdProperty::GetValue(
+ PCWSTR pwsz_key, IModelObject* p_v8_compiler_node_instance,
+ IModelObject** pp_value) noexcept {
+ WRL::ComPtr<IModelObject> sp_bit_field;
+ RETURN_IF_FAIL(p_v8_compiler_node_instance->GetRawValue(
+ SymbolKind::SymbolField, L"bit_field_", RawSearchNone, &sp_bit_field));
+
+ uint64_t bit_field_value;
+ RETURN_IF_FAIL(
+ UnboxULong64(sp_bit_field.Get(), &bit_field_value, true /*convert*/));
+
+ WRL::ComPtr<IDebugHostContext> sp_host_context;
+ RETURN_IF_FAIL(p_v8_compiler_node_instance->GetContext(&sp_host_context));
+
+ WRL::ComPtr<IDebugHostType> sp_id_field_type;
+ RETURN_IF_FAIL(Extension::Current()
+ ->GetV8Module(sp_host_context)
+ ->FindTypeByName(L"v8::internal::compiler::Node::IdField",
+ &sp_id_field_type));
+
+ // Get 2nd template parameter as 24 in class.
+ // v8::base::BitField<v8::internal::compiler::NodeId, 0, 24>.
+ bool is_generic;
+ RETURN_IF_FAIL(sp_id_field_type->IsGeneric(&is_generic));
+ if (!is_generic) return E_FAIL;
+
+ WRL::ComPtr<IDebugHostSymbol> sp_k_size_arg;
+ RETURN_IF_FAIL(sp_id_field_type->GetGenericArgumentAt(2, &sp_k_size_arg));
+
+ WRL::ComPtr<IDebugHostConstant> sp_k_size_constant;
+ RETURN_IF_FAIL(sp_k_size_arg.As(&sp_k_size_constant));
+
+ int k_size;
+ RETURN_IF_FAIL(GetInt32(sp_k_size_constant.Get(), &k_size));
+
+ // Compute node_id.
+ uint32_t node_id = bit_field_value & (0xFFFFFFFF >> k_size);
+ RETURN_IF_FAIL(CreateUInt32(node_id, pp_value));
+
+ return S_OK;
+}
+
+IFACEMETHODIMP V8InternalCompilerNodeIdProperty::SetValue(
+ PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
+ IModelObject* /*p_value*/) noexcept {
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP V8InternalCompilerBitsetNameProperty::GetValue(
+ PCWSTR pwsz_key, IModelObject* p_v8_compiler_type_instance,
+ IModelObject** pp_value) noexcept {
+ WRL::ComPtr<IModelObject> sp_payload;
+ RETURN_IF_FAIL(p_v8_compiler_type_instance->GetRawValue(
+ SymbolKind::SymbolField, L"payload_", RawSearchNone, &sp_payload));
+
+ uint64_t payload_value;
+ RETURN_IF_FAIL(
+ UnboxULong64(sp_payload.Get(), &payload_value, true /*convert*/));
+
+ const char* bitset_name = ::BitsetName(payload_value);
+ if (!bitset_name) return E_FAIL;
+ std::string name(bitset_name);
+ RETURN_IF_FAIL(CreateString(ConvertToU16String(name), pp_value));
+
+ return S_OK;
+}
+
+IFACEMETHODIMP V8InternalCompilerBitsetNameProperty::SetValue(
+ PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
+ IModelObject* /*p_value*/) noexcept {
+ return E_NOTIMPL;
+}
+
constexpr wchar_t usage[] =
LR"(Invalid arguments.
First argument should be a uint64 representing the tagged value to investigate.
diff --git a/chromium/v8/tools/v8windbg/src/object-inspection.h b/chromium/v8/tools/v8windbg/src/object-inspection.h
index 27283ca5569..a280b05cade 100644
--- a/chromium/v8/tools/v8windbg/src/object-inspection.h
+++ b/chromium/v8/tools/v8windbg/src/object-inspection.h
@@ -245,6 +245,38 @@ class V8LocalValueProperty
IModelObject* /*p_value*/);
};
+// The implemention of the "NodeId" getter for v8::internal::compiler::Node
+// type.
+class V8InternalCompilerNodeIdProperty
+ : public WRL::RuntimeClass<
+ WRL::RuntimeClassFlags<WRL::RuntimeClassType::ClassicCom>,
+ IModelPropertyAccessor> {
+ public:
+ IFACEMETHOD(GetValue)
+ (PCWSTR pwsz_key, IModelObject* p_v8_object_instance,
+ IModelObject** pp_value);
+
+ IFACEMETHOD(SetValue)
+ (PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
+ IModelObject* /*p_value*/);
+};
+
+// The implemention of the "bitset_name" getter for v8::internal::compiler::Type
+// type.
+class V8InternalCompilerBitsetNameProperty
+ : public WRL::RuntimeClass<
+ WRL::RuntimeClassFlags<WRL::RuntimeClassType::ClassicCom>,
+ IModelPropertyAccessor> {
+ public:
+ IFACEMETHOD(GetValue)
+ (PCWSTR pwsz_key, IModelObject* p_v8_compiler_type_instance,
+ IModelObject** pp_value);
+
+ IFACEMETHOD(SetValue)
+ (PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
+ IModelObject* /*p_value*/);
+};
+
// A way that someone can directly inspect a tagged value, even if that value
// isn't in memory (from a register, or the user's imagination, etc.).
class InspectV8ObjectMethod
diff --git a/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.cc b/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.cc
index 4a8dcc9add7..0767ff5f09e 100644
--- a/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.cc
+++ b/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.cc
@@ -155,3 +155,5 @@ std::vector<std::u16string> ListObjectClasses() {
}
return result;
}
+
+const char* BitsetName(uint64_t payload) { return d::BitsetName(payload); }
diff --git a/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.h b/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.h
index 96bd59b30ea..9208f098327 100644
--- a/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.h
+++ b/chromium/v8/tools/v8windbg/src/v8-debug-helper-interop.h
@@ -135,4 +135,6 @@ inline uint64_t ExpandCompressedPointer(uint32_t ptr) { return ptr; }
std::vector<std::u16string> ListObjectClasses();
+const char* BitsetName(uint64_t payload);
+
#endif // V8_TOOLS_V8WINDBG_SRC_V8_DEBUG_HELPER_INTEROP_H_
diff --git a/chromium/v8/tools/v8windbg/src/v8windbg-extension.cc b/chromium/v8/tools/v8windbg/src/v8windbg-extension.cc
index 68c90d2833b..58a520cff1f 100644
--- a/chromium/v8/tools/v8windbg/src/v8windbg-extension.cc
+++ b/chromium/v8/tools/v8windbg/src/v8windbg-extension.cc
@@ -215,7 +215,8 @@ HRESULT Extension::Initialize() {
&sp_object_type_signature));
RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
sp_object_type_signature.Get(), sp_object_data_model_.Get()));
- registered_object_types_.push_back(sp_object_type_signature);
+ registered_types_.push_back(
+ {sp_object_type_signature.Get(), sp_object_data_model_.Get()});
}
// Create an instance of the DataModel parent for custom iterable fields.
@@ -244,7 +245,7 @@ HRESULT Extension::Initialize() {
sp_debug_host_symbols->CreateTypeSignature(name, nullptr, &signature));
RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
signature.Get(), sp_local_data_model_.Get()));
- registered_handle_types_.push_back(signature);
+ registered_types_.push_back({signature.Get(), sp_local_data_model_.Get()});
}
// Add the 'Value' property to the parent model.
@@ -279,6 +280,46 @@ HRESULT Extension::Initialize() {
RETURN_IF_FAIL(OverrideLocalsGetter(stack_frame.Get(), L"Parameters",
/*is_parameters=*/true));
+ // Add node_id property for v8::internal::compiler::Node.
+ RETURN_IF_FAIL(
+ RegisterAndAddPropertyForClass<V8InternalCompilerNodeIdProperty>(
+ L"v8::internal::compiler::Node", L"node_id",
+ sp_compiler_node_data_model_));
+
+ // Add bitset_name property for v8::internal::compiler::Type.
+ RETURN_IF_FAIL(
+ RegisterAndAddPropertyForClass<V8InternalCompilerBitsetNameProperty>(
+ L"v8::internal::compiler::Type", L"bitset_name",
+ sp_compiler_type_data_model_));
+
+ return S_OK;
+}
+
+template <class PropertyClass>
+HRESULT Extension::RegisterAndAddPropertyForClass(
+ const wchar_t* class_name, const wchar_t* property_name,
+ WRL::ComPtr<IModelObject> sp_data_model) {
+ // Create an instance of the DataModel parent class.
+ auto instance_data_model{WRL::Make<V8LocalDataModel>()};
+ RETURN_IF_FAIL(sp_data_model_manager->CreateDataModelObject(
+ instance_data_model.Get(), &sp_data_model));
+
+ // Register that parent model.
+ WRL::ComPtr<IDebugHostTypeSignature> class_signature;
+ RETURN_IF_FAIL(sp_debug_host_symbols->CreateTypeSignature(class_name, nullptr,
+ &class_signature));
+ RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
+ class_signature.Get(), sp_data_model.Get()));
+ registered_types_.push_back({class_signature.Get(), sp_data_model.Get()});
+
+ // Add the property to the parent model.
+ auto property{WRL::Make<PropertyClass>()};
+ WRL::ComPtr<IModelObject> sp_property_model;
+ RETURN_IF_FAIL(CreateProperty(sp_data_model_manager.Get(), property.Get(),
+ &sp_property_model));
+ RETURN_IF_FAIL(
+ sp_data_model->SetKey(property_name, sp_property_model.Get(), nullptr));
+
return S_OK;
}
@@ -318,18 +359,24 @@ Extension::PropertyOverride::PropertyOverride(const PropertyOverride&) =
Extension::PropertyOverride& Extension::PropertyOverride::operator=(
const PropertyOverride&) = default;
+Extension::RegistrationType::RegistrationType() = default;
+Extension::RegistrationType::RegistrationType(
+ IDebugHostTypeSignature* sp_signature, IModelObject* sp_data_model)
+ : sp_signature(sp_signature), sp_data_model(sp_data_model) {}
+Extension::RegistrationType::~RegistrationType() = default;
+Extension::RegistrationType::RegistrationType(const RegistrationType&) =
+ default;
+Extension::RegistrationType& Extension::RegistrationType::operator=(
+ const RegistrationType&) = default;
+
Extension::~Extension() {
sp_debug_host_extensibility->DestroyFunctionAlias(pcur_isolate);
sp_debug_host_extensibility->DestroyFunctionAlias(plist_chunks);
sp_debug_host_extensibility->DestroyFunctionAlias(pv8_object);
- for (const auto& registered : registered_object_types_) {
- sp_data_model_manager->UnregisterModelForTypeSignature(
- sp_object_data_model_.Get(), registered.Get());
- }
- for (const auto& registered : registered_handle_types_) {
+ for (const auto& registered : registered_types_) {
sp_data_model_manager->UnregisterModelForTypeSignature(
- sp_local_data_model_.Get(), registered.Get());
+ registered.sp_data_model.Get(), registered.sp_signature.Get());
}
for (const auto& override : overridden_properties_) {
diff --git a/chromium/v8/tools/v8windbg/src/v8windbg-extension.h b/chromium/v8/tools/v8windbg/src/v8windbg-extension.h
index d54f43c847e..46331611523 100644
--- a/chromium/v8/tools/v8windbg/src/v8windbg-extension.h
+++ b/chromium/v8/tools/v8windbg/src/v8windbg-extension.h
@@ -46,6 +46,11 @@ class Extension {
HRESULT OverrideLocalsGetter(IModelObject* parent, const wchar_t* key_name,
bool is_parameters);
+ template <class PropertyClass>
+ HRESULT RegisterAndAddPropertyForClass(
+ const wchar_t* class_name, const wchar_t* property_name,
+ WRL::ComPtr<IModelObject> sp_data_model);
+
// A property that has been overridden by this extension. The original value
// must be put back in place during ~Extension.
struct PropertyOverride {
@@ -62,20 +67,32 @@ class Extension {
WRL::ComPtr<IKeyStore> original_metadata;
};
+ struct RegistrationType {
+ RegistrationType();
+ RegistrationType(IDebugHostTypeSignature* sp_signature,
+ IModelObject* sp_data_model);
+ ~RegistrationType();
+ RegistrationType(const RegistrationType&);
+ RegistrationType& operator=(const RegistrationType&);
+
+ WRL::ComPtr<IDebugHostTypeSignature> sp_signature;
+ WRL::ComPtr<IModelObject> sp_data_model;
+ };
+
static std::unique_ptr<Extension> current_extension_;
WRL::ComPtr<IModelObject> sp_object_data_model_;
WRL::ComPtr<IModelObject> sp_local_data_model_;
+ WRL::ComPtr<IModelObject> sp_compiler_node_data_model_;
+ WRL::ComPtr<IModelObject> sp_compiler_type_data_model_;
WRL::ComPtr<IModelObject> sp_indexed_field_model_;
WRL::ComPtr<IDebugHostModule> sp_v8_module_;
std::unordered_map<std::u16string, WRL::ComPtr<IDebugHostType>>
cached_v8_module_types_;
- std::vector<WRL::ComPtr<IDebugHostTypeSignature>> registered_object_types_;
- std::vector<WRL::ComPtr<IDebugHostTypeSignature>> registered_handle_types_;
+ std::vector<RegistrationType> registered_types_;
std::vector<PropertyOverride> overridden_properties_;
WRL::ComPtr<IDebugHostContext> sp_v8_module_ctx_;
ULONG v8_module_proc_id_;
};
-
#endif // V8_TOOLS_V8WINDBG_SRC_V8WINDBG_EXTENSION_H_
diff --git a/chromium/v8/tools/wasm/update-wasm-spec-tests.sh b/chromium/v8/tools/wasm/update-wasm-spec-tests.sh
index b3e9185c4df..8a63f94368e 100755
--- a/chromium/v8/tools/wasm/update-wasm-spec-tests.sh
+++ b/chromium/v8/tools/wasm/update-wasm-spec-tests.sh
@@ -95,7 +95,10 @@ for repo in ${repos}; do
log_and_run ./run.py --wasm ../../interpreter/wasm ${rel_filename} --out _build 2> /dev/null
fi
done
- log_and_run cp _build/*.js ${SPEC_TEST_DIR}/tests/proposals/${repo}/
+
+ if ls _build/*.js > /dev/null; then
+ log_and_run cp _build/*.js ${SPEC_TEST_DIR}/tests/proposals/${repo}/
+ fi
echo ">> Process js-api tests"
log_and_run mkdir ${JS_API_TEST_DIR}/tests/proposals/${repo}
diff --git a/chromium/v8/tools/whitespace.txt b/chromium/v8/tools/whitespace.txt
index 4668daa518f..94f29d13401 100644
--- a/chromium/v8/tools/whitespace.txt
+++ b/chromium/v8/tools/whitespace.txt
@@ -8,7 +8,7 @@ The doubles heard this and started to unbox.
The Smi looked at them when a crazy v8-autoroll account showed up...
The autoroller bought a round of Himbeerbrause. Suddenly.....
The bartender starts to shake the bottles.....................
-I can't add trailing whitespaces, so I'm adding this line.....
+I can't add trailing whitespaces, so I'm adding this line......
I'm starting to think that just adding trailing whitespaces might not be bad.
Because whitespaces are not that funny.....
diff --git a/chromium/v8/tools/zone-stats/categories.js b/chromium/v8/tools/zone-stats/categories.js
new file mode 100644
index 00000000000..96c4e32eaf7
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/categories.js
@@ -0,0 +1,129 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const UNCLASSIFIED_CATEGORY = 'unclassified';
+const UNCLASSIFIED_CATEGORY_NAME = 'Unclassified';
+
+// Categories for zones.
+export const CATEGORIES = new Map([
+ [
+ 'parser', new Set([
+ 'AstStringConstants',
+ 'ParseInfo',
+ 'Parser',
+ ])
+ ],
+ [
+ 'misc', new Set([
+ 'Run',
+ 'CanonicalHandleScope',
+ 'Temporary scoped zone',
+ 'UpdateFieldType',
+ ])
+ ],
+ [
+ 'interpreter', new Set([
+ 'InterpreterCompilationJob',
+ ])
+ ],
+ [
+ 'regexp', new Set([
+ 'CompileIrregexp',
+ ])
+ ],
+ [
+ 'compiler-huge', new Set([
+ 'graph-zone',
+ 'instruction-zone',
+ 'pipeline-compilation-job-zone',
+ 'register-allocation-zone',
+ 'register-allocator-verifier-zone',
+ ])
+ ],
+ [
+ 'compiler-other', new Set([
+ 'Compile',
+ 'V8.TFAllocateFPRegisters',
+ 'V8.TFAllocateGeneralRegisters',
+ 'V8.TFAssembleCode',
+ 'V8.TFAssignSpillSlots',
+ 'V8.TFBuildLiveRangeBundles',
+ 'V8.TFBuildLiveRanges',
+ 'V8.TFBytecodeGraphBuilder',
+ 'V8.TFCommitAssignment',
+ 'V8.TFConnectRanges',
+ 'V8.TFControlFlowOptimization',
+ 'V8.TFDecideSpillingMode',
+ 'V8.TFDecompressionOptimization',
+ 'V8.TFEarlyOptimization',
+ 'V8.TFEarlyTrimming',
+ 'V8.TFEffectLinearization',
+ 'V8.TFEscapeAnalysis',
+ 'V8.TFFinalizeCode',
+ 'V8.TFFrameElision',
+ 'V8.TFGenericLowering',
+ 'V8.TFHeapBrokerInitialization',
+ 'V8.TFInlining',
+ 'V8.TFJumpThreading',
+ 'V8.TFLateGraphTrimming',
+ 'V8.TFLateOptimization',
+ 'V8.TFLoadElimination',
+ 'V8.TFLocateSpillSlots',
+ 'V8.TFLoopPeeling',
+ 'V8.TFMachineOperatorOptimization',
+ 'V8.TFMeetRegisterConstraints',
+ 'V8.TFMemoryOptimization',
+ 'V8.TFOptimizeMoves',
+ 'V8.TFPopulatePointerMaps',
+ 'V8.TFResolveControlFlow',
+ 'V8.TFResolvePhis',
+ 'V8.TFScheduling',
+ 'V8.TFSelectInstructions',
+ 'V8.TFSerializeMetadata',
+ 'V8.TFSimplifiedLowering',
+ 'V8.TFStoreStoreElimination',
+ 'V8.TFTypedLowering',
+ 'V8.TFTyper',
+ 'V8.TFUntyper',
+ 'V8.TFVerifyGraph',
+ 'ValidatePendingAssessment',
+ 'codegen-zone',
+ ])
+ ],
+ [UNCLASSIFIED_CATEGORY, new Set()],
+]);
+
+// Maps category to description text that is shown in html.
+export const CATEGORY_NAMES = new Map([
+ ['parser', 'Parser'],
+ ['misc', 'Misc'],
+ ['interpreter', 'Ignition'],
+ ['regexp', 'Regexp compiler'],
+ ['compiler-huge', 'TurboFan (huge zones)'],
+ ['compiler-other', 'TurboFan (other zones)'],
+ [UNCLASSIFIED_CATEGORY, UNCLASSIFIED_CATEGORY_NAME],
+]);
+
+function buildZoneToCategoryMap() {
+ const map = new Map();
+ for (let [category, zone_names] of CATEGORIES.entries()) {
+ for (let zone_name of zone_names) {
+ if (map.has(zone_name)) {
+ console.error("Zone belongs to multiple categories: " + zone_name);
+ } else {
+ map.set(zone_name, category);
+ }
+ }
+ }
+ return map;
+}
+
+const CATEGORY_BY_ZONE = buildZoneToCategoryMap();
+
+// Maps zone name to category.
+export function categoryByZoneName(zone_name) {
+ const category = CATEGORY_BY_ZONE.get(zone_name);
+ if (category !== undefined) return category;
+ return UNCLASSIFIED_CATEGORY;
+}
diff --git a/chromium/v8/tools/zone-stats/details-selection-template.html b/chromium/v8/tools/zone-stats/details-selection-template.html
new file mode 100644
index 00000000000..ef1e2f68b9b
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/details-selection-template.html
@@ -0,0 +1,146 @@
+<!-- Copyright 2020 the V8 project authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<style>
+#dataSelectionSection {
+ display: none;
+}
+
+.box {
+ border-left: dashed 1px #666666;
+ border-right: dashed 1px #666666;
+ border-bottom: dashed 1px #666666;
+ padding: 10px;
+ overflow: hidden;
+ position: relative;
+}
+
+.box:nth-of-type(1) {
+ border-top: dashed 1px #666666;
+ border-radius: 5px 5px 0px 0px;
+}
+
+.box:last-of-type {
+ border-radius: 0px 0px 5px 5px;
+}
+
+.box > ul {
+ margin: 0px;
+ padding: 0px;
+}
+
+.box > ul > li {
+ display: inline-block;
+}
+
+.box > ul > li:not(:first-child) {
+ margin-left: 10px;
+}
+
+.box > ul > li:first-child {
+ font-weight: bold;
+}
+
+.zonesSelectBox {
+ position: relative;
+ overflow: hidden;
+ float: left;
+ padding: 0px 5px 2px 0px;
+ margin: 3px;
+ border-radius: 3px;
+}
+
+.zonesSelectBox > label {
+ font-size: xx-small;
+}
+
+.zonesSelectBox > input {
+ vertical-align: middle;
+}
+
+.percentBackground {
+ position: absolute;
+ width: 200%;
+ height: 100%;
+ left: 0%;
+ top: 0px;
+ margin-left: -100%;
+ transition: all 1s ease-in-out;
+}
+
+.zonesSelectBox > .percentBackground {
+ background: linear-gradient(90deg, #68b0f7 50%, #b3d9ff 50%);
+ z-index: -1;
+}
+.box > .percentBackground {
+ background: linear-gradient(90deg, #e0edfe 50%, #fff 50%);
+ z-index: -2;
+}
+
+#categories {
+ margin-top: 10px;
+}
+
+#category-filter {
+ text-align: right;
+ width: 50px;
+}
+
+</style>
+<section id="dataSelectionSection">
+ <h2>Data selection</h2>
+ <ul>
+ <li>
+ <label for="isolate-select">
+ Isolate
+ </label>
+ <select id="isolate-select">
+ <option>No data</option>
+ </select>
+ </li>
+ <li>
+ <label for="data-view-select">
+ Data view
+ </label>
+ <select id="data-view-select">
+ <option>No data</option>
+ </select>
+ </li>
+ <li>
+ <label for="show-totals-select">
+ Show total allocated/used zone memory
+ </label>
+ <input type="checkbox" id="show-totals-select" checked>
+ </li>
+ <li>
+ <label for="data-kind-select">
+ Data kind
+ </label>
+ <select id="data-kind-select">
+ <option>No data</option>
+ </select>
+ </li>
+ <li>
+ <label for="time-start-select">
+ Time start
+ </label>
+ <input type="number" id="time-start-select" value="0">ms</input>
+ </li>
+ <li>
+ <label for="time-end-select">
+ Time end
+ </label>
+ <input type="number" id="time-end-select" value="0">ms</input>
+ </li>
+ <li>
+ <label for="memory-usage-sample-select">
+ Memory usage sample (at a specific time in ms)
+ </label>
+ <select id="memory-usage-sample-select">
+ <option>No data</option>
+ </select>
+ </li>
+ </ul>
+
+ <div id="categories"></div>
+</section>
diff --git a/chromium/v8/tools/zone-stats/details-selection.js b/chromium/v8/tools/zone-stats/details-selection.js
new file mode 100644
index 00000000000..b25a11337a3
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/details-selection.js
@@ -0,0 +1,365 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+import {CATEGORIES, CATEGORY_NAMES, categoryByZoneName} from './categories.js';
+
+export const VIEW_TOTALS = 'by-totals';
+export const VIEW_BY_ZONE_NAME = 'by-zone-name';
+export const VIEW_BY_ZONE_CATEGORY = 'by-zone-category';
+
+export const KIND_ALLOCATED_MEMORY = 'kind-detailed-allocated';
+export const KIND_USED_MEMORY = 'kind-detailed-used';
+
+defineCustomElement('details-selection', (templateText) =>
+ class DetailsSelection extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.innerHTML = templateText;
+ this.isolateSelect.addEventListener(
+ 'change', e => this.handleIsolateChange(e));
+ this.dataViewSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ this.dataKindSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ this.showTotalsSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ this.memoryUsageSampleSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ this.timeStartSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ this.timeEndSelect.addEventListener(
+ 'change', e => this.notifySelectionChanged(e));
+ }
+
+ connectedCallback() {
+ for (let category of CATEGORIES.keys()) {
+ this.$('#categories').appendChild(this.buildCategory(category));
+ }
+ }
+
+ set data(value) {
+ this._data = value;
+ this.dataChanged();
+ }
+
+ get data() {
+ return this._data;
+ }
+
+ get selectedIsolate() {
+ return this._data[this.selection.isolate];
+ }
+
+ get selectedData() {
+ console.assert(this.data, 'invalid data');
+ console.assert(this.selection, 'invalid selection');
+ const time = this.selection.time;
+ return this.selectedIsolate.samples.get(time);
+ }
+
+ $(id) {
+ return this.shadowRoot.querySelector(id);
+ }
+
+ querySelectorAll(query) {
+ return this.shadowRoot.querySelectorAll(query);
+ }
+
+ get dataViewSelect() {
+ return this.$('#data-view-select');
+ }
+
+ get dataKindSelect() {
+ return this.$('#data-kind-select');
+ }
+
+ get isolateSelect() {
+ return this.$('#isolate-select');
+ }
+
+ get memoryUsageSampleSelect() {
+ return this.$('#memory-usage-sample-select');
+ }
+
+ get showTotalsSelect() {
+ return this.$('#show-totals-select');
+ }
+
+ get timeStartSelect() {
+ return this.$('#time-start-select');
+ }
+
+ get timeEndSelect() {
+ return this.$('#time-end-select');
+ }
+
+ buildCategory(name) {
+ const div = document.createElement('div');
+ div.id = name;
+ div.classList.add('box');
+ const ul = document.createElement('ul');
+ div.appendChild(ul);
+ const name_li = document.createElement('li');
+ ul.appendChild(name_li);
+ name_li.innerHTML = CATEGORY_NAMES.get(name);
+ const percent_li = document.createElement('li');
+ ul.appendChild(percent_li);
+ percent_li.innerHTML = '0%';
+ percent_li.id = name + 'PercentContent';
+ const all_li = document.createElement('li');
+ ul.appendChild(all_li);
+ const all_button = document.createElement('button');
+ all_li.appendChild(all_button);
+ all_button.innerHTML = 'All';
+ all_button.addEventListener('click', e => this.selectCategory(name));
+ const none_li = document.createElement('li');
+ ul.appendChild(none_li);
+ const none_button = document.createElement('button');
+ none_li.appendChild(none_button);
+ none_button.innerHTML = 'None';
+ none_button.addEventListener('click', e => this.unselectCategory(name));
+ const innerDiv = document.createElement('div');
+ div.appendChild(innerDiv);
+ innerDiv.id = name + 'Content';
+ const percentDiv = document.createElement('div');
+ div.appendChild(percentDiv);
+ percentDiv.className = 'percentBackground';
+ percentDiv.id = name + 'PercentBackground';
+ return div;
+ }
+
+ dataChanged() {
+ this.selection = {categories: {}, zones: new Map()};
+ this.resetUI(true);
+ this.populateIsolateSelect();
+ this.handleIsolateChange();
+ this.$('#dataSelectionSection').style.display = 'block';
+ }
+
+ populateIsolateSelect() {
+ let isolates = Object.entries(this.data);
+ // Sort by peak heap memory consumption.
+ isolates.sort((a, b) => b[1].peakAllocatedMemory - a[1].peakAllocatedMemory);
+ this.populateSelect(
+ '#isolate-select', isolates, (key, isolate) => isolate.getLabel());
+ }
+
+ resetUI(resetIsolateSelect) {
+ if (resetIsolateSelect) removeAllChildren(this.isolateSelect);
+
+ removeAllChildren(this.dataViewSelect);
+ removeAllChildren(this.dataKindSelect);
+ removeAllChildren(this.memoryUsageSampleSelect);
+ this.clearCategories();
+ }
+
+ handleIsolateChange(e) {
+ this.selection.isolate = this.isolateSelect.value;
+ if (this.selection.isolate.length === 0) {
+ this.selection.isolate = null;
+ return;
+ }
+ this.resetUI(false);
+ this.populateSelect(
+ '#data-view-select', [
+ [VIEW_TOTALS, 'Total memory usage'],
+ [VIEW_BY_ZONE_NAME, 'Selected zones types'],
+ [VIEW_BY_ZONE_CATEGORY, 'Selected zone categories'],
+ ],
+ (key, label) => label, VIEW_TOTALS);
+ this.populateSelect(
+ '#data-kind-select', [
+ [KIND_ALLOCATED_MEMORY, 'Allocated memory per zone'],
+ [KIND_USED_MEMORY, 'Used memory per zone'],
+ ],
+ (key, label) => label, KIND_ALLOCATED_MEMORY);
+
+ this.populateSelect(
+ '#memory-usage-sample-select',
+ [...this.selectedIsolate.samples.entries()].filter(([time, sample]) => {
+ // Remove samples that does not have detailed per-zone data.
+ return sample.zones !== undefined;
+ }),
+ (time, sample, index) => {
+ return ((index + ': ').padStart(6, '\u00A0') +
+ formatSeconds(time).padStart(8, '\u00A0') + ' ' +
+ formatBytes(sample.allocated).padStart(12, '\u00A0'));
+ },
+ this.selectedIsolate.peakUsageTime);
+
+ this.timeStartSelect.value = this.selectedIsolate.start;
+ this.timeEndSelect.value = this.selectedIsolate.end;
+
+ this.populateCategories();
+ this.notifySelectionChanged();
+ }
+
+ notifySelectionChanged(e) {
+ if (!this.selection.isolate) return;
+
+ this.selection.data_view = this.dataViewSelect.value;
+ this.selection.data_kind = this.dataKindSelect.value;
+ this.selection.categories = Object.create(null);
+ this.selection.zones = new Map();
+ this.$('#categories').style.display = 'none';
+ for (let category of CATEGORIES.keys()) {
+ const selected = this.selectedInCategory(category);
+ if (selected.length > 0) this.selection.categories[category] = selected;
+ for (const zone_name of selected) {
+ this.selection.zones.set(zone_name, category);
+ }
+ }
+ this.$('#categories').style.display = 'block';
+ this.selection.category_names = CATEGORY_NAMES;
+ this.selection.show_totals = this.showTotalsSelect.checked;
+ this.selection.time = Number(this.memoryUsageSampleSelect.value);
+ this.selection.timeStart = Number(this.timeStartSelect.value);
+ this.selection.timeEnd = Number(this.timeEndSelect.value);
+ this.updatePercentagesInCategory();
+ this.updatePercentagesInZones();
+ this.dispatchEvent(new CustomEvent(
+ 'change', {bubbles: true, composed: true, detail: this.selection}));
+ }
+
+ updatePercentagesInCategory() {
+ const overalls = Object.create(null);
+ let overall = 0;
+ // Reset all categories.
+ this.selection.category_names.forEach((_, category) => {
+ overalls[category] = 0;
+ });
+ // Only update categories that have selections.
+ Object.entries(this.selection.categories).forEach(([category, value]) => {
+ overalls[category] =
+ Object.values(value).reduce(
+ (accu, current) => {
+ const zone_data = this.selectedData.zones.get(current);
+ return zone_data === undefined ? accu
+ : accu + zone_data.allocated;
+ }, 0) /
+ KB;
+ overall += overalls[category];
+ });
+ Object.entries(overalls).forEach(([category, category_overall]) => {
+ let percents = category_overall / overall * 100;
+ this.$(`#${category}PercentContent`).innerHTML =
+ `${percents.toFixed(1)}%`;
+ this.$('#' + category + 'PercentBackground').style.left = percents + '%';
+ });
+ }
+
+ updatePercentagesInZones() {
+ const selected_data = this.selectedData;
+ const zones_data = selected_data.zones;
+ const total_allocated = selected_data.allocated;
+ this.querySelectorAll('.zonesSelectBox input').forEach(checkbox => {
+ const zone_name = checkbox.value;
+ const zone_data = zones_data.get(zone_name);
+ const zone_allocated = zone_data === undefined ? 0 : zone_data.allocated;
+ if (zone_allocated == 0) {
+ checkbox.parentNode.style.display = 'none';
+ } else {
+ const percents = zone_allocated / total_allocated;
+ const percent_div = checkbox.parentNode.querySelector('.percentBackground');
+ percent_div.style.left = (percents * 100) + '%';
+ checkbox.parentNode.style.display = 'block';
+ }
+ });
+ }
+
+ selectedInCategory(category) {
+ let tmp = [];
+ this.querySelectorAll('input[name=' + category + 'Checkbox]:checked')
+ .forEach(checkbox => tmp.push(checkbox.value));
+ return tmp;
+ }
+
+ createOption(value, text) {
+ const option = document.createElement('option');
+ option.value = value;
+ option.text = text;
+ return option;
+ }
+
+ populateSelect(id, iterable, labelFn = null, autoselect = null) {
+ if (labelFn == null) labelFn = e => e;
+ let index = 0;
+ for (let [key, value] of iterable) {
+ index++;
+ const label = labelFn(key, value, index);
+ const option = this.createOption(key, label);
+ if (autoselect === key) {
+ option.selected = 'selected';
+ }
+ this.$(id).appendChild(option);
+ }
+ }
+
+ clearCategories() {
+ for (const category of CATEGORIES.keys()) {
+ let f = this.$('#' + category + 'Content');
+ while (f.firstChild) {
+ f.removeChild(f.firstChild);
+ }
+ }
+ }
+
+ populateCategories() {
+ this.clearCategories();
+ const categories = Object.create(null);
+ for (let cat of CATEGORIES.keys()) {
+ categories[cat] = [];
+ }
+
+ for (const [zone_name, zone_stats] of this.selectedIsolate.zones) {
+ const category = categoryByZoneName(zone_name);
+ categories[category].push(zone_name);
+ }
+ for (let category of Object.keys(categories)) {
+ categories[category].sort();
+ for (let zone_name of categories[category]) {
+ this.$('#' + category + 'Content')
+ .appendChild(this.createCheckBox(zone_name, category));
+ }
+ }
+ }
+
+ unselectCategory(category) {
+ this.querySelectorAll('input[name=' + category + 'Checkbox]')
+ .forEach(checkbox => checkbox.checked = false);
+ this.notifySelectionChanged();
+ }
+
+ selectCategory(category) {
+ this.querySelectorAll('input[name=' + category + 'Checkbox]')
+ .forEach(checkbox => checkbox.checked = true);
+ this.notifySelectionChanged();
+ }
+
+ createCheckBox(instance_type, category) {
+ const div = document.createElement('div');
+ div.classList.add('zonesSelectBox');
+ div.style.width = "200px";
+ const input = document.createElement('input');
+ div.appendChild(input);
+ input.type = 'checkbox';
+ input.name = category + 'Checkbox';
+ input.checked = 'checked';
+ input.id = instance_type + 'Checkbox';
+ input.instance_type = instance_type;
+ input.value = instance_type;
+ input.addEventListener('change', e => this.notifySelectionChanged(e));
+ const label = document.createElement('label');
+ div.appendChild(label);
+ label.innerText = instance_type;
+ label.htmlFor = instance_type + 'Checkbox';
+ const percentDiv = document.createElement('div');
+ percentDiv.className = 'percentBackground';
+ div.appendChild(percentDiv);
+ return div;
+ }
+});
diff --git a/chromium/v8/tools/zone-stats/global-timeline-template.html b/chromium/v8/tools/zone-stats/global-timeline-template.html
new file mode 100644
index 00000000000..49e75646f16
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/global-timeline-template.html
@@ -0,0 +1,16 @@
+<!-- Copyright 2020 the V8 project authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<style>
+#chart {
+ width: 100%;
+ height: 500px;
+}
+</style>
+<div id="container" style="display: none;">
+ <h2>Stats</h2>
+ <p>Peak allocated zone memory <span id="peak-memory-label"></span></p>
+
+ <h2>Timeline</h2>
+ <div id="chart"></div>
+</div>
diff --git a/chromium/v8/tools/zone-stats/global-timeline.js b/chromium/v8/tools/zone-stats/global-timeline.js
new file mode 100644
index 00000000000..ea1793101b4
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/global-timeline.js
@@ -0,0 +1,323 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+import {categoryByZoneName} from './categories.js';
+
+import {
+ VIEW_TOTALS,
+ VIEW_BY_ZONE_NAME,
+ VIEW_BY_ZONE_CATEGORY,
+
+ KIND_ALLOCATED_MEMORY,
+ KIND_USED_MEMORY,
+} from './details-selection.js';
+
+defineCustomElement('global-timeline', (templateText) =>
+ class GlobalTimeline extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.innerHTML = templateText;
+ }
+
+ $(id) {
+ return this.shadowRoot.querySelector(id);
+ }
+
+ set data(value) {
+ this._data = value;
+ this.stateChanged();
+ }
+
+ get data() {
+ return this._data;
+ }
+
+ set selection(value) {
+ this._selection = value;
+ this.stateChanged();
+ }
+
+ get selection() {
+ return this._selection;
+ }
+
+ isValid() {
+ return this.data && this.selection;
+ }
+
+ hide() {
+ this.$('#container').style.display = 'none';
+ }
+
+ show() {
+ this.$('#container').style.display = 'block';
+ }
+
+ stateChanged() {
+ if (this.isValid()) {
+ const isolate_data = this.data[this.selection.isolate];
+ const peakAllocatedMemory = isolate_data.peakAllocatedMemory;
+ this.$('#peak-memory-label').innerText = formatBytes(peakAllocatedMemory);
+ this.drawChart();
+ } else {
+ this.hide();
+ }
+ }
+
+ getZoneLabels(zone_names) {
+ switch (this.selection.data_kind) {
+ case KIND_ALLOCATED_MEMORY:
+ return zone_names.map(name => {
+ return {label: name + " (allocated)", type: 'number'};
+ });
+
+ case KIND_USED_MEMORY:
+ return zone_names.map(name => {
+ return {label: name + " (used)", type: 'number'};
+ });
+
+ default:
+ // Don't show detailed per-zone information.
+ return [];
+ }
+ }
+
+ getTotalsData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const labels = [
+ { label: "Time", type: "number" },
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getZoneData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const zone_names = isolate_data.sorted_zone_names;
+ const selected_zones = this.selection.zones;
+ const data_kind = this.selection.data_kind;
+ const show_totals = this.selection.show_totals;
+ const zones_labels = this.getZoneLabels(zone_names);
+
+ const totals_labels = show_totals
+ ? [
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ]
+ : [];
+
+ const labels = [
+ { label: "Time", type: "number" },
+ ...totals_labels,
+ ...zones_labels,
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const active_zone_stats = Object.create(null);
+ if (zone_data.zones !== undefined) {
+ for (const [zone_name, zone_stats] of zone_data.zones) {
+ if (!selected_zones.has(zone_name)) continue; // Not selected, skip.
+
+ const current_stats = active_zone_stats[zone_name];
+ if (current_stats === undefined) {
+ active_zone_stats[zone_name] =
+ { allocated: zone_stats.allocated, used: zone_stats.used };
+ } else {
+ // We've got two zones with the same name.
+ console.log("=== Duplicate zone names: " + zone_name);
+ // Sum stats.
+ current_stats.allocated += zone_stats.allocated;
+ current_stats.used += zone_stats.used;
+ }
+ }
+ }
+
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ if (show_totals) {
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ }
+
+ if (zone_data.used > 30 * MB) {
+ console.log("BOOOM!!!! Zone usage in a sample is too big: " +
+ (zone_data.used / MB) + " MB");
+ }
+
+ zone_names.forEach(zone => {
+ const sample = active_zone_stats[zone];
+ let used = null;
+ let allocated = null;
+ if (sample !== undefined) {
+ used = sample.used / KB;
+ allocated = sample.allocated / KB;
+ }
+ if (data_kind == KIND_ALLOCATED_MEMORY) {
+ data.push(allocated);
+ } else {
+ // KIND_USED_MEMORY
+ data.push(used);
+ }
+ });
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getCategoryData() {
+ const isolate_data = this.data[this.selection.isolate];
+ const categories = Object.keys(this.selection.categories);
+ const categories_names =
+ categories.map(k => this.selection.category_names.get(k));
+ const selected_zones = this.selection.zones;
+ const data_kind = this.selection.data_kind;
+ const show_totals = this.selection.show_totals;
+
+ const categories_labels = this.getZoneLabels(categories_names);
+
+ const totals_labels = show_totals
+ ? [
+ { label: "Total allocated", type: "number" },
+ { label: "Total used", type: "number" },
+ ]
+ : [];
+
+ const labels = [
+ { label: "Time", type: "number" },
+ ...totals_labels,
+ ...categories_labels,
+ ];
+ const chart_data = [labels];
+
+ const timeStart = this.selection.timeStart;
+ const timeEnd = this.selection.timeEnd;
+ const filter_entries = timeStart > 0 || timeEnd > 0;
+
+ for (const [time, zone_data] of isolate_data.samples) {
+ if (filter_entries && (time < timeStart || time > timeEnd)) continue;
+ const active_category_stats = Object.create(null);
+ if (zone_data.zones !== undefined) {
+ for (const [zone_name, zone_stats] of zone_data.zones) {
+ const category = selected_zones.get(zone_name);
+ if (category === undefined) continue; // Zone was not selected.
+
+ const current_stats = active_category_stats[category];
+ if (current_stats === undefined) {
+ active_category_stats[category] =
+ { allocated: zone_stats.allocated, used: zone_stats.used };
+ } else {
+ // Sum stats.
+ current_stats.allocated += zone_stats.allocated;
+ current_stats.used += zone_stats.used;
+ }
+ }
+ }
+
+ const data = [];
+ data.push(time * kMillis2Seconds);
+ if (show_totals) {
+ data.push(zone_data.allocated / KB);
+ data.push(zone_data.used / KB);
+ }
+
+ categories.forEach(category => {
+ const sample = active_category_stats[category];
+ let used = null;
+ let allocated = null;
+ if (sample !== undefined) {
+ used = sample.used / KB;
+ allocated = sample.allocated / KB;
+ }
+ if (data_kind == KIND_ALLOCATED_MEMORY) {
+ data.push(allocated);
+ } else {
+ // KIND_USED_MEMORY
+ data.push(used);
+ }
+ });
+ chart_data.push(data);
+ }
+ return chart_data;
+ }
+
+ getChartData() {
+ switch (this.selection.data_view) {
+ case VIEW_BY_ZONE_NAME:
+ return this.getZoneData();
+ case VIEW_BY_ZONE_CATEGORY:
+ return this.getCategoryData();
+ case VIEW_TOTALS:
+ default:
+ return this.getTotalsData();
+ }
+ }
+
+ getChartOptions() {
+ const options = {
+ isStacked: true,
+ interpolateNulls: true,
+ hAxis: {
+ format: '###.##s',
+ title: 'Time [s]',
+ },
+ vAxis: {
+ format: '#,###KB',
+ title: 'Memory consumption [KBytes]'
+ },
+ chartArea: {left:100, width: '85%', height: '70%'},
+ legend: {position: 'top', maxLines: '1'},
+ pointsVisible: true,
+ pointSize: 3,
+ explorer: {},
+ };
+
+ // Overlay total allocated/used points on top of the graph.
+ const series = {}
+ if (this.selection.data_view == VIEW_TOTALS) {
+ series[0] = {type: 'line', color: "red"};
+ series[1] = {type: 'line', color: "blue"};
+ } else if (this.selection.show_totals) {
+ series[0] = {type: 'line', color: "red", lineDashStyle: [13, 13]};
+ series[1] = {type: 'line', color: "blue", lineDashStyle: [13, 13]};
+ }
+ return Object.assign(options, {series: series});
+ }
+
+ drawChart() {
+ console.assert(this.data, 'invalid data');
+ console.assert(this.selection, 'invalid selection');
+
+ const chart_data = this.getChartData();
+
+ const data = google.visualization.arrayToDataTable(chart_data);
+ const options = this.getChartOptions();
+ const chart = new google.visualization.AreaChart(this.$('#chart'));
+ this.show();
+ chart.draw(data, google.charts.Line.convertOptions(options));
+ }
+});
diff --git a/chromium/v8/tools/zone-stats/helper.js b/chromium/v8/tools/zone-stats/helper.js
new file mode 100644
index 00000000000..a0d04859d1c
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/helper.js
@@ -0,0 +1,30 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const KB = 1024;
+const MB = KB * KB;
+const GB = MB * KB;
+const kMillis2Seconds = 1 / 1000;
+
+function formatBytes(bytes) {
+ const units = [' B', ' KB', ' MB', ' GB'];
+ const divisor = 1024;
+ let index = 0;
+ while (index < units.length && bytes >= divisor) {
+ index++;
+ bytes /= divisor;
+ }
+ return bytes.toFixed(2) + units[index];
+}
+
+function formatSeconds(millis) {
+ return (millis * kMillis2Seconds).toFixed(2) + 's';
+}
+
+function defineCustomElement(name, generator) {
+ let htmlTemplatePath = name + '-template.html';
+ fetch(htmlTemplatePath)
+ .then(stream => stream.text())
+ .then(templateText => customElements.define(name, generator(templateText)));
+}
diff --git a/chromium/v8/tools/zone-stats/index.html b/chromium/v8/tools/zone-stats/index.html
new file mode 100644
index 00000000000..5997aab8831
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/index.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<!-- Copyright 2020 the V8 project authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <title>V8 Heap Statistics</title>
+ <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
+ <script
+ src="https://www.gstatic.com/charts/loader.js"></script>
+ <script
+ src="https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.6/pako_inflate.js"
+ integrity1="sha256-N1z6ddQzX83fjw8v7uSNe7/MgOmMKdwFUv1+AJMDqNM="
+ crossorigin="anonymous"></script>
+
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/oboe.js/2.1.5/oboe-browser.js"
+ crossorigin="anonymous"></script>
+ <script src="helper.js"></script>
+
+ <script type="module" src="details-selection.js"></script>
+ <script type="module" src="global-timeline.js"></script>
+ <script type="module" src="trace-file-reader.js"></script>
+
+ <style>
+body {
+ font-family: 'Roboto', sans-serif;
+ margin-left: 5%;
+ margin-right: 5%;
+}
+
+ </style>
+ <script>
+'use strict';
+
+google.charts.load('current', {'packages':['line', 'corechart', 'bar']});
+
+function $(id) { return document.querySelector(id); }
+
+function removeAllChildren(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+}
+
+let state = Object.create(null);
+
+function globalDataChanged(e) {
+ state.data = e.detail;
+ // Emit one entry with the whole model for debugging purposes.
+ console.log(state.data);
+ state.selection = null;
+ $('#global-timeline').selection = state.selection;
+ $('#global-timeline').data = state.data;
+ $('#details-selection').data = state.data;
+}
+
+function globalSelectionChangedA(e) {
+ state.selection = e.detail;
+ console.log(state.selection);
+ $('#global-timeline').selection = state.selection;
+}
+
+ </script>
+</head>
+
+<body>
+ <h1>V8 Zone memory usage Statistics</h1>
+ <trace-file-reader onchange="globalDataChanged(event)"></trace-file-reader>
+
+ <details-selection id="details-selection" onchange="globalSelectionChangedA(event)"></details-selection>
+ <global-timeline id="global-timeline"></global-timeline>
+
+ <p>Visualize zone usage profile and statistics that have been gathered using</p>
+ <ul>
+ <li><code>--trace-zone-stats</code> on V8</li>
+ <li>
+ <a
+ href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chrome's
+ tracing infrastructure</a> collecting data for the category
+ <code>v8.zone_stats</code>.
+ </li>
+ </ul>
+ <p>
+ Note that the visualizer needs to run on a web server due to HTML imports
+ requiring <a
+ href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>.
+ </p>
+</body>
+
+</html>
diff --git a/chromium/v8/tools/zone-stats/model.js b/chromium/v8/tools/zone-stats/model.js
new file mode 100644
index 00000000000..80f45237631
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/model.js
@@ -0,0 +1,92 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+export class Isolate {
+ constructor(address) {
+ this.address = address;
+ this.start = null;
+ this.end = null;
+ this.peakUsageTime = null;
+ // Maps zone name to per-zone statistics.
+ this.zones = new Map();
+ // Zone names sorted by memory usage (from low to high).
+ this.sorted_zone_names = [];
+ // Maps time to total and per-zone memory usages.
+ this.samples = new Map();
+
+ this.peakAllocatedMemory = 0;
+
+ // Maps zone name to their max memory consumption.
+ this.zonePeakMemory = Object.create(null);
+ // Peak memory consumed by a single zone.
+ this.singleZonePeakMemory = 0;
+ }
+
+ finalize() {
+ this.samples.forEach(sample => this.finalizeSample(sample));
+ this.start = Math.floor(this.start);
+ this.end = Math.ceil(this.end);
+ this.sortZoneNamesByPeakMemory();
+ }
+
+ getLabel() {
+ let label = `${this.address}: `;
+ label += ` peak=${formatBytes(this.peakAllocatedMemory)}`;
+ label += ` time=[${this.start}, ${this.end}] ms`;
+ return label;
+ }
+
+ finalizeSample(sample) {
+ const time = sample.time;
+ if (this.start == null) {
+ this.start = time;
+ this.end = time;
+ } else {
+ this.end = Math.max(this.end, time);
+ }
+
+ const allocated = sample.allocated;
+ if (allocated > this.peakAllocatedMemory) {
+ this.peakUsageTime = time;
+ this.peakAllocatedMemory = allocated;
+ }
+
+ const sample_zones = sample.zones;
+ if (sample_zones !== undefined) {
+ sample.zones.forEach((zone_sample, zone_name) => {
+ let zone_stats = this.zones.get(zone_name);
+ if (zone_stats === undefined) {
+ zone_stats = {max_allocated: 0, max_used: 0};
+ this.zones.set(zone_name, zone_stats);
+ }
+
+ zone_stats.max_allocated =
+ Math.max(zone_stats.max_allocated, zone_sample.allocated);
+ zone_stats.max_used = Math.max(zone_stats.max_used, zone_sample.used);
+ });
+ }
+ }
+
+ sortZoneNamesByPeakMemory() {
+ let entries = [...this.zones.keys()];
+ entries.sort((a, b) =>
+ this.zones.get(a).max_allocated - this.zones.get(b).max_allocated
+ );
+ this.sorted_zone_names = entries;
+
+ let max = 0;
+ for (let [key, value] of entries) {
+ this.zonePeakMemory[key] = value;
+ max = Math.max(max, value);
+ }
+ this.singleZonePeakMemory = max;
+ }
+
+ getInstanceTypePeakMemory(type) {
+ if (!(type in this.zonePeakMemory)) return 0;
+ return this.zonePeakMemory[type];
+ }
+}
diff --git a/chromium/v8/tools/zone-stats/trace-file-reader-template.html b/chromium/v8/tools/zone-stats/trace-file-reader-template.html
new file mode 100644
index 00000000000..ede7ee9a75b
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/trace-file-reader-template.html
@@ -0,0 +1,81 @@
+<!-- Copyright 2020 the V8 project authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -->
+<style>
+#fileReader {
+ width: 100%;
+ height: 100px;
+ line-height: 100px;
+ text-align: center;
+ border: solid 1px #000000;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 0.5s ease-in-out;
+}
+
+#fileReader.done {
+ height: 20px;
+ line-height: 20px;
+}
+
+#fileReader:hover {
+ background-color: #e0edfe ;
+}
+
+.loading #fileReader {
+ cursor: wait;
+}
+
+#fileReader > input {
+ display: none;
+}
+
+
+#loader {
+ display: none;
+}
+
+.loading #loader {
+ display: block;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(255, 255, 255, 0.5);
+}
+
+#spinner {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ top: 40%;
+ left: 50%;
+ margin-left: -50px;
+ border: 30px solid #000;
+ border-top: 30px solid #36E;
+ border-radius: 50%;
+ animation: spin 1s ease-in-out infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+</style>
+
+<section id="fileReaderSection">
+ <div id="fileReader" tabindex=1 >
+ <span id="label">
+ Drag and drop a trace file into this area, or click to choose from disk.
+ </span>
+ <input id="file" type="file" name="file" />
+ </div>
+ <div id="loader">
+ <div id="spinner"></div>
+ </div>
+</section>
diff --git a/chromium/v8/tools/zone-stats/trace-file-reader.js b/chromium/v8/tools/zone-stats/trace-file-reader.js
new file mode 100644
index 00000000000..6decfa8f273
--- /dev/null
+++ b/chromium/v8/tools/zone-stats/trace-file-reader.js
@@ -0,0 +1,294 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+import {Isolate} from './model.js';
+
+defineCustomElement('trace-file-reader', (templateText) =>
+ class TraceFileReader extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.innerHTML = templateText;
+ this.addEventListener('click', e => this.handleClick(e));
+ this.addEventListener('dragover', e => this.handleDragOver(e));
+ this.addEventListener('drop', e => this.handleChange(e));
+ this.$('#file').addEventListener('change', e => this.handleChange(e));
+ this.$('#fileReader').addEventListener('keydown', e => this.handleKeyEvent(e));
+ }
+
+ $(id) {
+ return this.shadowRoot.querySelector(id);
+ }
+
+ get section() {
+ return this.$('#fileReaderSection');
+ }
+
+ updateLabel(text) {
+ this.$('#label').innerText = text;
+ }
+
+ handleKeyEvent(event) {
+ if (event.key == "Enter") this.handleClick(event);
+ }
+
+ handleClick(event) {
+ this.$('#file').click();
+ }
+
+ handleChange(event) {
+ // Used for drop and file change.
+ event.preventDefault();
+ var host = event.dataTransfer ? event.dataTransfer : event.target;
+ this.readFile(host.files[0]);
+ }
+
+ handleDragOver(event) {
+ event.preventDefault();
+ }
+
+ connectedCallback() {
+ this.$('#fileReader').focus();
+ }
+
+ readFile(file) {
+ if (!file) {
+ this.updateLabel('Failed to load file.');
+ return;
+ }
+ this.$('#fileReader').blur();
+
+ this.section.className = 'loading';
+ const reader = new FileReader();
+
+ if (['application/gzip', 'application/x-gzip'].includes(file.type)) {
+ reader.onload = (e) => {
+ try {
+ // Decode data as strings of 64Kb chunks. Bigger chunks may cause
+ // parsing failures in Oboe.js.
+ const chunkedInflate = new pako.Inflate(
+ {to: 'string', chunkSize: 65536}
+ );
+ let processingState = undefined;
+ chunkedInflate.onData = (chunk) => {
+ if (processingState === undefined) {
+ processingState = this.startProcessing(file, chunk);
+ } else {
+ processingState.processChunk(chunk);
+ }
+ };
+ chunkedInflate.onEnd = () => {
+ if (processingState !== undefined) {
+ const result_data = processingState.endProcessing();
+ this.processLoadedData(file, result_data);
+ }
+ };
+ console.log("======");
+ const textResult = chunkedInflate.push(e.target.result);
+
+ this.section.className = 'success';
+ this.$('#fileReader').classList.add('done');
+ } catch (err) {
+ console.error(err);
+ this.section.className = 'failure';
+ }
+ };
+ // Delay the loading a bit to allow for CSS animations to happen.
+ setTimeout(() => reader.readAsArrayBuffer(file), 0);
+ } else {
+ reader.onload = (e) => {
+ try {
+ // Process the whole file in at once.
+ const processingState = this.startProcessing(file, e.target.result);
+ const dataModel = processingState.endProcessing();
+ this.processLoadedData(file, dataModel);
+
+ this.section.className = 'success';
+ this.$('#fileReader').classList.add('done');
+ } catch (err) {
+ console.error(err);
+ this.section.className = 'failure';
+ }
+ };
+ // Delay the loading a bit to allow for CSS animations to happen.
+ setTimeout(() => reader.readAsText(file), 0);
+ }
+ }
+
+ processLoadedData(file, dataModel) {
+ console.log("Trace file parsed successfully.");
+ this.extendAndSanitizeModel(dataModel);
+ this.updateLabel('Finished loading \'' + file.name + '\'.');
+ this.dispatchEvent(new CustomEvent(
+ 'change', {bubbles: true, composed: true, detail: dataModel}));
+ }
+
+ createOrUpdateEntryIfNeeded(data, entry) {
+ console.assert(entry.isolate, 'entry should have an isolate');
+ if (!(entry.isolate in data)) {
+ data[entry.isolate] = new Isolate(entry.isolate);
+ }
+ }
+
+ extendAndSanitizeModel(data) {
+ const checkNonNegativeProperty = (obj, property) => {
+ console.assert(obj[property] >= 0, 'negative property', obj, property);
+ };
+
+ Object.values(data).forEach(isolate => isolate.finalize());
+ }
+
+ processOneZoneStatsEntry(data, entry_stats) {
+ this.createOrUpdateEntryIfNeeded(data, entry_stats);
+ const isolate_data = data[entry_stats.isolate];
+ let zones = undefined;
+ const entry_zones = entry_stats.zones;
+ if (entry_zones !== undefined) {
+ zones = new Map();
+ entry_zones.forEach(zone => {
+ // There might be multiple occurrences of the same zone in the set,
+ // combine numbers in this case.
+ const existing_zone_stats = zones.get(zone.name);
+ if (existing_zone_stats !== undefined) {
+ existing_zone_stats.allocated += zone.allocated;
+ existing_zone_stats.used += zone.used;
+ } else {
+ zones.set(zone.name, {allocated: zone.allocated, used: zone.used});
+ }
+ });
+ }
+ const time = entry_stats.time;
+ const sample = {
+ time: time,
+ allocated: entry_stats.allocated,
+ used: entry_stats.used,
+ zones: zones
+ };
+ isolate_data.samples.set(time, sample);
+ }
+
+ startProcessing(file, chunk) {
+ const isV8TraceFile = chunk.includes('v8-zone-trace');
+ const processingState =
+ isV8TraceFile ? this.startProcessingAsV8TraceFile(file)
+ : this.startProcessingAsChromeTraceFile(file);
+
+ processingState.processChunk(chunk);
+ return processingState;
+ }
+
+ startProcessingAsChromeTraceFile(file) {
+ console.log(`Processing log as chrome trace file.`);
+ const data = Object.create(null); // Final data container.
+ const parseOneZoneEvent = (actual_data) => {
+ if ('stats' in actual_data) {
+ try {
+ const entry_stats = JSON.parse(actual_data.stats);
+ this.processOneZoneStatsEntry(data, entry_stats);
+ } catch (e) {
+ console.error('Unable to parse data set entry', e);
+ }
+ }
+ };
+ const zone_events_filter = (event) => {
+ if (event.name == 'V8.Zone_Stats') {
+ parseOneZoneEvent(event.args);
+ }
+ return oboe.drop;
+ };
+
+ const oboe_stream = oboe();
+ // Trace files support two formats.
+ oboe_stream
+ // 1) {traceEvents: [ data ]}
+ .node('traceEvents.*', zone_events_filter)
+ // 2) [ data ]
+ .node('!.*', zone_events_filter)
+ .fail((errorReport) => {
+ throw new Error("Trace data parse failed: " + errorReport.thrown);
+ });
+
+ let failed = false;
+
+ const processingState = {
+ file: file,
+
+ processChunk(chunk) {
+ if (failed) return false;
+ try {
+ oboe_stream.emit('data', chunk);
+ return true;
+ } catch (e) {
+ console.error('Unable to parse chrome trace file.', e);
+ failed = true;
+ return false;
+ }
+ },
+
+ endProcessing() {
+ if (failed) return null;
+ oboe_stream.emit('end');
+ return data;
+ },
+ };
+ return processingState;
+ }
+
+ startProcessingAsV8TraceFile(file) {
+ console.log('Processing log as V8 trace file.');
+ const data = Object.create(null); // Final data container.
+
+ const processOneLine = (line) => {
+ try {
+ // Strip away a potentially present adb logcat prefix.
+ line = line.replace(/^I\/v8\s*\(\d+\):\s+/g, '');
+
+ const entry = JSON.parse(line);
+ if (entry === null || entry.type === undefined) return;
+ if ((entry.type === 'v8-zone-trace') && ('stats' in entry)) {
+ const entry_stats = entry.stats;
+ this.processOneZoneStatsEntry(data, entry_stats);
+ } else {
+ console.log('Unknown entry type: ' + entry.type);
+ }
+ } catch (e) {
+ console.log('Unable to parse line: \'' + line + '\' (' + e + ')');
+ }
+ };
+
+ let prev_chunk_leftover = "";
+
+ const processingState = {
+ file: file,
+
+ processChunk(chunk) {
+ const contents = chunk.split('\n');
+ const last_line = contents.pop();
+ const linesCount = contents.length;
+ if (linesCount == 0) {
+ // There was only one line in the chunk, it may still be unfinished.
+ prev_chunk_leftover += last_line;
+ } else {
+ contents[0] = prev_chunk_leftover + contents[0];
+ prev_chunk_leftover = last_line;
+ for (let line of contents) {
+ processOneLine(line);
+ }
+ }
+ return true;
+ },
+
+ endProcessing() {
+ if (prev_chunk_leftover.length > 0) {
+ processOneLine(prev_chunk_leftover);
+ prev_chunk_leftover = "";
+ }
+ return data;
+ },
+ };
+ return processingState;
+ }
+});