PR-URL: https://github.com/nodejs/node/pull/61898 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
126 lines
3.5 KiB
Python
Executable file
126 lines
3.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Copyright 2019 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.
|
|
|
|
"""
|
|
python %prog trace-file
|
|
|
|
Parses output generated by v8 with flag --trace-regexp-bytecodes and generates
|
|
a list of the most common sequences.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
import re
|
|
import collections
|
|
|
|
def parse(file, seqlen):
|
|
# example:
|
|
# pc = 00, sp = 0, curpos = 0, curchar = 0000000a ..., PushBacktrack, label: e4
|
|
rx = re.compile(
|
|
r'pc = (?P<pc>[0-9a-f]+), sp = (?P<sp>\d+), '
|
|
r'curpos = (?P<curpos>\d+), curchar = (?P<char_hex>[0-9a-f]+) '
|
|
r'(:?\.|\()(?P<char>\.|\w)(:?\.|\)), (?P<bc>\w+), .*')
|
|
total = 0
|
|
|
|
# stats[len][seq_tuple] = {'count': 0, 'loops': 0}
|
|
# Using tuple as key for easier rotation
|
|
stats = [{} for _ in range(seqlen)]
|
|
|
|
# last[len] = deque of (bc, pc_int)
|
|
last = [collections.deque(maxlen=i + 1) for i in range(seqlen)]
|
|
|
|
with open(file) as f:
|
|
l = f.readline()
|
|
while l:
|
|
l = l.strip()
|
|
if l.startswith("Start bytecode interpreter"):
|
|
for i in range(seqlen):
|
|
last[i].clear()
|
|
|
|
match = rx.search(l)
|
|
if match:
|
|
total += 1
|
|
bc = match.group('bc')
|
|
pc = int(match.group('pc'), 16)
|
|
|
|
current_node = (bc, pc)
|
|
|
|
for i in range(seqlen):
|
|
deque = last[i]
|
|
|
|
if len(deque) == i + 1:
|
|
# Canonicalize based on PC: find the rotation that starts with the
|
|
# lowest PC. This aligns loops to their header.
|
|
min_pc = float('inf')
|
|
rotation_start = 0
|
|
|
|
# Find the index with the minimum PC
|
|
for idx, item in enumerate(deque):
|
|
if item[1] < min_pc:
|
|
min_pc = item[1]
|
|
rotation_start = idx
|
|
elif item[1] == min_pc:
|
|
# Tie-break. If the same sequence appears twice (A->B->A->B),
|
|
# we prefer the first.
|
|
pass
|
|
|
|
# Rotate to start at rotation_start
|
|
canonical_seq = []
|
|
for k in range(i + 1):
|
|
idx = (rotation_start + k) % (i + 1)
|
|
canonical_seq.append(deque[idx][0])
|
|
|
|
seq_key = tuple(canonical_seq)
|
|
|
|
if seq_key not in stats[i]:
|
|
stats[i][seq_key] = {'count': 0, 'loops': 0}
|
|
|
|
stats[i][seq_key]['count'] += 1
|
|
|
|
# Check for loops. Note that current_node is the node *after* the
|
|
# sequence we are currently looking at.
|
|
first_node = deque[0]
|
|
if current_node[1] == first_node[1]:
|
|
stats[i][seq_key]['loops'] += 1
|
|
|
|
deque.append(current_node)
|
|
|
|
l = f.readline()
|
|
return stats, total
|
|
|
|
def print_most_common(d, seqlen, total):
|
|
# Sort by count
|
|
sorted_items = sorted(d.items(), key=lambda kv: kv[1]['count'], reverse=True)
|
|
|
|
for (k, val) in sorted_items:
|
|
count = val['count']
|
|
loops = val['loops']
|
|
pct = count * 100.0 / total
|
|
|
|
if pct < 1.0:
|
|
return
|
|
|
|
loop_pct = loops * 100.0 / count
|
|
|
|
seq_str = ' --> '.join(k)
|
|
print("{}: {} ({:.2f} %) - Loops: {} ({:.2f} %)".format(
|
|
seq_str, count, pct, loops, loop_pct))
|
|
|
|
def main(argv):
|
|
if len(argv) < 2:
|
|
print("Usage: python {} <trace-file>".format(argv[0]))
|
|
sys.exit(1)
|
|
|
|
max_seq = 7
|
|
stats, total = parse(argv[1], max_seq)
|
|
for i in range(max_seq):
|
|
print()
|
|
print("Most common of length {}".format(i+1))
|
|
print()
|
|
print_most_common(stats[i], i, total)
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv)
|