1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# Copyright 2022 The ChromiumOS Authors 3*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 4*760c253cSXin Li# found in the LICENSE file. 5*760c253cSXin Li 6*760c253cSXin Li# pylint: disable=line-too-long 7*760c253cSXin Li 8*760c253cSXin Li"""Handle most aspects of creating and benchmarking PGO profiles for Rust. 9*760c253cSXin Li 10*760c253cSXin LiThis is meant to be done at Rust uprev time. Ultimately profdata files need 11*760c253cSXin Lito be placed at 12*760c253cSXin Li 13*760c253cSXin Ligs://chromeos-localmirror/distfiles/rust-pgo-{rust_version}-frontend.profdata{s}.tz 14*760c253cSXin Liand 15*760c253cSXin Ligs://chromeos-localmirror/distfiles/rust-pgo-{rust_version}-llvm.profdata{s}.tz 16*760c253cSXin Li 17*760c253cSXin LiHere {s} is an optional suffix to distinguish between profdata files on the same 18*760c253cSXin LiRust version. 19*760c253cSXin Li 20*760c253cSXin LiThe intended flow is that you first get the new Rust version in a shape so that 21*760c253cSXin Liit builds, for instance modifying or adding patches as necessary. Note that if 22*760c253cSXin Liyou need to generate manifests for dev-lang/rust and dev-lang/rust-host before 23*760c253cSXin Lithe profdata files are created, which will cause the `ebuild manifest` command 24*760c253cSXin Lito fail. One way to handle this is to temporarily delete the lines of the 25*760c253cSXin Livariable SRC_URI in cros-rustc.eclass which refer to profdata files. 26*760c253cSXin Li 27*760c253cSXin LiAfter you have a new working Rust version, you can run the following. 28*760c253cSXin Li 29*760c253cSXin Li``` 30*760c253cSXin Li$ ./pgo_rust.py generate # generate profdata files 31*760c253cSXin Li$ ./pgo_rust.py benchmark-pgo # benchmark with PGO 32*760c253cSXin Li$ ./pgo_rust.py benchmark-nopgo # benchmark without PGO 33*760c253cSXin Li$ ./pgo_rust.py upload-profdata # upload profdata to localmirror 34*760c253cSXin Li``` 35*760c253cSXin Li 36*760c253cSXin LiThe benchmark steps aren't strictly necessary, but are recommended and will 37*760c253cSXin Liupload benchmark data to 38*760c253cSXin Li 39*760c253cSXin Ligs://chromeos-toolchain-artifacts/rust-pgo/benchmarks/{rust_version}/ 40*760c253cSXin Li 41*760c253cSXin LiCurrently by default ripgrep 13.0.0 is used as both the crate to build using an 42*760c253cSXin Liinstrumented Rust while generating profdata, and the crate to build to 43*760c253cSXin Libenchmark Rust. You may wish to experiment with other crates for either role. 44*760c253cSXin LiIn that case upload your crate to 45*760c253cSXin Li 46*760c253cSXin Ligs://chromeos-toolchain-artifacts/rust-pgo/crates/{name}-{version}.tar.xz 47*760c253cSXin Li 48*760c253cSXin Liand use `--crate-name` and `--crate-version` to indicate which crate to build 49*760c253cSXin Lito generate profdata (or which crate's generated profdata to use), and 50*760c253cSXin Li`--bench-crate-name` to indicate which crate to build in benchmarks. 51*760c253cSXin Li 52*760c253cSXin LiNotes on various local and GS locations follow. 53*760c253cSXin Li 54*760c253cSXin LiNote that currently we need to keep separate profdata files for the LLVM and 55*760c253cSXin Lifrontend components of Rust. This is because LLVM profdata is instrumented by 56*760c253cSXin Lithe system LLVM, but Rust's profdata is instrumented by its own LLVM, which 57*760c253cSXin Limay have separate profdata. 58*760c253cSXin Li 59*760c253cSXin Liprofdata files accessed by ebuilds must be stored in 60*760c253cSXin Li 61*760c253cSXin Ligs://chromeos-localmirror/distfiles 62*760c253cSXin Li 63*760c253cSXin LiSpecifically, they go to 64*760c253cSXin Li 65*760c253cSXin Ligs://chromeos-localmirror/distfiles/rust-pgo-{rust-version}-llvm.profdata.xz 66*760c253cSXin Li 67*760c253cSXin Ligs://chromeos-localmirror/distfiles/ 68*760c253cSXin Li rust-pgo-{rust-version}-frontend.profdata.xz 69*760c253cSXin Li 70*760c253cSXin LiBut we can store other data elsewhere, like gs://chromeos-toolchain-artifacts. 71*760c253cSXin Li 72*760c253cSXin LiGS locations: 73*760c253cSXin Li 74*760c253cSXin Li{GS_BASE}/crates/ - store crates we may use for generating profiles or 75*760c253cSXin Libenchmarking PGO optimized Rust compilers 76*760c253cSXin Li 77*760c253cSXin Li{GS_BASE}/benchmarks/{rust_version}/nopgo/ 78*760c253cSXin Li {bench_crate_name}-{bench_crate_version}-{triple} 79*760c253cSXin Li 80*760c253cSXin Li{GS_BASE}/benchmarks/{rust_version}/{crate_name}-{crate_version}/ 81*760c253cSXin Li {bench_crate_name}-{bench_crate_version}-{triple} 82*760c253cSXin Li 83*760c253cSXin LiLocal locations: 84*760c253cSXin Li 85*760c253cSXin Li{LOCAL_BASE}/crates/ 86*760c253cSXin Li 87*760c253cSXin Li{LOCAL_BASE}/llvm-profraw/ 88*760c253cSXin Li 89*760c253cSXin Li{LOCAL_BASE}/frontend-profraw/ 90*760c253cSXin Li 91*760c253cSXin Li{LOCAL_BASE}/profdata/{crate_name}-{crate_version}/llvm.profdata 92*760c253cSXin Li 93*760c253cSXin Li{LOCAL_BASE}/profdata/{crate_name}-{crate_version}/frontend.profdata 94*760c253cSXin Li 95*760c253cSXin Li{LOCAL_BASE}/benchmarks/{rust_version}/nopgo/ 96*760c253cSXin Li {bench_crate_name}-{bench_crate_version}-{triple} 97*760c253cSXin Li 98*760c253cSXin Li{LOCAL_BASE}/benchmarks/{rust_version}/{crate_name}-{crate_version}/ 99*760c253cSXin Li {bench_crate_name}-{bench_crate_version}-{triple} 100*760c253cSXin Li 101*760c253cSXin Li{LOCAL_BASE}/llvm.profdata - must go here to be used by Rust ebuild 102*760c253cSXin Li{LOCAL_BASE}/frontend.profdata - must go here to be used by Rust ebuild 103*760c253cSXin Li""" 104*760c253cSXin Li 105*760c253cSXin Liimport argparse 106*760c253cSXin Liimport contextlib 107*760c253cSXin Liimport logging 108*760c253cSXin Liimport os 109*760c253cSXin Lifrom pathlib import Path 110*760c253cSXin Lifrom pathlib import PurePosixPath 111*760c253cSXin Liimport re 112*760c253cSXin Liimport shutil 113*760c253cSXin Liimport subprocess 114*760c253cSXin Liimport sys 115*760c253cSXin Lifrom typing import cast, List, Mapping, Optional 116*760c253cSXin Li 117*760c253cSXin Li 118*760c253cSXin LiTARGET_TRIPLES = [ 119*760c253cSXin Li "x86_64-cros-linux-gnu", 120*760c253cSXin Li "x86_64-pc-linux-gnu", 121*760c253cSXin Li "armv7a-cros-linux-gnueabihf", 122*760c253cSXin Li "aarch64-cros-linux-gnu", 123*760c253cSXin Li] 124*760c253cSXin Li 125*760c253cSXin LiLOCAL_BASE = Path("/tmp/rust-pgo") 126*760c253cSXin Li 127*760c253cSXin LiGS_BASE = PurePosixPath("/chromeos-toolchain-artifacts/rust-pgo") 128*760c253cSXin Li 129*760c253cSXin LiGS_DISTFILES = PurePosixPath("/chromeos-localmirror/distfiles") 130*760c253cSXin Li 131*760c253cSXin LiCRATE_NAME = "ripgrep" 132*760c253cSXin Li 133*760c253cSXin LiCRATE_VERSION = "13.0.0" 134*760c253cSXin Li 135*760c253cSXin Li 136*760c253cSXin Li@contextlib.contextmanager 137*760c253cSXin Lidef chdir(new_directory: Path): 138*760c253cSXin Li initial_directory = Path.cwd() 139*760c253cSXin Li os.chdir(new_directory) 140*760c253cSXin Li try: 141*760c253cSXin Li yield 142*760c253cSXin Li finally: 143*760c253cSXin Li os.chdir(initial_directory) 144*760c253cSXin Li 145*760c253cSXin Li 146*760c253cSXin Lidef run( 147*760c253cSXin Li args: List, 148*760c253cSXin Li *, 149*760c253cSXin Li indent: int = 4, 150*760c253cSXin Li env: Optional[Mapping[str, str]] = None, 151*760c253cSXin Li capture_stdout: bool = False, 152*760c253cSXin Li message: bool = True, 153*760c253cSXin Li) -> Optional[str]: 154*760c253cSXin Li args = [str(arg) for arg in args] 155*760c253cSXin Li 156*760c253cSXin Li if env is None: 157*760c253cSXin Li new_env: Mapping[str, str] = os.environ 158*760c253cSXin Li else: 159*760c253cSXin Li new_env = os.environ.copy() 160*760c253cSXin Li new_env.update(env) 161*760c253cSXin Li 162*760c253cSXin Li if message: 163*760c253cSXin Li if env is None: 164*760c253cSXin Li logging.info("Running %s", args) 165*760c253cSXin Li else: 166*760c253cSXin Li logging.info("Running %s in environment %s", args, env) 167*760c253cSXin Li 168*760c253cSXin Li result = subprocess.run( 169*760c253cSXin Li args, 170*760c253cSXin Li env=new_env, 171*760c253cSXin Li stdout=subprocess.PIPE, 172*760c253cSXin Li stderr=subprocess.PIPE, 173*760c253cSXin Li encoding="utf-8", 174*760c253cSXin Li check=False, 175*760c253cSXin Li ) 176*760c253cSXin Li 177*760c253cSXin Li stdout = result.stdout 178*760c253cSXin Li stderr = result.stderr 179*760c253cSXin Li if indent != 0: 180*760c253cSXin Li stdout = re.sub("^", " " * indent, stdout, flags=re.MULTILINE) 181*760c253cSXin Li stderr = re.sub("^", " " * indent, stderr, flags=re.MULTILINE) 182*760c253cSXin Li 183*760c253cSXin Li if capture_stdout: 184*760c253cSXin Li ret = result.stdout 185*760c253cSXin Li else: 186*760c253cSXin Li logging.info("STDOUT:") 187*760c253cSXin Li logging.info(stdout) 188*760c253cSXin Li logging.info("STDERR:") 189*760c253cSXin Li logging.info(stderr) 190*760c253cSXin Li ret = None 191*760c253cSXin Li 192*760c253cSXin Li result.check_returncode() 193*760c253cSXin Li 194*760c253cSXin Li if message: 195*760c253cSXin Li if env is None: 196*760c253cSXin Li logging.info("Ran %s\n", args) 197*760c253cSXin Li else: 198*760c253cSXin Li logging.info("Ran %s in environment %s\n", args, env) 199*760c253cSXin Li 200*760c253cSXin Li return ret 201*760c253cSXin Li 202*760c253cSXin Li 203*760c253cSXin Lidef get_command_output(args: List, **kwargs) -> str: 204*760c253cSXin Li """Runs a command and returns its stdout and stderr as a string.""" 205*760c253cSXin Li return cast(str, run(args, capture_stdout=True, **kwargs)) 206*760c253cSXin Li 207*760c253cSXin Li 208*760c253cSXin Lidef get_rust_version() -> str: 209*760c253cSXin Li s = get_command_output(["rustc", "--version"]) 210*760c253cSXin Li m = re.search(r"\d+\.\d+\.\d+", s) 211*760c253cSXin Li assert m is not None, repr(s) 212*760c253cSXin Li return m.group(0) 213*760c253cSXin Li 214*760c253cSXin Li 215*760c253cSXin Lidef download_unpack_crate(*, crate_name: str, crate_version: str): 216*760c253cSXin Li filename_no_extension = f"{crate_name}-{crate_version}" 217*760c253cSXin Li gs_path = GS_BASE / "crates" / f"{filename_no_extension}.tar.xz" 218*760c253cSXin Li local_path = LOCAL_BASE / "crates" 219*760c253cSXin Li shutil.rmtree( 220*760c253cSXin Li local_path / f"{crate_name}-{crate_version}", ignore_errors=True 221*760c253cSXin Li ) 222*760c253cSXin Li with chdir(local_path): 223*760c253cSXin Li run(["gsutil", "cp", f"gs:/{gs_path}", "."]) 224*760c253cSXin Li run(["tar", "xaf", f"{filename_no_extension}.tar.xz"]) 225*760c253cSXin Li 226*760c253cSXin Li 227*760c253cSXin Lidef build_crate( 228*760c253cSXin Li *, 229*760c253cSXin Li crate_name: str, 230*760c253cSXin Li crate_version: str, 231*760c253cSXin Li target_triple: str, 232*760c253cSXin Li time_file: Optional[str] = None, 233*760c253cSXin Li): 234*760c253cSXin Li local_path = LOCAL_BASE / "crates" / f"{crate_name}-{crate_version}" 235*760c253cSXin Li with chdir(local_path): 236*760c253cSXin Li Path(".cargo").mkdir(exist_ok=True) 237*760c253cSXin Li with open(".cargo/config.toml", "w", encoding="utf-8") as f: 238*760c253cSXin Li f.write( 239*760c253cSXin Li "\n".join( 240*760c253cSXin Li ( 241*760c253cSXin Li "[source.crates-io]", 242*760c253cSXin Li 'replace-with = "vendored-sources"', 243*760c253cSXin Li "", 244*760c253cSXin Li "[source.vendored-sources]", 245*760c253cSXin Li 'directory = "vendor"', 246*760c253cSXin Li "", 247*760c253cSXin Li f"[target.{target_triple}]", 248*760c253cSXin Li f'linker = "{target_triple}-clang"', 249*760c253cSXin Li "", 250*760c253cSXin Li "[target.'cfg(all())']", 251*760c253cSXin Li "rustflags = [", 252*760c253cSXin Li ' "-Clto=thin",', 253*760c253cSXin Li ' "-Cembed-bitcode=yes",', 254*760c253cSXin Li "]", 255*760c253cSXin Li ) 256*760c253cSXin Li ) 257*760c253cSXin Li ) 258*760c253cSXin Li 259*760c253cSXin Li run(["cargo", "clean"]) 260*760c253cSXin Li 261*760c253cSXin Li cargo_cmd = ["cargo", "build", "--release", "--target", target_triple] 262*760c253cSXin Li 263*760c253cSXin Li if time_file is None: 264*760c253cSXin Li run(cargo_cmd) 265*760c253cSXin Li else: 266*760c253cSXin Li time_cmd = [ 267*760c253cSXin Li "/usr/bin/time", 268*760c253cSXin Li f"--output={time_file}", 269*760c253cSXin Li "--format=wall time (s) %e\nuser time (s) %U\nmax RSS %M\n", 270*760c253cSXin Li ] 271*760c253cSXin Li run(time_cmd + cargo_cmd) 272*760c253cSXin Li 273*760c253cSXin Li 274*760c253cSXin Lidef build_rust( 275*760c253cSXin Li *, 276*760c253cSXin Li generate_frontend_profile: bool = False, 277*760c253cSXin Li generate_llvm_profile: bool = False, 278*760c253cSXin Li use_frontend_profile: bool = False, 279*760c253cSXin Li use_llvm_profile: bool = False, 280*760c253cSXin Li): 281*760c253cSXin Li if use_frontend_profile or use_llvm_profile: 282*760c253cSXin Li assert not generate_frontend_profile and not generate_llvm_profile, ( 283*760c253cSXin Li "Can't build a compiler to both use profile information " 284*760c253cSXin Li "and generate it" 285*760c253cSXin Li ) 286*760c253cSXin Li 287*760c253cSXin Li assert ( 288*760c253cSXin Li not generate_frontend_profile or not generate_llvm_profile 289*760c253cSXin Li ), "Can't generate both frontend and LLVM profile information" 290*760c253cSXin Li 291*760c253cSXin Li use = "-rust_profile_frontend_use -rust_profile_llvm_use " 292*760c253cSXin Li if generate_frontend_profile: 293*760c253cSXin Li use += "rust_profile_frontend_generate " 294*760c253cSXin Li if generate_llvm_profile: 295*760c253cSXin Li use += "rust_profile_llvm_generate " 296*760c253cSXin Li if use_frontend_profile: 297*760c253cSXin Li use += "rust_profile_frontend_use_local " 298*760c253cSXin Li if use_llvm_profile: 299*760c253cSXin Li use += "rust_profile_llvm_use_local " 300*760c253cSXin Li 301*760c253cSXin Li env_use = os.getenv("USE", "").rstrip() 302*760c253cSXin Li use = (env_use + " " + use).strip() 303*760c253cSXin Li rust_cross_packages = [ 304*760c253cSXin Li f"cross-{x}/rust" for x in TARGET_TRIPLES if "-pc-linux-" not in x 305*760c253cSXin Li ] 306*760c253cSXin Li 307*760c253cSXin Li # -E to preserve environment variables like USE, FEATURES, etc. 308*760c253cSXin Li run( 309*760c253cSXin Li [ 310*760c253cSXin Li "sudo", 311*760c253cSXin Li "-E", 312*760c253cSXin Li "emerge", 313*760c253cSXin Li "-j", 314*760c253cSXin Li "dev-lang/rust-host", 315*760c253cSXin Li ] 316*760c253cSXin Li + rust_cross_packages, 317*760c253cSXin Li env={"USE": use}, 318*760c253cSXin Li ) 319*760c253cSXin Li 320*760c253cSXin Li 321*760c253cSXin Lidef merge_profdata(llvm_or_frontend, *, source_directory: Path, dest: Path): 322*760c253cSXin Li assert llvm_or_frontend in ("llvm", "frontend") 323*760c253cSXin Li 324*760c253cSXin Li # The two `llvm-profdata` programs come from different LLVM versions, and 325*760c253cSXin Li # may support different versions of the profdata format, so make sure to 326*760c253cSXin Li # use the right one. 327*760c253cSXin Li llvm_profdata = ( 328*760c253cSXin Li "/usr/bin/llvm-profdata" 329*760c253cSXin Li if llvm_or_frontend == "llvm" 330*760c253cSXin Li else "/usr/libexec/rust/llvm-profdata" 331*760c253cSXin Li ) 332*760c253cSXin Li 333*760c253cSXin Li dest.parent.mkdir(parents=True, exist_ok=True) 334*760c253cSXin Li 335*760c253cSXin Li files = list(source_directory.glob("*.profraw")) 336*760c253cSXin Li assert files, f"No profraw files found in {source_directory}" 337*760c253cSXin Li run([llvm_profdata, "merge", f"--output={dest}"] + files) 338*760c253cSXin Li 339*760c253cSXin Li 340*760c253cSXin Lidef do_upload_profdata(*, source: Path, dest: PurePosixPath): 341*760c253cSXin Li new_path = source.parent / (source.name + ".xz") 342*760c253cSXin Li run(["xz", "--keep", "--compress", "--force", source]) 343*760c253cSXin Li upload_file(source=new_path, dest=dest, public_read=True) 344*760c253cSXin Li 345*760c253cSXin Li 346*760c253cSXin Lidef upload_file( 347*760c253cSXin Li *, source: Path, dest: PurePosixPath, public_read: bool = False 348*760c253cSXin Li): 349*760c253cSXin Li if public_read: 350*760c253cSXin Li run(["gsutil", "cp", "-a", "public-read", source, f"gs:/{dest}"]) 351*760c253cSXin Li else: 352*760c253cSXin Li run(["gsutil", "cp", source, f"gs:/{dest}"]) 353*760c253cSXin Li 354*760c253cSXin Li 355*760c253cSXin Lidef maybe_download_crate(*, crate_name: str, crate_version: str): 356*760c253cSXin Li """Downloads a crate if its download directory does not already exist.""" 357*760c253cSXin Li directory = LOCAL_BASE / "crates" / f"{crate_name}-{crate_version}" 358*760c253cSXin Li if directory.is_dir(): 359*760c253cSXin Li logging.info("Crate already downloaded") 360*760c253cSXin Li else: 361*760c253cSXin Li logging.info("Downloading crate") 362*760c253cSXin Li download_unpack_crate( 363*760c253cSXin Li crate_name=crate_name, crate_version=crate_version 364*760c253cSXin Li ) 365*760c253cSXin Li 366*760c253cSXin Li 367*760c253cSXin Lidef generate(args): 368*760c253cSXin Li maybe_download_crate( 369*760c253cSXin Li crate_name=args.crate_name, crate_version=args.crate_version 370*760c253cSXin Li ) 371*760c253cSXin Li 372*760c253cSXin Li llvm_dir = LOCAL_BASE / "llvm-profraw" 373*760c253cSXin Li shutil.rmtree(llvm_dir, ignore_errors=True) 374*760c253cSXin Li frontend_dir = LOCAL_BASE / "frontend-profraw" 375*760c253cSXin Li shutil.rmtree(frontend_dir, ignore_errors=True) 376*760c253cSXin Li 377*760c253cSXin Li logging.info("Building Rust instrumented for llvm") 378*760c253cSXin Li build_rust(generate_llvm_profile=True) 379*760c253cSXin Li 380*760c253cSXin Li llvm_dir.mkdir(parents=True, exist_ok=True) 381*760c253cSXin Li for triple in TARGET_TRIPLES: 382*760c253cSXin Li logging.info( 383*760c253cSXin Li "Building crate with LLVM instrumentation, for triple %s", triple 384*760c253cSXin Li ) 385*760c253cSXin Li build_crate( 386*760c253cSXin Li crate_name=args.crate_name, 387*760c253cSXin Li crate_version=args.crate_version, 388*760c253cSXin Li target_triple=triple, 389*760c253cSXin Li ) 390*760c253cSXin Li 391*760c253cSXin Li logging.info("Merging LLVM profile data") 392*760c253cSXin Li merge_profdata( 393*760c253cSXin Li "llvm", 394*760c253cSXin Li source_directory=LOCAL_BASE / "llvm-profraw", 395*760c253cSXin Li dest=( 396*760c253cSXin Li LOCAL_BASE 397*760c253cSXin Li / "profdata" 398*760c253cSXin Li / f"{args.crate_name}-{args.crate_version}" 399*760c253cSXin Li / "llvm.profdata" 400*760c253cSXin Li ), 401*760c253cSXin Li ) 402*760c253cSXin Li 403*760c253cSXin Li logging.info("Building Rust instrumented for frontend") 404*760c253cSXin Li build_rust(generate_frontend_profile=True) 405*760c253cSXin Li 406*760c253cSXin Li frontend_dir.mkdir(parents=True, exist_ok=True) 407*760c253cSXin Li for triple in TARGET_TRIPLES: 408*760c253cSXin Li logging.info( 409*760c253cSXin Li "Building crate with frontend instrumentation, for triple %s", 410*760c253cSXin Li triple, 411*760c253cSXin Li ) 412*760c253cSXin Li build_crate( 413*760c253cSXin Li crate_name=args.crate_name, 414*760c253cSXin Li crate_version=args.crate_version, 415*760c253cSXin Li target_triple=triple, 416*760c253cSXin Li ) 417*760c253cSXin Li 418*760c253cSXin Li logging.info("Merging frontend profile data") 419*760c253cSXin Li merge_profdata( 420*760c253cSXin Li "frontend", 421*760c253cSXin Li source_directory=LOCAL_BASE / "frontend-profraw", 422*760c253cSXin Li dest=( 423*760c253cSXin Li LOCAL_BASE 424*760c253cSXin Li / "profdata" 425*760c253cSXin Li / f"{args.crate_name}-{args.crate_version}" 426*760c253cSXin Li / "frontend.profdata" 427*760c253cSXin Li ), 428*760c253cSXin Li ) 429*760c253cSXin Li 430*760c253cSXin Li 431*760c253cSXin Lidef benchmark_nopgo(args): 432*760c253cSXin Li maybe_download_crate( 433*760c253cSXin Li crate_name=args.bench_crate_name, crate_version=args.bench_crate_version 434*760c253cSXin Li ) 435*760c253cSXin Li 436*760c253cSXin Li logging.info("Building Rust, no PGO") 437*760c253cSXin Li build_rust() 438*760c253cSXin Li 439*760c253cSXin Li time_directory = LOCAL_BASE / "benchmarks" / "nopgo" 440*760c253cSXin Li logging.info("Benchmarking crate build with no PGO") 441*760c253cSXin Li time_directory.mkdir(parents=True, exist_ok=True) 442*760c253cSXin Li for triple in TARGET_TRIPLES: 443*760c253cSXin Li build_crate( 444*760c253cSXin Li crate_name=args.bench_crate_name, 445*760c253cSXin Li crate_version=args.bench_crate_version, 446*760c253cSXin Li target_triple=triple, 447*760c253cSXin Li time_file=( 448*760c253cSXin Li time_directory 449*760c253cSXin Li / f"{args.bench_crate_name}-{args.bench_crate_version}-{triple}" 450*760c253cSXin Li ), 451*760c253cSXin Li ) 452*760c253cSXin Li 453*760c253cSXin Li rust_version = get_rust_version() 454*760c253cSXin Li dest_directory = ( 455*760c253cSXin Li GS_BASE / "benchmarks" / rust_version / f"nopgo{args.suffix}" 456*760c253cSXin Li ) 457*760c253cSXin Li logging.info("Uploading benchmark data") 458*760c253cSXin Li for file in time_directory.iterdir(): 459*760c253cSXin Li upload_file( 460*760c253cSXin Li source=time_directory / file.name, dest=dest_directory / file.name 461*760c253cSXin Li ) 462*760c253cSXin Li 463*760c253cSXin Li 464*760c253cSXin Lidef benchmark_pgo(args): 465*760c253cSXin Li maybe_download_crate( 466*760c253cSXin Li crate_name=args.bench_crate_name, crate_version=args.bench_crate_version 467*760c253cSXin Li ) 468*760c253cSXin Li 469*760c253cSXin Li files_dir = Path( 470*760c253cSXin Li "/mnt/host/source/src/third_party/chromiumos-overlay", 471*760c253cSXin Li "dev-lang/rust/files", 472*760c253cSXin Li ) 473*760c253cSXin Li 474*760c253cSXin Li logging.info("Copying profile data to be used in building Rust") 475*760c253cSXin Li run( 476*760c253cSXin Li [ 477*760c253cSXin Li "cp", 478*760c253cSXin Li ( 479*760c253cSXin Li LOCAL_BASE 480*760c253cSXin Li / "profdata" 481*760c253cSXin Li / f"{args.crate_name}-{args.crate_version}" 482*760c253cSXin Li / "llvm.profdata" 483*760c253cSXin Li ), 484*760c253cSXin Li files_dir, 485*760c253cSXin Li ] 486*760c253cSXin Li ) 487*760c253cSXin Li run( 488*760c253cSXin Li [ 489*760c253cSXin Li "cp", 490*760c253cSXin Li ( 491*760c253cSXin Li LOCAL_BASE 492*760c253cSXin Li / "profdata" 493*760c253cSXin Li / f"{args.crate_name}-{args.crate_version}" 494*760c253cSXin Li / "frontend.profdata" 495*760c253cSXin Li ), 496*760c253cSXin Li files_dir, 497*760c253cSXin Li ] 498*760c253cSXin Li ) 499*760c253cSXin Li 500*760c253cSXin Li logging.info("Building Rust with PGO") 501*760c253cSXin Li build_rust(use_llvm_profile=True, use_frontend_profile=True) 502*760c253cSXin Li 503*760c253cSXin Li time_directory = ( 504*760c253cSXin Li LOCAL_BASE / "benchmarks" / f"{args.crate_name}-{args.crate_version}" 505*760c253cSXin Li ) 506*760c253cSXin Li time_directory.mkdir(parents=True, exist_ok=True) 507*760c253cSXin Li logging.info("Benchmarking crate built with PGO") 508*760c253cSXin Li for triple in TARGET_TRIPLES: 509*760c253cSXin Li build_crate( 510*760c253cSXin Li crate_name=args.bench_crate_name, 511*760c253cSXin Li crate_version=args.bench_crate_version, 512*760c253cSXin Li target_triple=triple, 513*760c253cSXin Li time_file=( 514*760c253cSXin Li time_directory 515*760c253cSXin Li / f"{args.bench_crate_name}-{args.bench_crate_version}-{triple}" 516*760c253cSXin Li ), 517*760c253cSXin Li ) 518*760c253cSXin Li 519*760c253cSXin Li rust_version = get_rust_version() 520*760c253cSXin Li dest_directory = ( 521*760c253cSXin Li GS_BASE 522*760c253cSXin Li / "benchmarks" 523*760c253cSXin Li / rust_version 524*760c253cSXin Li / f"{args.crate_name}-{args.crate_version}{args.suffix}" 525*760c253cSXin Li ) 526*760c253cSXin Li logging.info("Uploading benchmark data") 527*760c253cSXin Li for file in time_directory.iterdir(): 528*760c253cSXin Li upload_file( 529*760c253cSXin Li source=time_directory / file.name, dest=dest_directory / file.name 530*760c253cSXin Li ) 531*760c253cSXin Li 532*760c253cSXin Li 533*760c253cSXin Lidef upload_profdata(args): 534*760c253cSXin Li directory = ( 535*760c253cSXin Li LOCAL_BASE / "profdata" / f"{args.crate_name}-{args.crate_version}" 536*760c253cSXin Li ) 537*760c253cSXin Li rust_version = get_rust_version() 538*760c253cSXin Li 539*760c253cSXin Li logging.info("Uploading LLVM profdata") 540*760c253cSXin Li do_upload_profdata( 541*760c253cSXin Li source=directory / "llvm.profdata", 542*760c253cSXin Li dest=( 543*760c253cSXin Li GS_DISTFILES 544*760c253cSXin Li / f"rust-pgo-{rust_version}-llvm{args.suffix}.profdata.xz" 545*760c253cSXin Li ), 546*760c253cSXin Li ) 547*760c253cSXin Li 548*760c253cSXin Li logging.info("Uploading frontend profdata") 549*760c253cSXin Li do_upload_profdata( 550*760c253cSXin Li source=directory / "frontend.profdata", 551*760c253cSXin Li dest=( 552*760c253cSXin Li GS_DISTFILES 553*760c253cSXin Li / f"rust-pgo-{rust_version}-frontend{args.suffix}.profdata.xz" 554*760c253cSXin Li ), 555*760c253cSXin Li ) 556*760c253cSXin Li 557*760c253cSXin Li 558*760c253cSXin Lidef main(argv: List[str]) -> int: 559*760c253cSXin Li logging.basicConfig( 560*760c253cSXin Li stream=sys.stdout, level=logging.NOTSET, format="%(message)s" 561*760c253cSXin Li ) 562*760c253cSXin Li 563*760c253cSXin Li parser = argparse.ArgumentParser( 564*760c253cSXin Li prog=argv[0], 565*760c253cSXin Li description=__doc__, 566*760c253cSXin Li formatter_class=argparse.RawDescriptionHelpFormatter, 567*760c253cSXin Li ) 568*760c253cSXin Li subparsers = parser.add_subparsers(dest="command", help="") 569*760c253cSXin Li subparsers.required = True 570*760c253cSXin Li 571*760c253cSXin Li parser_generate = subparsers.add_parser( 572*760c253cSXin Li "generate", 573*760c253cSXin Li help="Generate LLVM and frontend profdata files by building " 574*760c253cSXin Li "instrumented Rust compilers, and using them to build the " 575*760c253cSXin Li "indicated crate (downloading the crate if necessary).", 576*760c253cSXin Li ) 577*760c253cSXin Li parser_generate.set_defaults(func=generate) 578*760c253cSXin Li parser_generate.add_argument( 579*760c253cSXin Li "--crate-name", default=CRATE_NAME, help="Name of the crate to build" 580*760c253cSXin Li ) 581*760c253cSXin Li parser_generate.add_argument( 582*760c253cSXin Li "--crate-version", 583*760c253cSXin Li default=CRATE_VERSION, 584*760c253cSXin Li help="Version of the crate to build", 585*760c253cSXin Li ) 586*760c253cSXin Li 587*760c253cSXin Li parser_benchmark_nopgo = subparsers.add_parser( 588*760c253cSXin Li "benchmark-nopgo", 589*760c253cSXin Li help="Build the Rust compiler without PGO, benchmark " 590*760c253cSXin Li "the build of the indicated crate, and upload " 591*760c253cSXin Li "the benchmark data.", 592*760c253cSXin Li ) 593*760c253cSXin Li parser_benchmark_nopgo.set_defaults(func=benchmark_nopgo) 594*760c253cSXin Li parser_benchmark_nopgo.add_argument( 595*760c253cSXin Li "--bench-crate-name", 596*760c253cSXin Li default=CRATE_NAME, 597*760c253cSXin Li help="Name of the crate whose build to benchmark", 598*760c253cSXin Li ) 599*760c253cSXin Li parser_benchmark_nopgo.add_argument( 600*760c253cSXin Li "--bench-crate-version", 601*760c253cSXin Li default=CRATE_VERSION, 602*760c253cSXin Li help="Version of the crate whose benchmark to build", 603*760c253cSXin Li ) 604*760c253cSXin Li parser_benchmark_nopgo.add_argument( 605*760c253cSXin Li "--suffix", 606*760c253cSXin Li default="", 607*760c253cSXin Li help="Suffix to distinguish benchmarks and profdata with identical " 608*760c253cSXin Li "rustc versions", 609*760c253cSXin Li ) 610*760c253cSXin Li 611*760c253cSXin Li parser_benchmark_pgo = subparsers.add_parser( 612*760c253cSXin Li "benchmark-pgo", 613*760c253cSXin Li help="Build the Rust compiler using PGO with the indicated " 614*760c253cSXin Li "profdata files, benchmark the build of the indicated crate, " 615*760c253cSXin Li "and upload the benchmark data.", 616*760c253cSXin Li ) 617*760c253cSXin Li parser_benchmark_pgo.set_defaults(func=benchmark_pgo) 618*760c253cSXin Li parser_benchmark_pgo.add_argument( 619*760c253cSXin Li "--bench-crate-name", 620*760c253cSXin Li default=CRATE_NAME, 621*760c253cSXin Li help="Name of the crate whose build to benchmark", 622*760c253cSXin Li ) 623*760c253cSXin Li parser_benchmark_pgo.add_argument( 624*760c253cSXin Li "--bench-crate-version", 625*760c253cSXin Li default=CRATE_VERSION, 626*760c253cSXin Li help="Version of the crate whose benchmark to build", 627*760c253cSXin Li ) 628*760c253cSXin Li parser_benchmark_pgo.add_argument( 629*760c253cSXin Li "--crate-name", 630*760c253cSXin Li default=CRATE_NAME, 631*760c253cSXin Li help="Name of the crate whose profile to use", 632*760c253cSXin Li ) 633*760c253cSXin Li parser_benchmark_pgo.add_argument( 634*760c253cSXin Li "--crate-version", 635*760c253cSXin Li default=CRATE_VERSION, 636*760c253cSXin Li help="Version of the crate whose profile to use", 637*760c253cSXin Li ) 638*760c253cSXin Li parser_benchmark_pgo.add_argument( 639*760c253cSXin Li "--suffix", 640*760c253cSXin Li default="", 641*760c253cSXin Li help="Suffix to distinguish benchmarks and profdata with identical " 642*760c253cSXin Li "rustc versions", 643*760c253cSXin Li ) 644*760c253cSXin Li 645*760c253cSXin Li parser_upload_profdata = subparsers.add_parser( 646*760c253cSXin Li "upload-profdata", help="Upload the profdata files" 647*760c253cSXin Li ) 648*760c253cSXin Li parser_upload_profdata.set_defaults(func=upload_profdata) 649*760c253cSXin Li parser_upload_profdata.add_argument( 650*760c253cSXin Li "--crate-name", 651*760c253cSXin Li default=CRATE_NAME, 652*760c253cSXin Li help="Name of the crate whose profile to use", 653*760c253cSXin Li ) 654*760c253cSXin Li parser_upload_profdata.add_argument( 655*760c253cSXin Li "--crate-version", 656*760c253cSXin Li default=CRATE_VERSION, 657*760c253cSXin Li help="Version of the crate whose profile to use", 658*760c253cSXin Li ) 659*760c253cSXin Li parser_upload_profdata.add_argument( 660*760c253cSXin Li "--suffix", 661*760c253cSXin Li default="", 662*760c253cSXin Li help="Suffix to distinguish benchmarks and profdata with identical " 663*760c253cSXin Li "rustc versions", 664*760c253cSXin Li ) 665*760c253cSXin Li 666*760c253cSXin Li args = parser.parse_args(argv[1:]) 667*760c253cSXin Li 668*760c253cSXin Li (LOCAL_BASE / "crates").mkdir(parents=True, exist_ok=True) 669*760c253cSXin Li (LOCAL_BASE / "llvm-profraw").mkdir(parents=True, exist_ok=True) 670*760c253cSXin Li (LOCAL_BASE / "frontend-profraw").mkdir(parents=True, exist_ok=True) 671*760c253cSXin Li (LOCAL_BASE / "benchmarks").mkdir(parents=True, exist_ok=True) 672*760c253cSXin Li 673*760c253cSXin Li args.func(args) 674*760c253cSXin Li 675*760c253cSXin Li return 0 676*760c253cSXin Li 677*760c253cSXin Li 678*760c253cSXin Liif __name__ == "__main__": 679*760c253cSXin Li sys.exit(main(sys.argv)) 680