1#!/usr/bin/env python 2# Copyright (c) 2013 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 6"""Run Performance Test Bisect Tool 7 8This script is used by a trybot to run the src/tools/bisect-perf-regression.py 9script with the parameters specified in run-bisect-perf-regression.cfg. It will 10check out a copy of the depot in a subdirectory 'bisect' of the working 11directory provided, and run the bisect-perf-regression.py script there. 12 13""" 14 15import imp 16import optparse 17import os 18import subprocess 19import sys 20import traceback 21 22CROS_BOARD_ENV = 'BISECT_CROS_BOARD' 23CROS_IP_ENV = 'BISECT_CROS_IP' 24 25def LoadConfigFile(path_to_file): 26 """Attempts to load the file 'run-bisect-perf-regression.cfg' as a module 27 and grab the global config dict. 28 29 Args: 30 path_to_file: Path to the run-bisect-perf-regression.cfg file. 31 32 Returns: 33 The config dict which should be formatted as follows: 34 {'command': string, 'good_revision': string, 'bad_revision': string 35 'metric': string}. 36 Returns None on failure. 37 """ 38 try: 39 local_vars = {} 40 execfile(os.path.join(path_to_file, 'run-bisect-perf-regression.cfg'), 41 local_vars) 42 43 return local_vars['config'] 44 except: 45 print 46 traceback.print_exc() 47 print 48 return None 49 50 51def RunBisectionScript(config, working_directory, path_to_file, path_to_goma): 52 """Attempts to execute src/tools/bisect-perf-regression.py with the parameters 53 passed in. 54 55 Args: 56 config: A dict containing the parameters to pass to the script. 57 working_directory: A working directory to provide to the 58 bisect-perf-regression.py script, where it will store it's own copy of 59 the depot. 60 path_to_file: Path to the bisect-perf-regression.py script. 61 path_to_goma: Path to goma directory. 62 63 Returns: 64 0 on success, otherwise 1. 65 """ 66 67 cmd = ['python', os.path.join(path_to_file, 'bisect-perf-regression.py'), 68 '-c', config['command'], 69 '-g', config['good_revision'], 70 '-b', config['bad_revision'], 71 '-m', config['metric'], 72 '--working_directory', working_directory, 73 '--output_buildbot_annotations'] 74 75 if config['repeat_count']: 76 cmd.extend(['-r', config['repeat_count']]) 77 78 if config['truncate_percent']: 79 cmd.extend(['-t', config['truncate_percent']]) 80 81 if config['max_time_minutes']: 82 cmd.extend(['--repeat_test_max_time', config['max_time_minutes']]) 83 84 cmd.extend(['--build_preference', 'ninja']) 85 86 if '--browser=cros' in config['command']: 87 cmd.extend(['--target_platform', 'cros']) 88 89 if os.environ[CROS_BOARD_ENV] and os.environ[CROS_IP_ENV]: 90 cmd.extend(['--cros_board', os.environ[CROS_BOARD_ENV]]) 91 cmd.extend(['--cros_remote_ip', os.environ[CROS_IP_ENV]]) 92 else: 93 print 'Error: Cros build selected, but BISECT_CROS_IP or'\ 94 'BISECT_CROS_BOARD undefined.' 95 print 96 return 1 97 98 if '--browser=android' in config['command']: 99 cmd.extend(['--target_platform', 'android']) 100 101 goma_file = '' 102 if path_to_goma: 103 path_to_goma = os.path.abspath(path_to_goma) 104 105 if os.name == 'nt': 106 os.environ['CC'] = os.path.join(path_to_goma, 'gomacc.exe') + ' cl.exe' 107 os.environ['CXX'] = os.path.join(path_to_goma, 'gomacc.exe') + ' cl.exe' 108 goma_file = os.path.join(path_to_goma, 'goma_ctl.bat') 109 else: 110 os.environ['PATH'] = os.pathsep.join([path_to_goma, os.environ['PATH']]) 111 goma_file = os.path.join(path_to_goma, 'goma_ctl.sh') 112 113 cmd.append('--use_goma') 114 115 # Sometimes goma is lingering around if something went bad on a previous 116 # run. Stop it before starting a new process. Can ignore the return code 117 # since it will return an error if it wasn't running. 118 subprocess.call([goma_file, 'stop']) 119 120 return_code = subprocess.call([goma_file, 'start']) 121 if return_code: 122 print 'Error: goma failed to start.' 123 print 124 return return_code 125 126 cmd = [str(c) for c in cmd] 127 128 return_code = subprocess.call(cmd) 129 130 if path_to_goma: 131 subprocess.call([goma_file, 'stop']) 132 133 if return_code: 134 print 'Error: bisect-perf-regression.py returned with error %d' %\ 135 return_code 136 print 137 138 return return_code 139 140 141def main(): 142 143 usage = ('%prog [options] [-- chromium-options]\n' 144 'Used by a trybot to run the bisection script using the parameters' 145 ' provided in the run-bisect-perf-regression.cfg file.') 146 147 parser = optparse.OptionParser(usage=usage) 148 parser.add_option('-w', '--working_directory', 149 type='str', 150 help='A working directory to supply to the bisection ' 151 'script, which will use it as the location to checkout ' 152 'a copy of the chromium depot.') 153 parser.add_option('-p', '--path_to_goma', 154 type='str', 155 help='Path to goma directory. If this is supplied, goma ' 156 'builds will be enabled.') 157 (opts, args) = parser.parse_args() 158 159 if not opts.working_directory: 160 print 'Error: missing required parameter: --working_directory' 161 print 162 parser.print_help() 163 return 1 164 165 path_to_file = os.path.abspath(os.path.dirname(sys.argv[0])) 166 167 config = LoadConfigFile(path_to_file) 168 if not config: 169 print 'Error: Could not load config file. Double check your changes to '\ 170 'run-bisect-perf-regression.cfg for syntax errors.' 171 print 172 return 1 173 174 return RunBisectionScript(config, opts.working_directory, path_to_file, 175 opts.path_to_goma) 176 177 178if __name__ == '__main__': 179 sys.exit(main()) 180