commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 6 | |
| 7 | """rebase.py: standalone script to batch update bench expectations. |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 8 | |
| 9 | Requires gsutil to access gs://chromium-skia-gm and Rietveld credentials. |
| 10 | |
| 11 | Usage: |
| 12 | Copy script to a separate dir outside Skia repo. The script will create a |
| 13 | skia dir on the first run to host the repo, and will create/delete |
| 14 | temp dirs as needed. |
| 15 | ./rebase.py --githash <githash prefix to use for getting bench data> |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 16 | """ |
| 17 | |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 18 | |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 19 | import argparse |
| 20 | import filecmp |
| 21 | import os |
| 22 | import re |
| 23 | import shutil |
| 24 | import subprocess |
| 25 | import time |
| 26 | import urllib2 |
| 27 | |
| 28 | |
| 29 | # googlesource url that has most recent Skia git hash info. |
| 30 | SKIA_GIT_HEAD_URL = 'https://skia.googlesource.com/skia/+log/HEAD' |
| 31 | |
| 32 | # Google Storage bench file prefix. |
| 33 | GS_PREFIX = 'gs://chromium-skia-gm/perfdata' |
| 34 | |
| 35 | # List of Perf platforms we want to process. Populate from expectations/bench. |
| 36 | PLATFORMS = [] |
| 37 | |
| 38 | # Regular expression for matching githash data. |
| 39 | HA_RE = '<a href="/skia/\+/([0-9a-f]+)">' |
| 40 | HA_RE_COMPILED = re.compile(HA_RE) |
| 41 | |
| 42 | |
| 43 | def get_git_hashes(): |
| 44 | print 'Getting recent git hashes...' |
| 45 | hashes = HA_RE_COMPILED.findall( |
| 46 | urllib2.urlopen(SKIA_GIT_HEAD_URL).read()) |
| 47 | |
| 48 | return hashes |
| 49 | |
| 50 | def filter_file(f): |
| 51 | if f.find('_msaa') > 0 or f.find('_record') > 0: |
| 52 | return True |
| 53 | |
| 54 | return False |
| 55 | |
| 56 | def clean_dir(d): |
| 57 | if os.path.exists(d): |
| 58 | shutil.rmtree(d) |
| 59 | os.makedirs(d) |
| 60 | |
| 61 | def get_gs_filelist(p, h): |
| 62 | print 'Looking up for the closest bench files in Google Storage...' |
| 63 | proc = subprocess.Popen(['gsutil', 'ls', |
| 64 | '/'.join([GS_PREFIX, p, 'bench_' + h + '_data_skp_*'])], |
| 65 | stdout=subprocess.PIPE) |
| 66 | out, err = proc.communicate() |
| 67 | if err or not out: |
| 68 | return [] |
| 69 | return [i for i in out.strip().split('\n') if not filter_file(i)] |
| 70 | |
| 71 | def download_gs_files(p, h, gs_dir): |
| 72 | print 'Downloading raw bench files from Google Storage...' |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 73 | proc = subprocess.Popen(['gsutil', 'cp', '-q', |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 74 | '/'.join([GS_PREFIX, p, 'bench_' + h + '_data_skp_*']), |
| 75 | '%s/%s' % (gs_dir, p)], |
| 76 | stdout=subprocess.PIPE) |
| 77 | out, err = proc.communicate() |
| 78 | if err: |
| 79 | clean_dir(gs_dir) |
| 80 | return False |
| 81 | files = 0 |
| 82 | for f in os.listdir(os.path.join(gs_dir, p)): |
| 83 | if filter_file(f): |
| 84 | os.remove(os.path.join(gs_dir, p, f)) |
| 85 | else: |
| 86 | files += 1 |
| 87 | if files == 4: |
| 88 | return True |
| 89 | return False |
| 90 | |
| 91 | def calc_expectations(p, h, gs_dir, exp_dir, repo_dir): |
| 92 | exp_filename = 'bench_expectations_%s.txt' % p |
| 93 | proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py', |
| 94 | '-r', h, '-b', p, '-d', os.path.join(gs_dir, p), '-o', |
| 95 | os.path.join(exp_dir, exp_filename)], |
| 96 | stdout=subprocess.PIPE) |
| 97 | out, err = proc.communicate() |
| 98 | if err: |
| 99 | print 'ERR_CALCULATING_EXPECTATIONS: ' + err |
| 100 | return False |
| 101 | print 'CALCULATED_EXPECTATIONS: ' + out |
| 102 | repo_file = os.path.join(repo_dir, 'expectations', 'bench', exp_filename) |
| 103 | if (os.path.isfile(repo_file) and |
| 104 | filecmp.cmp(repo_file, os.path.join(exp_dir, exp_filename))): |
| 105 | print 'NO CHANGE ON %s' % repo_file |
| 106 | return False |
| 107 | return True |
| 108 | |
| 109 | def checkout_or_update_skia(repo_dir): |
| 110 | status = True |
| 111 | old_cwd = os.getcwd() |
| 112 | os.chdir(repo_dir) |
| 113 | print 'CHECK SKIA REPO...' |
| 114 | if subprocess.call(['git', 'pull'], |
| 115 | stderr=subprocess.PIPE): |
| 116 | print 'Checking out Skia from git, please be patient...' |
| 117 | os.chdir(old_cwd) |
| 118 | clean_dir(repo_dir) |
| 119 | os.chdir(repo_dir) |
| 120 | if subprocess.call(['git', 'clone', '-q', '--depth=50', '--single-branch', |
| 121 | 'https://skia.googlesource.com/skia.git', '.']): |
| 122 | status = False |
| 123 | subprocess.call(['git', 'checkout', 'master']) |
| 124 | subprocess.call(['git', 'pull']) |
| 125 | os.chdir(old_cwd) |
| 126 | return status |
| 127 | |
| 128 | def git_commit_expectations(repo_dir, exp_dir, update_li, h, commit): |
commit-bot@chromium.org | 7776735 | 2014-05-22 12:00:55 +0000 | [diff] [blame^] | 129 | commit_msg = """manual bench rebase after %s |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 130 | |
commit-bot@chromium.org | 7776735 | 2014-05-22 12:00:55 +0000 | [diff] [blame^] | 131 | TBR=robertphillips@google.com |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 132 | |
commit-bot@chromium.org | 7776735 | 2014-05-22 12:00:55 +0000 | [diff] [blame^] | 133 | Bypassing trybots: |
| 134 | NOTRY=true""" % h |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 135 | old_cwd = os.getcwd() |
| 136 | os.chdir(repo_dir) |
| 137 | upload = ['git', 'cl', 'upload', '-f', '--bypass-hooks', |
| 138 | '--bypass-watchlists', '-m', commit_msg] |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 139 | branch = exp_dir.split('/')[-1] |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 140 | if commit: |
| 141 | upload.append('--use-commit-queue') |
| 142 | cmds = ([['git', 'checkout', 'master'], |
| 143 | ['git', 'pull'], |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 144 | ['git', 'checkout', '-b', branch, '-t', 'origin/master']] + |
| 145 | [['cp', '%s/%s' % (exp_dir, f), 'expectations/bench'] for f in |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 146 | update_li] + |
| 147 | [['git', 'add'] + ['expectations/bench/%s' % i for i in update_li], |
| 148 | ['git', 'commit', '-m', commit_msg], |
| 149 | upload, |
| 150 | ['git', 'checkout', 'master'], |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 151 | ['git', 'branch', '-D', branch], |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 152 | ]) |
| 153 | status = True |
| 154 | for cmd in cmds: |
| 155 | print 'Running ' + ' '.join(cmd) |
| 156 | if subprocess.call(cmd): |
| 157 | print 'FAILED. Please check if skia git repo is present.' |
| 158 | subprocess.call(['git', 'checkout', 'master']) |
| 159 | status = False |
| 160 | break |
| 161 | os.chdir(old_cwd) |
| 162 | return status |
| 163 | |
| 164 | def delete_dirs(li): |
| 165 | for d in li: |
| 166 | print 'Deleting directory %s' % d |
| 167 | shutil.rmtree(d) |
| 168 | |
| 169 | |
| 170 | def main(): |
| 171 | d = os.path.dirname(os.path.abspath(__file__)) |
| 172 | os.chdir(d) |
| 173 | if not subprocess.call(['git', 'rev-parse'], stderr=subprocess.PIPE): |
| 174 | print 'Please copy script to a separate dir outside git repos to use.' |
| 175 | return |
| 176 | parser = argparse.ArgumentParser() |
| 177 | parser.add_argument('--githash', |
| 178 | help='Githash prefix (7+ chars) to rebaseline to.') |
| 179 | parser.add_argument('--commit', action='store_true', |
| 180 | help='Whether to commit changes automatically.') |
| 181 | args = parser.parse_args() |
| 182 | |
| 183 | repo_dir = os.path.join(d, 'skia') |
| 184 | if not os.path.exists(repo_dir): |
| 185 | os.makedirs(repo_dir) |
| 186 | if not checkout_or_update_skia(repo_dir): |
| 187 | print 'ERROR setting up Skia repo at %s' % repo_dir |
| 188 | return 1 |
| 189 | |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 190 | file_in_repo = os.path.join(d, 'skia/experimental/benchtools/rebase.py') |
| 191 | if not filecmp.cmp(__file__, file_in_repo): |
| 192 | shutil.copy(file_in_repo, __file__) |
| 193 | print 'Updated this script from repo; please run again.' |
| 194 | return |
| 195 | |
commit-bot@chromium.org | 336df8f | 2014-05-21 22:57:44 +0000 | [diff] [blame] | 196 | for item in os.listdir(os.path.join(d, 'skia/expectations/bench')): |
| 197 | PLATFORMS.append( |
| 198 | item.replace('bench_expectations_', '').replace('.txt', '')) |
| 199 | |
commit-bot@chromium.org | 5ddea76 | 2014-05-21 21:12:11 +0000 | [diff] [blame] | 200 | if not args.githash or len(args.githash) < 7: |
| 201 | raise Exception('Please provide --githash with a longer prefix (7+).') |
| 202 | commit = False |
| 203 | if args.commit: |
| 204 | commit = True |
| 205 | rebase_hash = args.githash[:7] |
| 206 | hashes = get_git_hashes() |
| 207 | short_hashes = [h[:7] for h in hashes] |
| 208 | if rebase_hash not in short_hashes: |
| 209 | raise Exception('Provided --githash not found in recent history!') |
| 210 | hashes = hashes[:short_hashes.index(rebase_hash) + 1] |
| 211 | update_li = [] |
| 212 | |
| 213 | ts_str = '%s' % time.time() |
| 214 | gs_dir = os.path.join(d, 'gs' + ts_str) |
| 215 | exp_dir = os.path.join(d, 'exp' + ts_str) |
| 216 | clean_dir(gs_dir) |
| 217 | clean_dir(exp_dir) |
| 218 | for p in PLATFORMS: |
| 219 | clean_dir(os.path.join(gs_dir, p)) |
| 220 | hash_to_use = '' |
| 221 | for h in reversed(hashes): |
| 222 | li = get_gs_filelist(p, h) |
| 223 | if len(li) != 4: # no or partial data |
| 224 | continue |
| 225 | if download_gs_files(p, h, gs_dir): |
| 226 | print 'Copied %s/%s' % (p, h) |
| 227 | hash_to_use = h |
| 228 | break |
| 229 | else: |
| 230 | print 'DOWNLOAD BENCH FAILED %s/%s' % (p, h) |
| 231 | break |
| 232 | if hash_to_use: |
| 233 | if calc_expectations(p, h, gs_dir, exp_dir, repo_dir): |
| 234 | update_li.append('bench_expectations_%s.txt' % p) |
| 235 | if not update_li: |
| 236 | print 'No bench data to update after %s!' % args.githash |
| 237 | elif not git_commit_expectations( |
| 238 | repo_dir, exp_dir, update_li, args.githash[:7], commit): |
| 239 | print 'ERROR uploading expectations using git.' |
| 240 | elif not commit: |
| 241 | print 'CL created. Please take a look at the link above.' |
| 242 | else: |
| 243 | print 'New bench baselines should be in CQ now.' |
| 244 | delete_dirs([gs_dir, exp_dir]) |
| 245 | |
| 246 | |
| 247 | if __name__ == "__main__": |
| 248 | main() |