blob: 07de7bf2930a7b4e6b0b54f22c05a236666f2224 [file] [log] [blame]
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Ensures that V8's embedded blob is marked as hot.
This is important for performance, as we ensure that code marked as hot ends up
in hugepages. If this isn't the case, there's a good chance that these builtins
may 'accidentally' end up in hugepages, or won't end up in hugepages at all.
"""
# As noted in Chrome's ebuild, this test is a temporary hack until we ship
# orderfiles. Those will guarantee the ordering of this code/etc, so we don't
# need a special hot section anymore.
from __future__ import print_function
import argparse
import subprocess
import sys
def _parse_text_section_bounds(chrome_binary):
"""Find out the boundary of text section from readelf.
Args:
chrome_binary: Path to unstripped Chrome binary.
Returns:
(offset, size): offset and size of text section.
Examples:
Section output looks like:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[16] .text PROGBITS 01070000 1070000 9f82cf6 00 AX 0 0 64
With offset being the fourth field, and size being the sixth field.
"""
readelf = subprocess.check_output(
['llvm-readelf', '--sections', '--wide', chrome_binary]).splitlines()
text = [x.strip() for x in readelf if b'.text' in x]
if len(text) != 1:
raise ValueError('Expected exactly one .text section; got: %s' % text)
text_line = text[0]
fields = text_line.split()
offset, size = fields[3], fields[5]
return int(offset, 16), int(size, 16)
def _parse_orderfile_marker_bounds(chrome_binary):
"""Find out the address of markers from orderfile in readelf.
Args:
chrome_binary: Path to unstripped Chrome binary.
Returns:
(start, end): addresses of chrome_begin_ordered_code and
chrome_end_ordered_code.
Examples:
We have to parse lines like:
Num: Value Size Type Bind Vis Ndx Name
403015: 0000000001070000 5 FUNC LOCAL DEFAULT 16 chrome_begin_ordered_code
403016: 00000000020e3dc0 5 FUNC LOCAL DEFAULT 16 chrome_end_ordered_code
Addresses are at second field.
"""
readelf = subprocess.check_output(
['readelf', '--symbols', '--wide', chrome_binary]).splitlines()
marker_start = [
x.strip() for x in readelf if b'chrome_begin_ordered_code' in x
]
if len(marker_start) != 1:
raise ValueError('Expect exactly one chrome_begin_ordered_code marker')
marker_end = [
x.strip() for x in readelf if b'chrome_end_ordered_code' in x
]
if len(marker_end) != 1:
raise ValueError('Expect exactly one chrome_end_ordered_code marker')
return int(marker_start[0].split()[1], 16), int(marker_end[0].split()[1],
16)
def _parse_builtins_locations(chrome_binary):
"""Find out all the builtin functions and their locations
Args:
chrome_binary: Path to unstripped Chrome binary.
Returns:
List of (location, symbol_name): a list of tuples of the address and
names of builtin functions
Examples:
We have to parse lines like:
Num: Value Size Type Bind Vis Ndx Name
470796: 00d21560 0 FUNC LOCAL DEFAULT 18 Builtins_Abort
We can rely on FUNC, LOCAL, and DEFAULT being consistent for all
`Builtins_`.
"""
cmd = subprocess.Popen(['readelf', '--symbols', '--wide', chrome_binary],
stdout=subprocess.PIPE)
keys = [b'FUNC', b'LOCAL', b'DEFAULT']
ok = True
try:
for line in cmd.stdout:
fields = line.split()
if not fields:
continue
symbol_name = fields[-1]
if not symbol_name.startswith(b'Builtins_'):
continue
if any(key not in fields for key in keys):
continue
location = int(fields[1], 16)
yield location, symbol_name
except:
ok = False
cmd.kill()
raise
finally:
exit_code = cmd.wait()
if ok and exit_code:
raise subprocess.CalledProcessError(exit_code, cmd.args)
def _err(x):
"""Print error message."""
print('%s: error: %s' % (__file__, x), file=sys.stderr)
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('chrome', help='Path to an unstripped chrome binary.')
opts = parser.parse_args(argv)
chrome_binary = opts.chrome
text_start, text_end = _parse_text_section_bounds(chrome_binary)
marker_start, marker_end = _parse_orderfile_marker_bounds(chrome_binary)
if marker_start < text_start or marker_end >= text_end:
_err('Markers landed outside of text section')
return 1
if marker_start >= marker_end:
_err('Markers are not ordered correctly')
return 1
out_of_bounds = []
num_found = 0
for location, symbol_name in _parse_builtins_locations(chrome_binary):
num_found += 1
if location >= marker_end or location < marker_start:
out_of_bounds.append((location, symbol_name))
if not num_found:
_err('No `Builtins_` found. Is the given chrome stripped?')
return 1
if not out_of_bounds:
print('[PASS]%d `Builtins_` functions are placed between the markers' %
num_found)
return 0
out_of_bounds.sort()
_err('%d/%d builtins didn\'t land in between the markers' %
(len(out_of_bounds), num_found))
_err('chrome_begin_ordered_code: %#x' % marker_start)
_err('chrome_end_ordered_code: %#x' % marker_end)
pretty_out_of_bounds = ('%#x: %s' % (offset, name)
for offset, name in out_of_bounds)
_err('Builtins:\n\t' + '\n\t'.join(pretty_out_of_bounds))
return 1
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))