[builtins][pgo] Add helper script to interact with PGO profile bucket
We start to host PGO profiles for builtins on a GCP bucket. This script supports various workflows to download profiles for tagged git versions. In a first step, we provide profiles for tagged git versions only. The script identifies this version from the current checkout and downloads (or validates the existence of) the profiles to a directory where they'll be used during build time. We introduce `checkout_v8_builtins_pgo_profiles` to the DEPS file (defaults to False). If set, we call the new helper script to download the profiles within the gclient sync step. The profile download is added to the Chromium project in crrev.com/c/4131525. Bug: chromium:1382471 Change-Id: I74ba4f3c102a85e230be7ef17b9c87621a1eab14 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4111528 Commit-Queue: Alexander Schulze <alexschulze@chromium.org> Reviewed-by: Liviu Rau <liviurau@chromium.org> Cr-Commit-Position: refs/heads/main@{#85253}
This commit is contained in:
parent
d8cd42360d
commit
25f779623d
13
DEPS
13
DEPS
@ -39,6 +39,9 @@ vars = {
|
||||
# Fetch clang-tidy into the same bin/ directory as our clang binary.
|
||||
'checkout_clang_tidy': False,
|
||||
|
||||
# Fetch and build V8 builtins with PGO profiles
|
||||
'checkout_v8_builtins_pgo_profiles': False,
|
||||
|
||||
'chromium_url': 'https://chromium.googlesource.com',
|
||||
'android_url': 'https://android.googlesource.com',
|
||||
'download_gcmole': False,
|
||||
@ -621,6 +624,16 @@ hooks = [
|
||||
'tools/generate-header-include-checks.py',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'checkout_v8_builtins_pgo_profiles',
|
||||
'pattern': '.',
|
||||
'condition': 'checkout_v8_builtins_pgo_profiles',
|
||||
'action': [
|
||||
'python3',
|
||||
'tools/builtins-pgo/download_profiles.py',
|
||||
'download',
|
||||
],
|
||||
},
|
||||
{
|
||||
# Clean up build dirs for crbug.com/1337238.
|
||||
# After a libc++ roll and revert, .ninja_deps would get into a state
|
||||
|
@ -6,9 +6,20 @@
|
||||
# use Python3 instead of Python2 when running the code in this file.
|
||||
USE_PYTHON3 = True
|
||||
|
||||
TEST_DIRECTORIES = [
|
||||
'unittests',
|
||||
'builtins-pgo',
|
||||
]
|
||||
|
||||
|
||||
def CheckChangeOnCommit(input_api, output_api):
|
||||
tests = input_api.canned_checks.GetUnitTestsInDirectory(
|
||||
input_api, output_api, 'unittests', files_to_check=[r'.+_test\.py$'],
|
||||
run_on_python2=False)
|
||||
tests = []
|
||||
for directory in TEST_DIRECTORIES:
|
||||
tests += input_api.canned_checks.GetUnitTestsInDirectory(
|
||||
input_api,
|
||||
output_api,
|
||||
directory,
|
||||
files_to_check=[r'.+_test\.py$'],
|
||||
run_on_python2=False)
|
||||
|
||||
return input_api.RunTests(tests)
|
||||
|
151
tools/builtins-pgo/download_profiles.py
Executable file
151
tools/builtins-pgo/download_profiles.py
Executable file
@ -0,0 +1,151 @@
|
||||
#!/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.
|
||||
"""
|
||||
Download PGO profiles for V8 builtins. The version is pulled from V8's version
|
||||
file (include/v8-version.h).
|
||||
|
||||
See argparse documentation for usage details.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
FILENAME = os.path.basename(__file__)
|
||||
PGO_PROFILE_BUCKET = 'chromium-v8-builtins-pgo'
|
||||
PGO_PROFILE_DIR = pathlib.Path(os.path.dirname(__file__))
|
||||
|
||||
BASE_DIR = PGO_PROFILE_DIR.parents[1]
|
||||
DEPOT_TOOLS_DEFAULT_PATH = os.path.join(BASE_DIR, 'third_party', 'depot_tools')
|
||||
VERSION_FILE = BASE_DIR / 'include' / 'v8-version.h'
|
||||
VERSION_RE = r"""#define V8_MAJOR_VERSION (\d+)
|
||||
#define V8_MINOR_VERSION (\d+)
|
||||
#define V8_BUILD_NUMBER (\d+)
|
||||
#define V8_PATCH_LEVEL (\d+)"""
|
||||
|
||||
|
||||
def main(cmd_args=None):
|
||||
args = parse_args(cmd_args)
|
||||
import_gsutil(args)
|
||||
version = retrieve_version(args)
|
||||
perform_action(version, args)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def parse_args(cmd_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
f'Download PGO profiles for V8 builtins generated for the version '
|
||||
f'defined in {VERSION_FILE}.'),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog='\n'.join([
|
||||
f'examples:', f' {FILENAME} download',
|
||||
f' {FILENAME} validate --bucket=chromium-v8-builtins-pgo-staging',
|
||||
f'', f'return codes:',
|
||||
f' 0 - profiles successfully downloaded or validated',
|
||||
f' 1 - unexpected error, see stdout',
|
||||
f' 2 - invalid arguments specified, see {FILENAME} --help',
|
||||
f' 3 - invalid path to depot_tools provided'
|
||||
f' 4 - gsutil was unable to retrieve data from the bucket'
|
||||
]),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'action',
|
||||
choices=['download', 'validate'],
|
||||
help=(
|
||||
'download or validate profiles for the currently checked out version'
|
||||
),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
help=('download (or validate) profiles for this version (e.g. 11.0.226.0 '
|
||||
'or 11.0.226.2), defaults to the version in v8\'s version file'),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--depot-tools',
|
||||
help=('path to depot tools, defaults to V8\'s version in '
|
||||
f'{DEPOT_TOOLS_DEFAULT_PATH}.'),
|
||||
type=pathlib.Path,
|
||||
default=DEPOT_TOOLS_DEFAULT_PATH,
|
||||
)
|
||||
|
||||
return parser.parse_args(cmd_args)
|
||||
|
||||
|
||||
def import_gsutil(args):
|
||||
abs_depot_tools_path = os.path.abspath(args.depot_tools)
|
||||
file = os.path.join(abs_depot_tools_path, 'download_from_google_storage.py')
|
||||
if not pathlib.Path(file).is_file():
|
||||
print(f'{file} does not exist; check --depot-tools path.', file=sys.stderr)
|
||||
sys.exit(3)
|
||||
|
||||
sys.path.append(abs_depot_tools_path)
|
||||
globals()['gcs_download'] = __import__('download_from_google_storage')
|
||||
|
||||
|
||||
def retrieve_version(args):
|
||||
if args.version:
|
||||
return args.version
|
||||
|
||||
with open(VERSION_FILE) as f:
|
||||
version_tuple = re.search(VERSION_RE, f.read()).groups(0)
|
||||
return '.'.join(version_tuple)
|
||||
|
||||
|
||||
def perform_action(version, args):
|
||||
path = f'{PGO_PROFILE_BUCKET}/by-version/{version}'
|
||||
|
||||
if args.action == 'download':
|
||||
cmd = ['cp', '-R', f'gs://{path}/*.profile', str(PGO_PROFILE_DIR)]
|
||||
failure_hint = f'https://storage.googleapis.com/{path} does not exist.'
|
||||
call_gsutil(cmd, failure_hint)
|
||||
return
|
||||
|
||||
if args.action == 'validate':
|
||||
meta_json = f'{path}/meta.json'
|
||||
cmd = ['stat', f'gs://{meta_json}']
|
||||
failure_hint = f'https://storage.googleapis.com/{meta_json} does not exist.'
|
||||
call_gsutil(cmd, failure_hint)
|
||||
return
|
||||
|
||||
raise AssertionError(f'Invalid action: {args.action}')
|
||||
|
||||
|
||||
def call_gsutil(cmd, failure_hint):
|
||||
# Load gsutil from depot tools, and execute command
|
||||
gsutil = gcs_download.Gsutil(gcs_download.GSUTIL_DEFAULT_PATH)
|
||||
returncode, stdout, stderr = gsutil.check_call(*cmd)
|
||||
if returncode != 0:
|
||||
print_error(['gsutil', *cmd], returncode, stdout, stderr, failure_hint)
|
||||
sys.exit(4)
|
||||
|
||||
|
||||
def print_error(cmd, returncode, stdout, stderr, failure_hint):
|
||||
message = [
|
||||
'The following command did not succeed:',
|
||||
f' $ {" ".join(cmd)}',
|
||||
]
|
||||
sections = [
|
||||
('return code', str(returncode)),
|
||||
('stdout', stdout.strip()),
|
||||
('stderr', stderr.strip()),
|
||||
('hint', failure_hint),
|
||||
]
|
||||
for label, output in sections:
|
||||
if not output:
|
||||
continue
|
||||
message += [f'{label}:', " " + "\n ".join(output.split("\n"))]
|
||||
|
||||
print('\n'.join(message), file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
62
tools/builtins-pgo/download_profiles_test.py
Normal file
62
tools/builtins-pgo/download_profiles_test.py
Normal file
@ -0,0 +1,62 @@
|
||||
#!/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 contextlib
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from tempfile import TemporaryDirectory
|
||||
from unittest.mock import patch
|
||||
|
||||
from download_profiles import main
|
||||
|
||||
|
||||
class TestDownloadProfiles(unittest.TestCase):
|
||||
|
||||
def _test_cmd(self, cmd, exitcode):
|
||||
out = io.StringIO()
|
||||
err = io.StringIO()
|
||||
with self.assertRaises(SystemExit) as se, \
|
||||
contextlib.redirect_stdout(out), \
|
||||
contextlib.redirect_stderr(err):
|
||||
main(cmd)
|
||||
self.assertEqual(se.exception.code, exitcode)
|
||||
return out.getvalue(), err.getvalue()
|
||||
|
||||
def test_validate_profiles(self):
|
||||
out, err = self._test_cmd(['validate', '--version', '11.1.0.0'], 0)
|
||||
self.assertEqual(len(out), 0)
|
||||
self.assertEqual(len(err), 0)
|
||||
|
||||
def test_download_profiles(self):
|
||||
with TemporaryDirectory() as td, \
|
||||
patch('download_profiles.PGO_PROFILE_DIR', td):
|
||||
out, err = self._test_cmd(['download', '--version', '11.1.0.0'], 0)
|
||||
self.assertEqual(len(out), 0)
|
||||
self.assertEqual(len(err), 0)
|
||||
self.assertGreater(
|
||||
len([f for f in os.listdir(td) if f.endswith('.profile')]), 0)
|
||||
|
||||
def test_invalid_args(self):
|
||||
out, err = self._test_cmd(['invalid-action'], 2)
|
||||
self.assertEqual(len(out), 0)
|
||||
self.assertGreater(len(err), 0)
|
||||
|
||||
def test_invalid_depot_tools_path(self):
|
||||
out, err = self._test_cmd(
|
||||
['validate', '--depot-tools', '/no-depot-tools-path'], 3)
|
||||
self.assertEqual(len(out), 0)
|
||||
self.assertGreater(len(err), 0)
|
||||
|
||||
def test_missing_profiles(self):
|
||||
out, err = self._test_cmd(['download', '--version', '0.0.0.42'], 4)
|
||||
self.assertEqual(len(out), 0)
|
||||
self.assertGreater(len(err), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user