blob: 5eee51630dcc1f7b92ebde8abcffd94e067a9337 [file] [log] [blame]
commit-bot@chromium.org336df8f2014-05-21 22:57:44 +00001#!/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.org5ddea762014-05-21 21:12:11 +00006
7"""rebase.py: standalone script to batch update bench expectations.
commit-bot@chromium.org336df8f2014-05-21 22:57:44 +00008
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.org5ddea762014-05-21 21:12:11 +000016"""
17
commit-bot@chromium.org336df8f2014-05-21 22:57:44 +000018
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +000019import argparse
20import filecmp
21import os
22import re
23import shutil
24import subprocess
25import time
26import urllib2
27
28
29# googlesource url that has most recent Skia git hash info.
30SKIA_GIT_HEAD_URL = 'https://skia.googlesource.com/skia/+log/HEAD'
31
32# Google Storage bench file prefix.
33GS_PREFIX = 'gs://chromium-skia-gm/perfdata'
34
35# List of Perf platforms we want to process. Populate from expectations/bench.
36PLATFORMS = []
37
38# Regular expression for matching githash data.
39HA_RE = '<a href="/skia/\+/([0-9a-f]+)">'
40HA_RE_COMPILED = re.compile(HA_RE)
41
42
43def 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
50def filter_file(f):
51 if f.find('_msaa') > 0 or f.find('_record') > 0:
52 return True
53
54 return False
55
56def clean_dir(d):
57 if os.path.exists(d):
58 shutil.rmtree(d)
59 os.makedirs(d)
60
61def 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
71def download_gs_files(p, h, gs_dir):
72 print 'Downloading raw bench files from Google Storage...'
commit-bot@chromium.org336df8f2014-05-21 22:57:44 +000073 proc = subprocess.Popen(['gsutil', 'cp', '-q',
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +000074 '/'.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
91def 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
109def 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
128def git_commit_expectations(repo_dir, exp_dir, update_li, h, commit):
commit-bot@chromium.org77767352014-05-22 12:00:55 +0000129 commit_msg = """manual bench rebase after %s
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000130
commit-bot@chromium.org77767352014-05-22 12:00:55 +0000131TBR=robertphillips@google.com
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000132
commit-bot@chromium.org77767352014-05-22 12:00:55 +0000133Bypassing trybots:
134NOTRY=true""" % h
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000135 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.org336df8f2014-05-21 22:57:44 +0000139 branch = exp_dir.split('/')[-1]
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000140 if commit:
141 upload.append('--use-commit-queue')
142 cmds = ([['git', 'checkout', 'master'],
143 ['git', 'pull'],
commit-bot@chromium.org336df8f2014-05-21 22:57:44 +0000144 ['git', 'checkout', '-b', branch, '-t', 'origin/master']] +
145 [['cp', '%s/%s' % (exp_dir, f), 'expectations/bench'] for f in
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000146 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.org336df8f2014-05-21 22:57:44 +0000151 ['git', 'branch', '-D', branch],
commit-bot@chromium.org5ddea762014-05-21 21:12:11 +0000152 ])
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
164def delete_dirs(li):
165 for d in li:
166 print 'Deleting directory %s' % d
167 shutil.rmtree(d)
168
169
170def 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.org5ddea762014-05-21 21:12:11 +0000190 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.org336df8f2014-05-21 22:57:44 +0000196 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.org5ddea762014-05-21 21:12:11 +0000200 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
247if __name__ == "__main__":
248 main()