Index: experimental/benchtools/rebase.py |
diff --git a/experimental/benchtools/rebase.py b/experimental/benchtools/rebase.py |
deleted file mode 100755 |
index f542454ccf014c02caa86cf2e9bc31d73c9d207c..0000000000000000000000000000000000000000 |
--- a/experimental/benchtools/rebase.py |
+++ /dev/null |
@@ -1,328 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright (c) 2014 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
- |
-"""rebase.py: standalone script to batch update bench expectations. |
- |
- Requires gsutil to access gs://chromium-skia-gm and Rietveld credentials. |
- |
- Usage: |
- Copy script to a separate dir outside Skia repo. The script will create a |
- skia dir on the first run to host the repo, and will create/delete |
- temp dirs as needed. |
- ./rebase.py --githash <githash prefix to use for getting bench data> |
-""" |
- |
- |
-import argparse |
-import filecmp |
-import os |
-import re |
-import shutil |
-import subprocess |
-import time |
-import urllib2 |
- |
- |
-# googlesource url that has most recent Skia git hash info. |
-SKIA_GIT_HEAD_URL = 'https://skia.googlesource.com/skia/+log/HEAD' |
- |
-# Google Storage bench file prefix. |
-GS_PREFIX = 'gs://chromium-skia-gm/perfdata' |
- |
-# Regular expression for matching githash data. |
-HA_RE = '<a href="/skia/\+/([0-9a-f]+)">' |
-HA_RE_COMPILED = re.compile(HA_RE) |
- |
- |
-def get_git_hashes(): |
- print 'Getting recent git hashes...' |
- hashes = HA_RE_COMPILED.findall( |
- urllib2.urlopen(SKIA_GIT_HEAD_URL).read()) |
- |
- return hashes |
- |
-def filter_file(f): |
- if f.find('_msaa') > 0 or f.find('_record') > 0: |
- return True |
- |
- return False |
- |
-def clean_dir(d): |
- if os.path.exists(d): |
- shutil.rmtree(d) |
- os.makedirs(d) |
- |
-def get_gs_filelist(p, h): |
- print 'Looking up for the closest bench files in Google Storage...' |
- proc = subprocess.Popen(['gsutil', 'ls', |
- '/'.join([GS_PREFIX, p, 'bench_' + h + '_data_skp_*'])], |
- stdout=subprocess.PIPE) |
- out, err = proc.communicate() |
- if err or not out: |
- return [] |
- return [i for i in out.strip().split('\n') if not filter_file(i)] |
- |
-def download_gs_files(p, h, gs_dir): |
- print 'Downloading raw bench files from Google Storage...' |
- proc = subprocess.Popen(['gsutil', 'cp', |
- '/'.join([GS_PREFIX, p, 'bench_' + h + '_data_skp_*']), |
- '%s/%s' % (gs_dir, p)], |
- stdout=subprocess.PIPE) |
- out, err = proc.communicate() |
- if err: |
- clean_dir(gs_dir) |
- return False |
- files = 0 |
- for f in os.listdir(os.path.join(gs_dir, p)): |
- if filter_file(f): |
- os.remove(os.path.join(gs_dir, p, f)) |
- else: |
- files += 1 |
- if files: |
- return True |
- return False |
- |
-def get_expectations_dict(f): |
- """Given an expectations file f, returns a dictionary of data.""" |
- # maps row_key to (expected, lower_bound, upper_bound) float tuple. |
- dic = {} |
- for l in open(f).readlines(): |
- line_parts = l.strip().split(',') |
- if line_parts[0].startswith('#') or len(line_parts) != 5: |
- continue |
- dic[','.join(line_parts[:2])] = (float(line_parts[2]), float(line_parts[3]), |
- float(line_parts[4])) |
- |
- return dic |
- |
-def calc_expectations(p, h, gs_dir, exp_dir, repo_dir, extra_dir, extra_hash): |
- exp_filename = 'bench_expectations_%s.txt' % p |
- exp_fullname = os.path.join(exp_dir, exp_filename) |
- proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py', |
- '-r', h, '-b', p, '-d', os.path.join(gs_dir, p), '-o', exp_fullname], |
- stdout=subprocess.PIPE) |
- out, err = proc.communicate() |
- if err: |
- print 'ERR_CALCULATING_EXPECTATIONS: ' + err |
- return False |
- print 'CALCULATED_EXPECTATIONS: ' + out |
- if extra_dir: # Adjust data with the ones in extra_dir |
- print 'USE_EXTRA_DATA_FOR_ADJUSTMENT.' |
- proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py', |
- '-r', extra_hash, '-b', p, '-d', os.path.join(extra_dir, p), '-o', |
- os.path.join(extra_dir, exp_filename)], |
- stdout=subprocess.PIPE) |
- out, err = proc.communicate() |
- if err: |
- print 'ERR_CALCULATING_EXTRA_EXPECTATIONS: ' + err |
- return False |
- extra_dic = get_expectations_dict(os.path.join(extra_dir, exp_filename)) |
- output_lines = [] |
- for l in open(exp_fullname).readlines(): |
- parts = l.strip().split(',') |
- if parts[0].startswith('#') or len(parts) != 5: |
- output_lines.append(l.strip()) |
- continue |
- key = ','.join(parts[:2]) |
- if key in extra_dic: |
- exp, lb, ub = (float(parts[2]), float(parts[3]), float(parts[4])) |
- alt, _, _ = extra_dic[key] |
- avg = (exp + alt) / 2 |
- # Keeps the extra range in lower/upper bounds from two actual values. |
- new_lb = min(exp, alt) - (exp - lb) |
- new_ub = max(exp, alt) + (ub - exp) |
- output_lines.append('%s,%.2f,%.2f,%.2f' % (key, avg, new_lb, new_ub)) |
- else: |
- output_lines.append(l.strip()) |
- with open(exp_fullname, 'w') as f: |
- f.write('\n'.join(output_lines)) |
- |
- repo_file = os.path.join(repo_dir, 'expectations', 'bench', exp_filename) |
- if (os.path.isfile(repo_file) and |
- filecmp.cmp(repo_file, os.path.join(exp_dir, exp_filename))): |
- print 'NO CHANGE ON %s' % repo_file |
- return False |
- return True |
- |
-def checkout_or_update_skia(repo_dir): |
- status = True |
- old_cwd = os.getcwd() |
- os.chdir(repo_dir) |
- print 'CHECK SKIA REPO...' |
- if subprocess.call(['git', 'pull'], |
- stderr=subprocess.PIPE): |
- print 'Checking out Skia from git, please be patient...' |
- os.chdir(old_cwd) |
- clean_dir(repo_dir) |
- os.chdir(repo_dir) |
- if subprocess.call(['git', 'clone', '-q', '--depth=50', '--single-branch', |
- 'https://skia.googlesource.com/skia.git', '.']): |
- status = False |
- subprocess.call(['git', 'checkout', 'master']) |
- subprocess.call(['git', 'pull']) |
- os.chdir(old_cwd) |
- return status |
- |
-def git_commit_expectations(repo_dir, exp_dir, update_li, h, commit, |
- extra_hash): |
- if extra_hash: |
- extra_hash = ', adjusted with ' + extra_hash |
- commit_msg = """manual bench rebase after %s%s |
- |
-TBR=robertphillips@google.com |
- |
-Bypassing trybots: |
-NOTRY=true""" % (h, extra_hash) |
- old_cwd = os.getcwd() |
- os.chdir(repo_dir) |
- upload = ['git', 'cl', 'upload', '-f', '--bypass-hooks', |
- '--bypass-watchlists', '-m', commit_msg] |
- branch = exp_dir.split('/')[-1] |
- if commit: |
- upload.append('--use-commit-queue') |
- cmds = ([['git', 'checkout', 'master'], |
- ['git', 'pull'], |
- ['git', 'checkout', '-b', branch, '-t', 'origin/master']] + |
- [['cp', '%s/%s' % (exp_dir, f), 'expectations/bench'] for f in |
- update_li] + |
- [['git', 'add'] + ['expectations/bench/%s' % i for i in update_li], |
- ['git', 'commit', '-m', commit_msg], |
- upload, |
- ['git', 'checkout', 'master'], |
- ['git', 'branch', '-D', branch], |
- ]) |
- status = True |
- for cmd in cmds: |
- print 'Running ' + ' '.join(cmd) |
- if subprocess.call(cmd): |
- print 'FAILED. Please check if skia git repo is present.' |
- subprocess.call(['git', 'checkout', 'master']) |
- status = False |
- break |
- os.chdir(old_cwd) |
- return status |
- |
-def delete_dirs(li): |
- for d in li: |
- print 'Deleting directory %s' % d |
- shutil.rmtree(d) |
- |
- |
-def main(): |
- d = os.path.dirname(os.path.abspath(__file__)) |
- os.chdir(d) |
- if not subprocess.call(['git', 'rev-parse'], stderr=subprocess.PIPE): |
- print 'Please copy script to a separate dir outside git repos to use.' |
- return |
- parser = argparse.ArgumentParser() |
- parser.add_argument('--githash', |
- help=('Githash prefix (7+ chars) to rebaseline to. If ' |
- 'a second one is supplied after comma, and it has ' |
- 'corresponding bench data, will shift the range ' |
- 'center to the average of two expected values.')) |
- parser.add_argument('--bots', |
- help=('Comma-separated list of bots to work on. If no ' |
- 'matching bots are found in the list, will default ' |
- 'to processing all bots.')) |
- parser.add_argument('--commit', action='store_true', |
- help='Whether to commit changes automatically.') |
- args = parser.parse_args() |
- |
- repo_dir = os.path.join(d, 'skia') |
- if not os.path.exists(repo_dir): |
- os.makedirs(repo_dir) |
- if not checkout_or_update_skia(repo_dir): |
- print 'ERROR setting up Skia repo at %s' % repo_dir |
- return 1 |
- |
- file_in_repo = os.path.join(d, 'skia/experimental/benchtools/rebase.py') |
- if not filecmp.cmp(__file__, file_in_repo): |
- shutil.copy(file_in_repo, __file__) |
- print 'Updated this script from repo; please run again.' |
- return |
- |
- all_platforms = [] # Find existing list of platforms with expectations. |
- for item in os.listdir(os.path.join(d, 'skia/expectations/bench')): |
- all_platforms.append( |
- item.replace('bench_expectations_', '').replace('.txt', '')) |
- |
- platforms = [] |
- # If at least one given bot is in all_platforms, use list of valid args.bots. |
- if args.bots: |
- bots = args.bots.strip().split(',') |
- for bot in bots: |
- if bot in all_platforms: # Filters platforms with given bot list. |
- platforms.append(bot) |
- if not platforms: # Include all existing platforms with expectations. |
- platforms = all_platforms |
- |
- if not args.githash or len(args.githash) < 7: |
- raise Exception('Please provide --githash with a longer prefix (7+).') |
- githashes = args.githash.strip().split(',') |
- if len(githashes[0]) < 7: |
- raise Exception('Please provide --githash with longer prefixes (7+).') |
- commit = False |
- if args.commit: |
- commit = True |
- rebase_hash = githashes[0][:7] |
- extra_hash = '' |
- if len(githashes) == 2: |
- extra_hash = githashes[1][:7] |
- hashes = get_git_hashes() |
- short_hashes = [h[:7] for h in hashes] |
- if (rebase_hash not in short_hashes or |
- (extra_hash and extra_hash not in short_hashes) or |
- rebase_hash == extra_hash): |
- raise Exception('Provided --githashes not found, or identical!') |
- if extra_hash: |
- extra_hash = hashes[short_hashes.index(extra_hash)] |
- hashes = hashes[:short_hashes.index(rebase_hash) + 1] |
- update_li = [] |
- |
- ts_str = '%s' % time.time() |
- gs_dir = os.path.join(d, 'gs' + ts_str) |
- exp_dir = os.path.join(d, 'exp' + ts_str) |
- extra_dir = os.path.join(d, 'extra' + ts_str) |
- clean_dir(gs_dir) |
- clean_dir(exp_dir) |
- clean_dir(extra_dir) |
- for p in platforms: |
- clean_dir(os.path.join(gs_dir, p)) |
- clean_dir(os.path.join(extra_dir, p)) |
- hash_to_use = '' |
- for h in reversed(hashes): |
- li = get_gs_filelist(p, h) |
- if not len(li): # no data |
- continue |
- if download_gs_files(p, h, gs_dir): |
- print 'Copied %s/%s' % (p, h) |
- hash_to_use = h |
- break |
- else: |
- print 'DOWNLOAD BENCH FAILED %s/%s' % (p, h) |
- break |
- if hash_to_use: |
- if extra_hash and download_gs_files(p, extra_hash, extra_dir): |
- print 'Copied extra data %s/%s' % (p, extra_hash) |
- if calc_expectations(p, h, gs_dir, exp_dir, repo_dir, extra_dir, |
- extra_hash): |
- update_li.append('bench_expectations_%s.txt' % p) |
- elif calc_expectations(p, h, gs_dir, exp_dir, repo_dir, '', ''): |
- update_li.append('bench_expectations_%s.txt' % p) |
- if not update_li: |
- print 'No bench data to update after %s!' % args.githash |
- elif not git_commit_expectations( |
- repo_dir, exp_dir, update_li, rebase_hash, commit, extra_hash): |
- print 'ERROR uploading expectations using git.' |
- elif not commit: |
- print 'CL created. Please take a look at the link above.' |
- else: |
- print 'New bench baselines should be in CQ now.' |
- delete_dirs([gs_dir, exp_dir, extra_dir]) |
- |
- |
-if __name__ == "__main__": |
- main() |