nodejs/deps/v8/tools/explicit-compile-hints/generate-explicit-function-compile-hints.py
Michaël Zasso 918fe04351
deps: update V8 to 13.6.233.8
PR-URL: https://github.com/nodejs/node/pull/58070
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
2025-05-02 15:06:53 +02:00

214 lines
5.5 KiB
Python

#!/usr/bin/env python3
# Copyright 2023 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 sys
varint_mask = (1 << 5) - 1
varint_continue_bit = 1 << 5
varint_shift = 5
# We use the same VLQ-Base64 encoding as source maps, where each integer is
# first expressed as a variable length sequence of 6-bit items, and each item
# is then Base64-encoded individually.
# Index -> character
custom_base64_encoding = [0] * 64
# Character -> index
custom_base64_decoding = [-1] * 256
def fill_custom_base64_data():
base = 0
for i in range(ord('A'), ord('Z') + 1):
custom_base64_encoding[base + i - ord('A')] = chr(i)
base += 26
for i in range(ord('a'), ord('z') + 1):
custom_base64_encoding[base + i - ord('a')] = chr(i)
base += 26
for i in range(ord('0'), ord('9') + 1):
custom_base64_encoding[base + i - ord('0')] = chr(i)
base += 10
custom_base64_encoding[base] = '+'
custom_base64_encoding[base + 1] = '/'
for i in range(0, 64):
custom_base64_decoding[ord(custom_base64_encoding[i])] = i
fill_custom_base64_data()
def encode_varint(n):
# Encodes integer n as a varint the same way as in source maps.
# Add sign bit (the least significant bit):
if n < 0:
n = ((-n) << 1) | 1
else:
n = n << 1
# Each item represents varint_shift bits; the first item represents the
# least significant bits etc.
res = []
while n > 0:
current_item = n & varint_mask
n = n >> varint_shift
if n > 0:
current_item = current_item | varint_continue_bit
res.append(current_item)
return res
def custom_b64encode(items):
result = []
for item in items:
result.append(custom_base64_encoding[item])
return ''.join(result)
def custom_b64decode(s):
result = []
for c in s:
index = custom_base64_decoding[ord(c)]
if index < 0 or index > 64:
raise 'Corrupt data'
result.append(index)
return result
def encode_delta_varint_base64(start_positions):
bytes = []
prev_pos = 0
for p in start_positions:
bytes.extend(encode_varint(p - prev_pos))
prev_pos = p
return custom_b64encode(bytes)
def decode_varints(bytes):
ints = []
current = 0
byte_ix = 0
while byte_ix < len(bytes):
# Start parsing the new varint.
n = bytes[byte_ix]
byte_ix += 1
current = n & varint_mask
current_shift = 0
while n & varint_continue_bit != 0:
# Read another item.
if byte_ix >= len(bytes):
raise 'Corrupt data'
n = bytes[byte_ix]
byte_ix += 1
current_shift = current_shift + varint_shift
current = current | ((n & varint_mask) << current_shift)
if current & 1 != 0:
sign = -1
else:
sign = 1
current = sign * (current >> 1)
ints.append(current)
return ints
def decode_delta_varint_base64(comment):
bytes = custom_b64decode(comment)
deltas = decode_varints(bytes)
prev_pos = 0
positions = []
for d in deltas:
prev_pos += d
positions.append(prev_pos)
return positions
def generate_compile_hints(script_name_to_start_positions):
for script_name, start_positions in script_name_to_start_positions.items():
start_positions = list(start_positions)
start_positions.sort()
print(script_name)
magic_comment = encode_delta_varint_base64(start_positions)
back = decode_delta_varint_base64(magic_comment)
if start_positions != back:
print('Decoding failed!')
exit(1)
print(f"//# functionsCalledOnLoad={magic_comment}\n")
def process_v8_log(filename):
script_id_to_name = {}
script_name_to_start_positions = {}
with open(filename, 'r') as log_file:
for line in log_file:
fields = line.strip().split(',')
if len(fields) >= 3:
if fields[0] == 'script-details':
script_id = int(fields[1])
script_name = fields[2]
script_id_to_name[script_id] = script_name
elif fields[0] == 'function' and fields[1] == 'parse-function':
script_id = int(fields[2])
start_position = int(fields[3])
script_name = script_id_to_name[script_id]
if script_name in script_name_to_start_positions:
script_name_to_start_positions[script_name].add(start_position)
else:
script_name_to_start_positions[script_name] = {start_position}
generate_compile_hints(script_name_to_start_positions)
def process_text(filename):
# Format:
# file1, pos1
# file2, pos2
# file1, pos1
# ...
script_name_to_start_positions = {}
with open(filename, 'r') as log_file:
for line in log_file:
fields = line.strip().split(',')
if len(fields) >= 2:
script_name = fields[0]
start_position = int(fields[1])
if script_name in script_name_to_start_positions:
script_name_to_start_positions[script_name].add(start_position)
else:
script_name_to_start_positions[script_name] = {start_position}
generate_compile_hints(script_name_to_start_positions)
def print_usage():
print(
'Usage: python3 generate-explicit-function-compile-hints.py --v8log v8.log'
)
print(
'or: python3 generate-explicit-function-compile-hints.py --text data.txt'
)
def main():
if len(sys.argv) < 3 or '--help' in sys.argv:
print_usage()
exit(1)
if sys.argv[1] == '--v8log':
process_v8_log(sys.argv[2])
elif sys.argv[1] == '--text':
process_text(sys.argv[2])
else:
print_usage()
exit(1)
if __name__ == '__main__':
main()