Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2019 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | # |
| 17 | # Sample Usage: |
Pirama Arumuga Nainar | f5db777 | 2019-11-04 21:19:59 -0800 | [diff] [blame] | 18 | # $ python update_profiles.py 500000 ALL --profdata-suffix 2019-04-15 |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 19 | # |
| 20 | # Additional/frequently-used arguments: |
| 21 | # -b BUG adds a 'Bug: <BUG>' to the commit message when adding the profiles. |
| 22 | # --do-not-merge adds a 'DO NOT MERGE' tag to the commit message to restrict |
| 23 | # automerge of profiles from release branches. |
| 24 | # |
| 25 | # Try '-h' for a full list of command line arguments. |
| 26 | |
| 27 | import argparse |
| 28 | import os |
| 29 | import shutil |
| 30 | import subprocess |
| 31 | import sys |
| 32 | import tempfile |
| 33 | import zipfile |
| 34 | |
| 35 | import utils |
| 36 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 37 | from android_build_client import AndroidBuildClient |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 38 | |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 39 | |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 40 | class Benchmark(object): |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 41 | |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 42 | def __init__(self, name): |
| 43 | self.name = name |
| 44 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 45 | def apct_test_tag(self): |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 46 | raise NotImplementedError() |
| 47 | |
| 48 | def profdata_file(self, suffix=''): |
| 49 | profdata = os.path.join(self.name, '{}.profdata'.format(self.name)) |
| 50 | if suffix: |
| 51 | profdata += '.' + suffix |
| 52 | return profdata |
| 53 | |
| 54 | def profraw_files(self): |
| 55 | raise NotImplementedError() |
| 56 | |
| 57 | def merge_profraws(self, profile_dir, output): |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 58 | profraws = [ |
| 59 | os.path.join(profile_dir, p) |
| 60 | for p in self.profraw_files(profile_dir) |
| 61 | ] |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 62 | utils.run_llvm_profdata(profraws, output) |
| 63 | |
| 64 | |
| 65 | class NativeExeBenchmark(Benchmark): |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 66 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 67 | def apct_test_tag(self): |
| 68 | return 'apct/perf/pgo/profile-collector' |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 69 | |
| 70 | def profraw_files(self, profile_dir): |
| 71 | if self.name == 'hwui': |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 72 | return [ |
| 73 | 'hwuimacro.profraw', 'hwuimacro_64.profraw', |
| 74 | 'hwuimicro.profraw', 'hwuimicro_64.profraw', |
| 75 | 'skia_nanobench.profraw', 'skia_nanobench_64.profraw' |
| 76 | ] |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 77 | elif self.name == 'hwbinder': |
| 78 | return ['hwbinder.profraw', 'hwbinder_64.profraw'] |
| 79 | |
| 80 | |
| 81 | class APKBenchmark(Benchmark): |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 82 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 83 | def apct_test_tag(self): |
| 84 | return 'apct/perf/pgo/apk-profile-collector' |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 85 | |
| 86 | def profdata_file(self, suffix=''): |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 87 | profdata = os.path.join('art', |
| 88 | '{}_arm_arm64.profdata'.format(self.name)) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 89 | if suffix: |
| 90 | profdata += '.' + suffix |
| 91 | return profdata |
| 92 | |
| 93 | def profraw_files(self, profile_dir): |
| 94 | return os.listdir(profile_dir) |
| 95 | |
| 96 | |
| 97 | def BenchmarkFactory(benchmark_name): |
| 98 | if benchmark_name == 'dex2oat': |
| 99 | return APKBenchmark(benchmark_name) |
| 100 | elif benchmark_name in ['hwui', 'hwbinder']: |
| 101 | return NativeExeBenchmark(benchmark_name) |
| 102 | else: |
| 103 | raise RuntimeError('Unknown benchmark ' + benchmark_name) |
| 104 | |
| 105 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 106 | def extract_profiles(build, test_tag, build_client, output_dir): |
| 107 | pgo_zip = build_client.download_pgo_zip(build, test_tag, output_dir) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 108 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 109 | zipfile_name = os.path.join(pgo_zip) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 110 | zip_ref = zipfile.ZipFile(zipfile_name) |
| 111 | zip_ref.extractall(output_dir) |
| 112 | zip_ref.close() |
| 113 | |
| 114 | |
| 115 | KNOWN_BENCHMARKS = ['ALL', 'dex2oat', 'hwui', 'hwbinder'] |
| 116 | |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 117 | |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 118 | def parse_args(): |
| 119 | """Parses and returns command line arguments.""" |
| 120 | parser = argparse.ArgumentParser() |
| 121 | |
| 122 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 123 | 'build', |
| 124 | metavar='BUILD', |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 125 | help='Build number to pull from the build server.') |
| 126 | |
| 127 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 128 | '-b', '--bug', type=int, help='Bug to reference in commit message.') |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 129 | |
| 130 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 131 | '--use-current-branch', |
| 132 | action='store_true', |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 133 | help='Do not repo start a new branch for the update.') |
| 134 | |
| 135 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 136 | '--add-do-not-merge', |
| 137 | action='store_true', |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 138 | help='Add \'DO NOT MERGE\' to the commit message.') |
| 139 | |
| 140 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 141 | '--profdata-suffix', |
| 142 | type=str, |
| 143 | default='', |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 144 | help='Suffix to append to merged profdata file') |
| 145 | |
| 146 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 147 | 'benchmark', |
| 148 | metavar='BENCHMARK', |
| 149 | help='Update profiles for BENCHMARK. Choices are {}'.format( |
| 150 | KNOWN_BENCHMARKS)) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 151 | |
| 152 | parser.add_argument( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 153 | '--skip-cleanup', |
| 154 | '-sc', |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 155 | action='store_true', |
| 156 | default=False, |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 157 | help='Skip the cleanup, and leave intermediate files (in /tmp/pgo-profiles-*)' |
| 158 | ) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 159 | |
| 160 | return parser.parse_args() |
| 161 | |
| 162 | |
| 163 | def get_current_profile(benchmark): |
| 164 | profile = benchmark.profdata_file() |
| 165 | dirname, basename = os.path.split(profile) |
| 166 | |
| 167 | old_profiles = [f for f in os.listdir(dirname) if f.startswith(basename)] |
| 168 | if len(old_profiles) == 0: |
| 169 | return '' |
| 170 | return os.path.join(dirname, old_profiles[0]) |
| 171 | |
| 172 | |
| 173 | def main(): |
| 174 | args = parse_args() |
| 175 | |
| 176 | if args.benchmark == 'ALL': |
| 177 | worklist = KNOWN_BENCHMARKS[1:] |
| 178 | else: |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 179 | worklist = [args.benchmark] |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 180 | |
| 181 | profiles_project = os.path.join(utils.android_build_top(), 'toolchain', |
| 182 | 'pgo-profiles') |
| 183 | os.chdir(profiles_project) |
| 184 | |
| 185 | if not args.use_current_branch: |
| 186 | branch_name = 'update-profiles-' + args.build |
| 187 | utils.check_call(['repo', 'start', branch_name, '.']) |
| 188 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 189 | build_client = AndroidBuildClient() |
| 190 | |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 191 | for benchmark_name in worklist: |
| 192 | benchmark = BenchmarkFactory(benchmark_name) |
| 193 | |
| 194 | # Existing profile file, which gets 'rm'-ed from 'git' down below. |
| 195 | current_profile = get_current_profile(benchmark) |
| 196 | |
| 197 | # Extract profiles to a temporary directory. After extraction, we |
| 198 | # expect to find one subdirectory with profraw files under the temporary |
| 199 | # directory. |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 200 | extract_dir = tempfile.mkdtemp(prefix='pgo-profiles-' + benchmark_name) |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 201 | extract_profiles(args.build, benchmark.apct_test_tag(), build_client, |
| 202 | extract_dir) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 203 | |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 204 | extract_subdirs = [ |
| 205 | os.path.join(extract_dir, sub) |
| 206 | for sub in os.listdir(extract_dir) |
| 207 | if os.path.isdir(os.path.join(extract_dir, sub)) |
| 208 | ] |
| 209 | if len(extract_subdirs) != 1: |
| 210 | raise RuntimeError( |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 211 | 'Expected one subdir under {}'.format(extract_dir)) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 212 | |
| 213 | # Merge profiles. |
| 214 | profdata = benchmark.profdata_file(args.profdata_suffix) |
Pirama Arumuga Nainar | 19a588f | 2019-11-04 13:22:07 -0800 | [diff] [blame] | 215 | benchmark.merge_profraws(extract_subdirs[0], profdata) |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 216 | |
| 217 | # Construct 'git' commit message. |
| 218 | message_lines = [ |
Pirama Arumuga Nainar | 75e9732 | 2019-11-04 21:26:19 -0800 | [diff] [blame] | 219 | 'Update PGO profiles for {}'.format(benchmark_name), '', |
| 220 | 'The profiles are from build {}.'.format(args.build), '' |
Pirama Arumuga Nainar | 799180e | 2019-04-11 14:31:57 -0700 | [diff] [blame] | 221 | ] |
| 222 | |
| 223 | if args.add_do_not_merge: |
| 224 | message_lines[0] = '[DO NOT MERGE] ' + message_lines[0] |
| 225 | |
| 226 | if args.bug: |
| 227 | message_lines.append('') |
| 228 | message_lines.append('Bug: http://b/{}'.format(args.bug)) |
| 229 | message_lines.append('Test: Build (TH)') |
| 230 | message = '\n'.join(message_lines) |
| 231 | |
| 232 | # Invoke git: Delete current profile, add new profile and commit these |
| 233 | # changes. |
| 234 | if current_profile: |
| 235 | utils.check_call(['git', 'rm', current_profile]) |
| 236 | utils.check_call(['git', 'add', profdata]) |
| 237 | utils.check_call(['git', 'commit', '-m', message]) |
| 238 | |
| 239 | if not args.skip_cleanup: |
| 240 | shutil.rmtree(extract_dir) |
| 241 | |
| 242 | |
| 243 | if __name__ == '__main__': |
| 244 | main() |