mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
Signed-off-by: Chengzhong Wu <legendecas@gmail.com> PR-URL: https://github.com/nodejs/node/pull/66136 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
93 lines
3.4 KiB
Python
93 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Train a Clang PGO build and merge its profiles into node.profdata."""
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
|
|
def find_llvm_profdata():
|
|
override = os.environ.get('LLVM_PROFDATA')
|
|
if override:
|
|
tool = shutil.which(override)
|
|
elif sys.platform == 'darwin':
|
|
tool = subprocess.check_output(
|
|
['xcrun', '--find', 'llvm-profdata'], text=True).strip()
|
|
else:
|
|
candidates = []
|
|
if sys.platform == 'win32':
|
|
vc_install = os.environ.get('VCINSTALLDIR')
|
|
if vc_install:
|
|
candidates.append(Path(vc_install) / 'Tools/Llvm/x64/bin/llvm-profdata.exe')
|
|
program_files = os.environ.get('ProgramFiles')
|
|
if program_files:
|
|
candidates.extend(
|
|
Path(program_files) / 'Microsoft Visual Studio' / version / edition /
|
|
'VC/Tools/Llvm/x64/bin/llvm-profdata.exe'
|
|
for version in ('2026', '2022')
|
|
for edition in ('Enterprise', 'Community', 'Professional', 'BuildTools'))
|
|
tool = next((str(path) for path in candidates if path.is_file()), None)
|
|
if tool is None:
|
|
tool = shutil.which('llvm-profdata')
|
|
if not tool:
|
|
raise ValueError('llvm-profdata not found. Set LLVM_PROFDATA to the tool '
|
|
'from your Clang toolchain.')
|
|
return tool
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--duration', type=int, default=15,
|
|
help='seconds per workload (default: 15)')
|
|
args = parser.parse_args()
|
|
if args.duration <= 0:
|
|
parser.error('--duration must be a positive integer')
|
|
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
if sys.platform == 'win32':
|
|
node = repo_root / 'Release/node.exe'
|
|
build = 'vcbuild.bat pgo-generate'
|
|
rebuild = 'vcbuild.bat pgo-use'
|
|
else:
|
|
node = repo_root / 'out/Release/node'
|
|
build = './configure --ninja --enable-pgo-generate && make'
|
|
rebuild = './configure --ninja --enable-pgo-use && make'
|
|
if not node.is_file():
|
|
raise ValueError(f'Instrumented binary not found: {node}\nBuild with: {build}')
|
|
llvm_profdata = find_llvm_profdata()
|
|
|
|
profile_dir = Path(tempfile.mkdtemp(prefix='pgo-profiles.', dir=repo_root))
|
|
env = os.environ.copy()
|
|
env['LLVM_PROFILE_FILE'] = str(profile_dir / 'node-%m-%p.profraw')
|
|
|
|
print(f'Training {node} for {args.duration}s per workload', flush=True)
|
|
print(f'Profile directory: {profile_dir}', flush=True)
|
|
try:
|
|
subprocess.run([str(node), str(repo_root / 'tools/pgo/pgo-run-all.js'),
|
|
f'--duration={args.duration}', '--verbose'],
|
|
cwd=repo_root, env=env, check=True)
|
|
profiles = list(profile_dir.glob('*.profraw'))
|
|
if not profiles:
|
|
raise ValueError(f'No .profraw files found in {profile_dir}. '
|
|
'Build Node with PGO instrumentation using Clang.')
|
|
|
|
print(f'Merging {len(profiles)} profiles with {llvm_profdata}', flush=True)
|
|
merged = profile_dir / 'node.profdata'
|
|
subprocess.run([llvm_profdata, 'merge', '-o', str(merged),
|
|
*map(str, profiles)], check=True)
|
|
merged.replace(repo_root / 'node.profdata')
|
|
except (OSError, ValueError, subprocess.CalledProcessError):
|
|
print(f'PGO failed. Collected profiles are in: {profile_dir}', file=sys.stderr)
|
|
raise
|
|
shutil.rmtree(profile_dir)
|
|
print(f'Profile data: {repo_root / "node.profdata"}')
|
|
print(f'Next step: {rebuild}')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|