#!/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. """Download profdata from different arches, merge them and upload to gs.""" from __future__ import print_function import argparse import datetime import distutils.spawn import os import os.path import shutil import subprocess import sys import tempfile _LLVM_PROFDATA = '/usr/bin/llvm-profdata' _GS_PREFIX = 'gs://' def _get_gs_latest(remote_lastest): assert remote_lastest.startswith(_GS_PREFIX) try: return subprocess.check_output(['gsutil', 'cat', remote_lastest]) except subprocess.CalledProcessError: raise RuntimeError('Lastest artifacts not found: %s' % remote_lastest) def _fetch_gs_artifact(remote_name, local_name): assert remote_name.startswith(_GS_PREFIX) subprocess.check_call(['gsutil', 'cp', remote_name, local_name]) def _find_latest_profdata(arch): remote_latest = ( '%schromeos-image-archive/' '%s-pgo-generate-llvm-next-toolchain/LATEST-master' % (_GS_PREFIX, arch)) version = _get_gs_latest(remote_latest) profdata = ('%s-pgo-generate-llvm-next-toolchain/%s/llvm_profdata.tar.xz' % (arch, version)) return profdata def _get_gs_profdata(profdata): remote_profdata = ('%schromeos-image-archive/%s' % (_GS_PREFIX, profdata)) tar = 'llvm_profdata.tar.xz' print('Downloading single profdata for: %s' % profdata) _fetch_gs_artifact(remote_profdata, tar) extract_cmd = ['tar', '-xf', tar] print('Extracting profdata tarball.\nCMD: %s\n' % extract_cmd) subprocess.check_call(extract_cmd) profdata = profdata.replace('llvm_profdata.tar.xz', 'llvm.profdata') # Return directory to the llvm.profdata extracted. return 'b/s/w/ir/cache/cbuild/repository/buildbot_archive/%s' % profdata def _merge_profdata(profdata_list, output_name): merge_cmd = [_LLVM_PROFDATA, 'merge', '-output', output_name] + profdata_list print('Merging PGO profiles.\nCMD: %s\n' % merge_cmd) subprocess.check_call(merge_cmd) def _tar_and_upload_profdata(profdata): timestamp = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d') tarball = 'llvm-profdata-%s.tar.xz' % timestamp print('Making profdata tarball: %s' % tarball) subprocess.check_call( ['tar', '--sparse', '-I', 'xz', '-cf', tarball, profdata]) # TODO: it's better to create a subdir: distfiles/llvm_pgo_profile, but # now llvm could only recognize distfiles. upload_cmd = [ 'gsutil', '-m', 'cp', '-n', '-a', 'public-read', tarball, '%schromeos-localmirror/distfiles/%s' % (_GS_PREFIX, tarball) ] print('Uploading tarball to gs.\nCMD: %s\n' % upload_cmd) subprocess.check_call(upload_cmd) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '-l', '--latest', default=[], action='append', help='User can specify the profdata from which architectures to ' 'download. By default, we merge profdata from arm, arm64, amd64.') parser.add_argument( '-t', '--tryjob', default=[], action='append', help='Extra pgo-generate-llvm-next-toolchain/tryjob results to be used. ' 'Format should be ' '{arch}-pgo-generate-llvm-next-toolchain(-tryjob)/{VERSION}.') parser.add_argument( '-o', '--output', default='llvm.profdata', help='Where to put merged PGO profile. The default is to not save it ' 'anywhere.') args = parser.parse_args() # If no --latest specified, by default we collect from listed arches. latest = ['arm', 'arm64', 'amd64'] if not args.latest else args.latest if not distutils.spawn.find_executable(_LLVM_PROFDATA): sys.exit(_LLVM_PROFDATA + ' not found; are you in the chroot?') initial_dir = os.getcwd() temp_dir = tempfile.mkdtemp(prefix='merge_pgo') success = True try: os.chdir(temp_dir) profdata_list = [] for arch in latest: profdata = _find_latest_profdata(arch) profdata_loc = _get_gs_profdata(profdata) profdata_list.append(profdata_loc) if args.tryjob: for tryjob in args.tryjob: profdata = os.path.join(tryjob, 'llvm_profdata.tar.xz') profdata_loc = _get_gs_profdata(profdata) profdata_list.append(profdata_loc) for profdata in profdata_list: if os.path.getsize(profdata_loc) < 512 * 1024: raise RuntimeError('The PGO profile in %s is suspiciously small. ' 'Something might have gone wrong.' % profdata) _merge_profdata(profdata_list, args.output) print('Merged profdata locates at %s\n' % os.path.abspath(args.output)) _tar_and_upload_profdata(args.output) print('Merged profdata uploaded successfully.') except: success = False raise finally: os.chdir(initial_dir) if success: print('Clearing temp directory.') shutil.rmtree(temp_dir, ignore_errors=True) else: print('Script fails, temp directory is at: %s' % temp_dir) if __name__ == '__main__': sys.exit(main())