Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 4 # for details. All rights reserved. Use of this source code is governed by a | 4 # for details. All rights reserved. Use of this source code is governed by a |
| 5 # BSD-style license that can be found in the LICENSE file. | 5 # BSD-style license that can be found in the LICENSE file. |
| 6 | 6 |
| 7 import datetime | 7 import datetime |
| 8 import math | 8 import math |
| 9 try: | |
| 10 from matplotlib.font_manager import FontProperties | |
| 11 import matplotlib.pyplot as plt | |
| 12 except ImportError: | |
| 13 pass # Only needed if we want to make graphs. | |
| 14 import optparse | 9 import optparse |
| 15 import os | 10 import os |
| 16 from os.path import dirname, abspath | 11 from os.path import dirname, abspath |
| 12 import pickle | |
| 17 import platform | 13 import platform |
| 18 import re | 14 import re |
| 19 import shutil | 15 import shutil |
| 20 import stat | 16 import stat |
| 21 import subprocess | 17 import subprocess |
| 22 import sys | 18 import sys |
| 23 import time | 19 import time |
| 24 | 20 |
| 25 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) | 21 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) |
| 26 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), | 22 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 if append: | 56 if append: |
| 61 mode = 'a' | 57 mode = 'a' |
| 62 out = open(outfile, mode) | 58 out = open(outfile, mode) |
| 63 if append: | 59 if append: |
| 64 # Annoying Windows "feature" -- append doesn't actually append unless | 60 # Annoying Windows "feature" -- append doesn't actually append unless |
| 65 # you explicitly go to the end of the file. | 61 # you explicitly go to the end of the file. |
| 66 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html | 62 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html |
| 67 out.seek(0, os.SEEK_END) | 63 out.seek(0, os.SEEK_END) |
| 68 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, | 64 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, |
| 69 stdin=subprocess.PIPE, shell=self.has_shell) | 65 stdin=subprocess.PIPE, shell=self.has_shell) |
| 70 output, stderr = p.communicate(std_in); | 66 output, stderr = p.communicate(std_in) |
| 71 if output: | 67 if output: |
| 72 print output | 68 print output |
| 73 if stderr: | 69 if stderr: |
| 74 print stderr | 70 print stderr |
| 75 return output | 71 return output, stderr |
| 76 | 72 |
| 77 def time_cmd(self, cmd): | 73 def time_cmd(self, cmd): |
| 78 """Determine the amount of (real) time it takes to execute a given | 74 """Determine the amount of (real) time it takes to execute a given |
| 79 command.""" | 75 command.""" |
| 80 start = time.time() | 76 start = time.time() |
| 81 self.run_cmd(cmd) | 77 self.run_cmd(cmd) |
| 82 return time.time() - start | 78 return time.time() - start |
| 83 | 79 |
| 84 @staticmethod | 80 def sync_and_build(self, suites, revision_num=''): |
| 85 def get_build_targets(suites): | |
| 86 """Loop through a set of tests that we want to run and find the build | |
| 87 targets that are necessary. | |
| 88 | |
| 89 Args: | |
| 90 suites: The test suites that we wish to run.""" | |
| 91 build_targets = set() | |
| 92 for test in suites: | |
| 93 if test.build_targets is not None: | |
| 94 for target in test.build_targets: | |
| 95 build_targets.add(target) | |
| 96 return build_targets | |
| 97 | |
| 98 def sync_and_build(self, suites): | |
| 99 """Make sure we have the latest version of of the repo, and build it. We | 81 """Make sure we have the latest version of of the repo, and build it. We |
| 100 begin and end standing in DART_INSTALL_LOCATION. | 82 begin and end standing in DART_INSTALL_LOCATION. |
| 101 | 83 |
| 102 Args: | 84 Args: |
| 103 suites: The set of suites that we wish to build. | 85 suites: The set of suites that we wish to build. |
| 104 | 86 |
| 105 Returns: | 87 Returns: |
| 106 err_code = 1 if there was a problem building.""" | 88 err_code = 1 if there was a problem building.""" |
| 107 os.chdir(DART_INSTALL_LOCATION) | 89 os.chdir(DART_INSTALL_LOCATION) |
| 90 | |
| 91 if revision_num == '': | |
| 92 self.run_cmd(['gclient', 'sync']) | |
| 93 else: | |
| 94 self.run_cmd(['gclient', 'sync', '-r', revision_num, '-t']) | |
| 108 | 95 |
| 109 self.run_cmd(['gclient', 'sync']) | 96 if revision_num == '': |
| 110 | 97 revision_num = search_for_revision(['svn', 'info']) |
| 111 # On Windows, the output directory is marked as "Read Only," which causes an | 98 if revision_num == -1: |
| 112 # error to be thrown when we use shutil.rmtree. This helper function changes | 99 revision_num = search_for_revision(['git', 'svn', 'info']) |
| 113 # the permissions so we can still delete the directory. | 100 _, stderr = self.run_cmd(['python', os.path.join(DART_INSTALL_LOCATION, |
| 114 def on_rm_error(func, path, exc_info): | 101 'tools', 'get_archive.py'), 'sdk', '-r', revision_num]) |
| 115 if os.path.exists(path): | 102 if 'InvalidUriError' in stderr: |
| 116 os.chmod(path, stat.S_IWRITE) | 103 return 1 |
| 117 os.unlink(path) | |
| 118 # TODO(efortuna): building the sdk locally is a band-aid until all build | |
| 119 # platform SDKs are hosted in Google storage. Pull from https://sandbox. | |
| 120 # google.com/storage/?arg=dart-dump-render-tree/sdk/#dart-dump-render-tree | |
| 121 # %2Fsdk eventually. | |
| 122 # TODO(efortuna): Currently always building ia32 architecture because we | |
| 123 # don't have test statistics for what's passing on x64. Eliminate arch | |
| 124 # specification when we have tests running on x64, too. | |
| 125 shutil.rmtree(os.path.join(os.getcwd(), | |
| 126 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | |
| 127 onerror=on_rm_error) | |
| 128 | |
| 129 for target in TestRunner.get_build_targets(suites): | |
| 130 lines = self.run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', | |
| 131 'release', '--arch=ia32', target]) | |
| 132 | |
| 133 for line in lines: | |
| 134 if 'BUILD FAILED' in lines: | |
| 135 # Someone checked in a broken build! Stop trying to make it work | |
| 136 # and wait to try again. | |
| 137 print 'Broken Build' | |
| 138 return 1 | |
| 139 return 0 | 104 return 0 |
| 140 | 105 |
| 141 def ensure_output_directory(self, dir_name): | 106 def ensure_output_directory(self, dir_name): |
| 142 """Test that the listed directory name exists, and if not, create one for | 107 """Test that the listed directory name exists, and if not, create one for |
| 143 our output to be placed. | 108 our output to be placed. |
| 144 | 109 |
| 145 Args: | 110 Args: |
| 146 dir_name: the directory we will create if it does not exist.""" | 111 dir_name: the directory we will create if it does not exist.""" |
| 147 dir_path = os.path.join(DART_INSTALL_LOCATION, 'tools', | 112 dir_path = os.path.join(DART_INSTALL_LOCATION, 'tools', |
| 148 'testing', 'perf_testing', dir_name) | 113 'testing', 'perf_testing', dir_name) |
| 149 if not os.path.exists(dir_path): | 114 if not os.path.exists(dir_path): |
| 150 os.makedirs(dir_path) | 115 os.makedirs(dir_path) |
| 151 print 'Creating output directory ', dir_path | 116 print 'Creating output directory ', dir_path |
| 152 | 117 |
| 153 def has_new_code(self): | 118 def has_new_code(self): |
| 154 """Tests if there are any newer versions of files on the server.""" | 119 """Tests if there are any newer versions of files on the server.""" |
| 155 os.chdir(DART_INSTALL_LOCATION) | 120 os.chdir(DART_INSTALL_LOCATION) |
| 156 # Pass 'p' in if we have a new certificate for the svn server, we want to | 121 # Pass 'p' in if we have a new certificate for the svn server, we want to |
| 157 # (p)ermanently accept it. | 122 # (p)ermanently accept it. |
| 158 results = self.run_cmd(['svn', 'st', '-u'], std_in='p\r\n') | 123 results, _ = self.run_cmd(['svn', 'st', '-u'], std_in='p\r\n') |
| 159 for line in results: | 124 for line in results: |
| 160 if '*' in line: | 125 if '*' in line: |
| 161 return True | 126 return True |
| 162 return False | 127 return False |
| 163 | 128 |
| 164 def get_os_directory(self): | 129 def get_os_directory(self): |
| 165 """Specifies the name of the directory for the testing build of dart, which | 130 """Specifies the name of the directory for the testing build of dart, which |
| 166 has yet a different naming convention from utils.getBuildRoot(...).""" | 131 has yet a different naming convention from utils.getBuildRoot(...).""" |
| 167 if platform.system() == 'Windows': | 132 if platform.system() == 'Windows': |
| 168 return 'windows' | 133 return 'windows' |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 204 print ('Error: Invalid suite %s not in ' % name) + \ | 169 print ('Error: Invalid suite %s not in ' % name) + \ |
| 205 '%s' % ','.join(TestBuilder.available_suite_names()) | 170 '%s' % ','.join(TestBuilder.available_suite_names()) |
| 206 sys.exit(1) | 171 sys.exit(1) |
| 207 self.suite_names = suites | 172 self.suite_names = suites |
| 208 self.no_build = args.no_build | 173 self.no_build = args.no_build |
| 209 self.no_upload = args.no_upload | 174 self.no_upload = args.no_upload |
| 210 self.no_test = args.no_test | 175 self.no_test = args.no_test |
| 211 self.verbose = args.verbose | 176 self.verbose = args.verbose |
| 212 return args.continuous | 177 return args.continuous |
| 213 | 178 |
| 214 def run_test_sequence(self): | 179 def run_test_sequence(self, revision_num='', num_reruns=1): |
| 215 """Run the set of commands to (possibly) build, run, and graph the results | 180 """Run the set of commands to (possibly) build, run, and post the results |
| 216 of our tests. | 181 of our tests. Returns 0 on a successful run, 1 if we fail to post results or |
| 182 the run failed, -1 if the build is broken. | |
| 217 """ | 183 """ |
| 218 suites = [] | 184 suites = [] |
| 185 success = True | |
| 219 for name in self.suite_names: | 186 for name in self.suite_names: |
| 220 suites += [TestBuilder.make_test(name, self)] | 187 for run in range(num_reruns): |
| 188 suites += [TestBuilder.make_test(name, self)] | |
| 221 | 189 |
| 222 if not self.no_build and self.sync_and_build(suites) == 1: | 190 if not self.no_build and self.sync_and_build(suites, revision_num) == 1: |
| 223 return # The build is broken. | 191 return -1 # The build is broken. |
| 224 | 192 |
| 225 for test in suites: | 193 for test in suites: |
| 226 test.run() | 194 success = success and test.run() |
| 195 if success: | |
| 196 return 0 | |
| 197 else: | |
| 198 return 1 | |
| 227 | 199 |
| 228 | 200 |
| 229 class Test(object): | 201 class Test(object): |
| 230 """The base class to provide shared code for different tests we will run and | 202 """The base class to provide shared code for different tests we will run and |
| 231 graph. At a high level, each test has three visitors (the tester, the | 203 post. At a high level, each test has three visitors (the tester and the |
| 232 file_processor and the grapher) that perform operations on the test object.""" | 204 file_processor) that perform operations on the test object.""" |
| 233 | 205 |
| 234 def __init__(self, result_folder_name, platform_list, variants, | 206 def __init__(self, result_folder_name, platform_list, variants, |
| 235 values_list, test_runner, tester, file_processor, grapher, | 207 values_list, test_runner, tester, file_processor, |
| 236 extra_metrics=['Geo-Mean'], build_targets=['create_sdk']): | 208 extra_metrics=['Geo-Mean']): |
| 237 """Args: | 209 """Args: |
| 238 result_folder_name: The name of the folder where a tracefile of | 210 result_folder_name: The name of the folder where a tracefile of |
| 239 performance results will be stored. | 211 performance results will be stored. |
| 240 platform_list: A list containing the platform(s) that our data has been | 212 platform_list: A list containing the platform(s) that our data has been |
| 241 run on. (command line, firefox, chrome, etc) | 213 run on. (command line, firefox, chrome, etc) |
| 242 variants: A list specifying whether we hold data about Frog | 214 variants: A list specifying whether we hold data about Frog |
| 243 generated code, plain JS code, or a combination of both, or | 215 generated code, plain JS code, or a combination of both, or |
| 244 Dart depending on the test. | 216 Dart depending on the test. |
| 245 values_list: A list containing the type of data we will be graphing | 217 values_list: A list containing the type of data we will be graphing |
| 246 (benchmarks, percentage passing, etc). | 218 (benchmarks, percentage passing, etc). |
| 247 test_runner: Reference to the parent test runner object that notifies a | 219 test_runner: Reference to the parent test runner object that notifies a |
| 248 test when to run. | 220 test when to run. |
| 249 tester: The visitor that actually performs the test running mechanics. | 221 tester: The visitor that actually performs the test running mechanics. |
| 250 file_processor: The visitor that processes files in the format | 222 file_processor: The visitor that processes files in the format |
| 251 appropriate for this test. | 223 appropriate for this test. |
| 252 grapher: The visitor that generates graphs given our test result data. | |
| 253 extra_metrics: A list of any additional measurements we wish to keep | 224 extra_metrics: A list of any additional measurements we wish to keep |
| 254 track of (such as the geometric mean of a set, the sum, etc). | 225 track of (such as the geometric mean of a set, the sum, etc).""" |
| 255 build_targets: The targets necessary to build to run these tests | |
| 256 (default target is create_sdk).""" | |
| 257 self.result_folder_name = result_folder_name | 226 self.result_folder_name = result_folder_name |
| 258 # cur_time is used as a timestamp of when this performance test was run. | 227 # cur_time is used as a timestamp of when this performance test was run. |
| 259 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) | 228 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) |
| 260 self.values_list = values_list | 229 self.values_list = values_list |
| 261 self.platform_list = platform_list | 230 self.platform_list = platform_list |
| 262 self.test_runner = test_runner | 231 self.test_runner = test_runner |
| 263 self.tester = tester | 232 self.tester = tester |
| 264 self.file_processor = file_processor | 233 self.file_processor = file_processor |
| 265 self.build_targets = build_targets | |
| 266 self.revision_dict = dict() | 234 self.revision_dict = dict() |
| 267 self.values_dict = dict() | 235 self.values_dict = dict() |
| 268 self.grapher = grapher | |
| 269 self.extra_metrics = extra_metrics | 236 self.extra_metrics = extra_metrics |
| 270 # Initialize our values store. | 237 # Initialize our values store. |
| 271 for platform in platform_list: | 238 for platform in platform_list: |
| 272 self.revision_dict[platform] = dict() | 239 self.revision_dict[platform] = dict() |
| 273 self.values_dict[platform] = dict() | 240 self.values_dict[platform] = dict() |
| 274 for f in variants: | 241 for f in variants: |
| 275 self.revision_dict[platform][f] = dict() | 242 self.revision_dict[platform][f] = dict() |
| 276 self.values_dict[platform][f] = dict() | 243 self.values_dict[platform][f] = dict() |
| 277 for val in values_list: | 244 for val in values_list: |
| 278 self.revision_dict[platform][f][val] = [] | 245 self.revision_dict[platform][f][val] = [] |
| 279 self.values_dict[platform][f][val] = [] | 246 self.values_dict[platform][f][val] = [] |
| 280 for extra_metric in extra_metrics: | 247 for extra_metric in extra_metrics: |
| 281 self.revision_dict[platform][f][extra_metric] = [] | 248 self.revision_dict[platform][f][extra_metric] = [] |
| 282 self.values_dict[platform][f][extra_metric] = [] | 249 self.values_dict[platform][f][extra_metric] = [] |
| 283 | 250 |
| 284 def is_valid_combination(self, platform, variant): | 251 def is_valid_combination(self, platform, variant): |
| 285 """Check whether data should be captured for this platform/variant | 252 """Check whether data should be captured for this platform/variant |
| 286 combination. | 253 combination. |
| 287 """ | 254 """ |
| 288 return True | 255 return True |
| 289 | 256 |
| 290 def run(self): | 257 def run(self): |
| 291 """Run the benchmarks/tests from the command line and plot the | 258 """Run the benchmarks/tests from the command line and plot the |
| 292 results. | 259 results. |
| 293 """ | 260 """ |
| 294 for visitor in [self.tester, self.file_processor, self.grapher]: | 261 for visitor in [self.tester, self.file_processor]: |
| 295 visitor.prepare() | 262 visitor.prepare() |
| 296 | 263 |
| 297 os.chdir(DART_INSTALL_LOCATION) | 264 os.chdir(DART_INSTALL_LOCATION) |
| 298 self.test_runner.ensure_output_directory(self.result_folder_name) | 265 self.test_runner.ensure_output_directory(self.result_folder_name) |
| 299 self.test_runner.ensure_output_directory(os.path.join( | 266 self.test_runner.ensure_output_directory(os.path.join( |
| 300 'old', self.result_folder_name)) | 267 'old', self.result_folder_name)) |
| 301 if not self.test_runner.no_test: | 268 if not self.test_runner.no_test: |
| 302 self.tester.run_tests() | 269 self.tester.run_tests() |
| 303 | 270 |
| 304 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) | 271 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) |
| 305 | 272 |
| 306 for afile in os.listdir(os.path.join('old', self.result_folder_name)): | 273 for afile in os.listdir(os.path.join('old', self.result_folder_name)): |
| 307 if not afile.startswith('.'): | 274 if not afile.startswith('.'): |
| 308 self.file_processor.process_file(afile, False) | 275 self.file_processor.process_file(afile, False) |
| 309 | 276 |
| 310 files = os.listdir(self.result_folder_name) | 277 files = os.listdir(self.result_folder_name) |
| 278 post_success = True | |
| 311 for afile in files: | 279 for afile in files: |
| 312 if not afile.startswith('.'): | 280 if not afile.startswith('.'): |
| 313 should_move_file = self.file_processor.process_file(afile, True) | 281 should_move_file = self.file_processor.process_file(afile, True) |
| 314 if should_move_file: | 282 if should_move_file: |
| 315 shutil.move(os.path.join(self.result_folder_name, afile), | 283 shutil.move(os.path.join(self.result_folder_name, afile), |
| 316 os.path.join('old', self.result_folder_name, afile)) | 284 os.path.join('old', self.result_folder_name, afile)) |
| 285 else: | |
| 286 post_success = False | |
| 317 | 287 |
| 318 if 'plt' in globals(): | 288 return post_success |
| 319 # Only run Matplotlib if it is installed. | |
| 320 self.grapher.plot_results('%s.png' % self.result_folder_name) | |
| 321 | 289 |
| 322 | 290 |
| 323 class Tester(object): | 291 class Tester(object): |
| 324 """The base level visitor class that runs tests. It contains convenience | 292 """The base level visitor class that runs tests. It contains convenience |
| 325 methods that many Tester objects use. Any class that would like to be a | 293 methods that many Tester objects use. Any class that would like to be a |
| 326 TesterVisitor must implement the run_tests() method.""" | 294 TesterVisitor must implement the run_tests() method.""" |
| 327 | 295 |
| 328 def __init__(self, test): | 296 def __init__(self, test): |
| 329 self.test = test | 297 self.test = test |
| 330 | 298 |
| 331 def prepare(self): | 299 def prepare(self): |
| 332 """Perform any initial setup required before the test is run.""" | 300 """Perform any initial setup required before the test is run.""" |
| 333 pass | 301 pass |
| 334 | 302 |
| 335 def add_svn_revision_to_trace(self, outfile, browser = None): | 303 def add_svn_revision_to_trace(self, outfile, browser = None): |
| 336 """Add the svn version number to the provided tracefile.""" | 304 """Add the svn version number to the provided tracefile.""" |
| 337 def search_for_revision(svn_info_command): | |
| 338 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | |
| 339 stderr = subprocess.STDOUT, shell = | |
| 340 self.test.test_runner.has_shell) | |
| 341 output, _ = p.communicate() | |
| 342 for line in output.split('\n'): | |
| 343 if 'Revision' in line: | |
| 344 self.test.test_runner.run_cmd(['echo', line.strip()], outfile) | |
| 345 return True | |
| 346 return False | |
| 347 | |
| 348 def get_dartium_revision(): | 305 def get_dartium_revision(): |
| 349 version_file_name = os.path.join(DART_INSTALL_LOCATION, 'client', 'tests', | 306 version_file_name = os.path.join(DART_INSTALL_LOCATION, 'client', 'tests', |
| 350 'dartium', 'LAST_VERSION') | 307 'dartium', 'LAST_VERSION') |
| 351 version_file = open(version_file_name, 'r') | 308 version_file = open(version_file_name, 'r') |
| 352 version = version_file.read().split('.')[-2] | 309 version = version_file.read().split('.')[-2] |
| 353 version_file.close() | 310 version_file.close() |
| 354 return version | 311 return version |
| 355 | 312 |
| 356 if browser and browser == 'dartium': | 313 if browser and browser == 'dartium': |
| 357 revision = get_dartium_revision() | 314 revision = get_dartium_revision() |
| 358 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | 315 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) |
| 359 elif not search_for_revision(['svn', 'info']): | 316 else: |
| 360 if not search_for_revision(['git', 'svn', 'info']): | 317 revision = search_for_revision(['svn', 'info']) |
| 361 self.test.test_runner.run_cmd(['echo', 'Revision: unknown'], outfile) | 318 if revision == -1: |
| 319 revision = search_for_revision(['git', 'svn', 'info']) | |
| 320 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | |
| 362 | 321 |
| 363 | 322 |
| 364 class Processor(object): | 323 class Processor(object): |
| 365 """The base level vistor class that processes tests. It contains convenience | 324 """The base level vistor class that processes tests. It contains convenience |
| 366 methods that many File Processor objects use. Any class that would like to be | 325 methods that many File Processor objects use. Any class that would like to be |
| 367 a ProcessorVisitor must implement the process_file() method.""" | 326 a ProcessorVisitor must implement the process_file() method.""" |
| 368 | 327 |
| 369 SCORE = 'Score' | 328 SCORE = 'Score' |
| 370 COMPILE_TIME = 'CompileTime' | 329 COMPILE_TIME = 'CompileTime' |
| 371 CODE_SIZE = 'CodeSize' | 330 CODE_SIZE = 'CodeSize' |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 402 dartium). | 361 dartium). |
| 403 | 362 |
| 404 Returns: True if the post was successful file.""" | 363 Returns: True if the post was successful file.""" |
| 405 return post_results.report_results(benchmark_name, score, platform, variant, | 364 return post_results.report_results(benchmark_name, score, platform, variant, |
| 406 revision_number, metric) | 365 revision_number, metric) |
| 407 | 366 |
| 408 def calculate_geometric_mean(self, platform, variant, svn_revision): | 367 def calculate_geometric_mean(self, platform, variant, svn_revision): |
| 409 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | 368 """Calculate the aggregate geometric mean for JS and frog benchmark sets, |
| 410 given two benchmark dictionaries.""" | 369 given two benchmark dictionaries.""" |
| 411 geo_mean = 0 | 370 geo_mean = 0 |
| 412 # TODO(vsm): Suppress graphing this combination altogether. For | |
| 413 # now, we feed a geomean of 0. | |
| 414 if self.test.is_valid_combination(platform, variant): | 371 if self.test.is_valid_combination(platform, variant): |
| 415 for benchmark in self.test.values_list: | 372 for benchmark in self.test.values_list: |
| 416 geo_mean += math.log( | 373 geo_mean += math.log( |
| 417 self.test.values_dict[platform][variant][benchmark][ | 374 self.test.values_dict[platform][variant][benchmark][ |
| 418 len(self.test.values_dict[platform][variant][benchmark]) - 1]) | 375 len(self.test.values_dict[platform][variant][benchmark]) - 1]) |
| 419 | 376 |
| 420 self.test.values_dict[platform][variant]['Geo-Mean'] += \ | 377 self.test.values_dict[platform][variant]['Geo-Mean'] += \ |
| 421 [math.pow(math.e, geo_mean / len(self.test.values_list))] | 378 [math.pow(math.e, geo_mean / len(self.test.values_list))] |
| 422 self.test.revision_dict[platform][variant]['Geo-Mean'] += [svn_revision] | 379 self.test.revision_dict[platform][variant]['Geo-Mean'] += [svn_revision] |
| 423 | |
| 424 | |
| 425 class Grapher(object): | |
| 426 """The base level visitor class that generates graphs for data. It contains | |
| 427 convenience methods that many Grapher objects use. Any class that would like | |
| 428 to be a GrapherVisitor must implement the plot_results() method.""" | |
| 429 | 380 |
| 430 graph_out_dir = 'graphs' | 381 def get_score_type(self, benchmark_name): |
| 382 """Determine the type of score for posting -- default is 'Score' (aka | |
| 383 Runtime), other options are CompileTime and CodeSize.""" | |
| 384 return self.SCORE | |
| 431 | 385 |
| 432 def __init__(self, test): | |
| 433 self.color_index = 0 | |
| 434 self.test = test | |
| 435 | |
| 436 def prepare(self): | |
| 437 """Perform any initial setup required before the test is run.""" | |
| 438 if 'plt' in globals(): | |
| 439 plt.cla() # cla = clear current axes | |
| 440 else: | |
| 441 print 'Unable to import Matplotlib and therefore unable to generate ' + \ | |
| 442 'graphs. Please install it for this version of Python.' | |
| 443 self.test.test_runner.ensure_output_directory(Grapher.graph_out_dir) | |
| 444 | |
| 445 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, | |
| 446 legend_loc, filename, platform_list, variants, | |
| 447 values_list, should_clear_axes=True): | |
| 448 """Sets style preferences for chart boilerplate that is consistent across | |
| 449 all charts, and saves the chart as a png. | |
| 450 Args: | |
| 451 size_x: the size of the printed chart, in inches, in the horizontal | |
| 452 direction | |
| 453 size_y: the size of the printed chart, in inches in the vertical direction | |
| 454 legend_loc: the location of the legend in on the chart. See suitable | |
| 455 arguments for the loc argument in matplotlib | |
| 456 filename: the filename that we want to save the resulting chart as | |
| 457 platform_list: a list containing the platform(s) that our data has been | |
| 458 run on. (command line, firefox, chrome, etc) | |
| 459 values_list: a list containing the type of data we will be graphing | |
| 460 (performance, percentage passing, etc) | |
| 461 should_clear_axes: True if we want to create a fresh graph, instead of | |
| 462 plotting additional lines on the current graph.""" | |
| 463 if should_clear_axes: | |
| 464 plt.cla() # cla = clear current axes | |
| 465 for platform in platform_list: | |
| 466 for f in variants: | |
| 467 for val in values_list: | |
| 468 plt.plot(self.test.revision_dict[platform][f][val], | |
| 469 self.test.values_dict[platform][f][val], | |
| 470 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) | |
| 471 | |
| 472 plt.xlabel('Revision Number') | |
| 473 plt.ylabel(y_axis_label) | |
| 474 plt.title(chart_title) | |
| 475 fontP = FontProperties() | |
| 476 fontP.set_size('small') | |
| 477 plt.legend(loc=legend_loc, prop = fontP) | |
| 478 | |
| 479 fig = plt.gcf() | |
| 480 fig.set_size_inches(size_x, size_y) | |
| 481 fig.savefig(os.path.join(Grapher.graph_out_dir, filename)) | |
| 482 | |
| 483 def get_color(self): | |
| 484 # Just a bunch of distinct colors for a potentially large number of values | |
| 485 # we wish to graph. | |
| 486 colors = [ | |
| 487 'blue', 'green', 'red', 'cyan', 'magenta', 'black', '#3366CC', | |
| 488 '#DC3912', '#FF9900', '#109618', '#990099', '#0099C6', '#DD4477', | |
| 489 '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11', | |
| 490 '#6633CC', '#E67300', '#8B0707', '#651067', '#329262', '#5574A6', | |
| 491 '#3B3EAC', '#B77322', '#16D620', '#B91383', '#F4359E', '#9C5935', | |
| 492 '#A9C413', '#2A778D', '#668D1C', '#BEA413', '#0C5922', '#743411', | |
| 493 '#45AFE2', '#FF3300', '#FFCC00', '#14C21D', '#DF51FD', '#15CBFF', | |
| 494 '#FF97D2', '#97FB00', '#DB6651', '#518BC6', '#BD6CBD', '#35D7C2', | |
| 495 '#E9E91F', '#9877DD', '#FF8F20', '#D20B0B', '#B61DBA', '#40BD7E', | |
| 496 '#6AA7C4', '#6D70CD', '#DA9136', '#2DEA36', '#E81EA6', '#F558AE', | |
| 497 '#C07145', '#D7EE53', '#3EA7C6', '#97D129', '#E9CA1D', '#149638', | |
| 498 '#C5571D'] | |
| 499 color = colors[self.color_index] | |
| 500 self.color_index = (self.color_index + 1) % len(colors) | |
| 501 return color | |
| 502 | 386 |
| 503 class RuntimePerformanceTest(Test): | 387 class RuntimePerformanceTest(Test): |
| 504 """Super class for all runtime performance testing.""" | 388 """Super class for all runtime performance testing.""" |
| 505 | 389 |
| 506 def __init__(self, result_folder_name, platform_list, platform_type, | 390 def __init__(self, result_folder_name, platform_list, platform_type, |
| 507 versions, benchmarks, test_runner, tester, file_processor, | 391 versions, benchmarks, test_runner, tester, file_processor): |
| 508 build_targets=['create_sdk']): | |
| 509 """Args: | 392 """Args: |
| 510 result_folder_name: The name of the folder where a tracefile of | 393 result_folder_name: The name of the folder where a tracefile of |
| 511 performance results will be stored. | 394 performance results will be stored. |
| 512 platform_list: A list containing the platform(s) that our data has been | 395 platform_list: A list containing the platform(s) that our data has been |
| 513 run on. (command line, firefox, chrome, etc) | 396 run on. (command line, firefox, chrome, etc) |
| 514 variants: A list specifying whether we hold data about Frog | 397 variants: A list specifying whether we hold data about Frog |
| 515 generated code, plain JS code, or a combination of both, or | 398 generated code, plain JS code, or a combination of both, or |
| 516 Dart depending on the test. | 399 Dart depending on the test. |
| 517 values_list: A list containing the type of data we will be graphing | 400 values_list: A list containing the type of data we will be graphing |
| 518 (benchmarks, percentage passing, etc). | 401 (benchmarks, percentage passing, etc). |
| 519 test_runner: Reference to the parent test runner object that notifies a | 402 test_runner: Reference to the parent test runner object that notifies a |
| 520 test when to run. | 403 test when to run. |
| 521 tester: The visitor that actually performs the test running mechanics. | 404 tester: The visitor that actually performs the test running mechanics. |
| 522 file_processor: The visitor that processes files in the format | 405 file_processor: The visitor that processes files in the format |
| 523 appropriate for this test. | 406 appropriate for this test. |
| 524 grapher: The visitor that generates graphs given our test result data. | |
| 525 extra_metrics: A list of any additional measurements we wish to keep | 407 extra_metrics: A list of any additional measurements we wish to keep |
| 526 track of (such as the geometric mean of a set, the sum, etc). | 408 track of (such as the geometric mean of a set, the sum, etc).""" |
| 527 build_targets: The targets necessary to build to run these tests | |
| 528 (default target is create_sdk).""" | |
| 529 super(RuntimePerformanceTest, self).__init__(result_folder_name, | 409 super(RuntimePerformanceTest, self).__init__(result_folder_name, |
| 530 platform_list, versions, benchmarks, test_runner, tester, | 410 platform_list, versions, benchmarks, test_runner, tester, |
| 531 file_processor, RuntimePerfGrapher(self), | 411 file_processor) |
| 532 build_targets=build_targets) | |
| 533 self.platform_list = platform_list | 412 self.platform_list = platform_list |
| 534 self.platform_type = platform_type | 413 self.platform_type = platform_type |
| 535 self.versions = versions | 414 self.versions = versions |
| 536 self.benchmarks = benchmarks | 415 self.benchmarks = benchmarks |
| 537 | 416 |
| 538 class RuntimePerfGrapher(Grapher): | |
| 539 def plot_all_perf(self, png_filename): | |
| 540 """Create a plot that shows the performance changes of individual | |
| 541 benchmarks run by JS and generated by frog, over svn history.""" | |
| 542 for benchmark in self.test.benchmarks: | |
| 543 self.style_and_save_perf_plot( | |
| 544 'Performance of %s over time on the %s on %s' % (benchmark, | |
| 545 self.test.platform_type, utils.GuessOS()), | |
| 546 'Speed (bigger = better)', 16, 14, 'lower left', | |
| 547 benchmark + png_filename, self.test.platform_list, | |
| 548 self.test.versions, [benchmark]) | |
| 549 | |
| 550 def plot_avg_perf(self, png_filename, platforms=None, versions=None): | |
| 551 """Generate a plot that shows the performance changes of the geomentric | |
| 552 mean of JS and frog benchmark performance over svn history.""" | |
| 553 if platforms == None: | |
| 554 platforms = self.test.platform_list | |
| 555 if versions == None: | |
| 556 versions = self.test.versions | |
| 557 (title, y_axis, size_x, size_y, loc, filename) = \ | |
| 558 ('Geometric Mean of benchmark %s performance on %s ' % | |
| 559 (self.test.platform_type, utils.GuessOS()), 'Speed (bigger = better)', | |
| 560 16, 5, 'lower left', 'avg'+png_filename) | |
| 561 clear_axis = True | |
| 562 for platform in platforms: | |
| 563 for version in versions: | |
| 564 if self.test.is_valid_combination(platform, version): | |
| 565 for metric in self.test.extra_metrics: | |
| 566 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, | |
| 567 filename, [platform], [version], | |
| 568 [metric], clear_axis) | |
| 569 clear_axis = False | |
| 570 | |
| 571 def plot_results(self, png_filename): | |
| 572 self.plot_all_perf(png_filename) | |
| 573 self.plot_avg_perf('2' + png_filename) | |
| 574 | 417 |
| 575 class BrowserTester(Tester): | 418 class BrowserTester(Tester): |
| 576 @staticmethod | 419 @staticmethod |
| 577 def get_browsers(add_dartium=True): | 420 def get_browsers(add_dartium=True): |
| 578 browsers = ['ff', 'chrome'] | 421 browsers = ['ff', 'chrome'] |
| 579 if add_dartium: | 422 if add_dartium: |
| 580 browsers += ['dartium'] | 423 browsers += ['dartium'] |
| 581 has_shell = False | 424 has_shell = False |
| 582 if platform.system() == 'Darwin': | 425 if platform.system() == 'Darwin': |
| 583 browsers += ['safari'] | 426 browsers += ['safari'] |
| 584 if platform.system() == 'Windows': | 427 if platform.system() == 'Windows': |
| 585 browsers += ['ie'] | 428 browsers += ['ie'] |
| 586 has_shell = True | 429 has_shell = True |
| 587 if 'dartium' in browsers: | 430 if 'dartium' in browsers: |
| 588 # Fetch it if necessary. | 431 # Fetch it if necessary. |
| 589 get_dartium = ['python', | 432 get_dartium = ['python', |
| 590 os.path.join(DART_INSTALL_LOCATION, 'tools', 'get_drt.py'), | 433 os.path.join(DART_INSTALL_LOCATION, 'tools', |
| 591 '--dartium'] | 434 'get_archive.py'), 'dartium'] |
| 592 # TODO(vsm): It's inconvenient that run_cmd isn't in scope here. | 435 # TODO(vsm): It's inconvenient that run_cmd isn't in scope here. |
| 593 # Perhaps there is a better place to put that or this. | 436 # Perhaps there is a better place to put that or this. |
| 594 subprocess.call(get_dartium, stdout=sys.stdout, stderr=sys.stderr, | 437 subprocess.call(get_dartium, stdout=sys.stdout, stderr=sys.stderr, |
| 595 shell=has_shell) | 438 shell=has_shell) |
| 596 return browsers | 439 return browsers |
| 597 | 440 |
| 598 | 441 |
| 599 class CommonBrowserTest(RuntimePerformanceTest): | 442 class CommonBrowserTest(RuntimePerformanceTest): |
| 600 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the | 443 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the |
| 601 browser.""" | 444 browser.""" |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 617 @staticmethod | 460 @staticmethod |
| 618 def get_standalone_benchmarks(): | 461 def get_standalone_benchmarks(): |
| 619 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', | 462 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', |
| 620 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', | 463 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', |
| 621 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', | 464 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', |
| 622 'TreeSort'] | 465 'TreeSort'] |
| 623 | 466 |
| 624 class CommonBrowserTester(BrowserTester): | 467 class CommonBrowserTester(BrowserTester): |
| 625 def run_tests(self): | 468 def run_tests(self): |
| 626 """Run a performance test in the browser.""" | 469 """Run a performance test in the browser.""" |
| 627 os.chdir('frog') | 470 self.test.test_runner.run_cmd([ |
| 628 self.test.test_runner.run_cmd(['python', os.path.join('benchmarks', | 471 'python', os.path.join('internal', 'browserBenchmarks', |
| 629 'make_web_benchmarks.py')]) | 472 'make_web_benchmarks.py')]) |
| 630 os.chdir('..') | |
| 631 | 473 |
| 632 for browser in self.test.platform_list: | 474 for browser in self.test.platform_list: |
| 633 for version in self.test.versions: | 475 for version in self.test.versions: |
| 634 if not self.test.is_valid_combination(browser, version): | 476 if not self.test.is_valid_combination(browser, version): |
| 635 continue | 477 continue |
| 636 self.test.trace_file = os.path.join( | 478 self.test.trace_file = os.path.join( |
| 637 'tools', 'testing', 'perf_testing', self.test.result_folder_name, | 479 'tools', 'testing', 'perf_testing', self.test.result_folder_name, |
| 638 'perf-%s-%s-%s' % (self.test.cur_time, browser, version)) | 480 'perf-%s-%s-%s' % (self.test.cur_time, browser, version)) |
| 639 self.add_svn_revision_to_trace(self.test.trace_file, browser) | 481 self.add_svn_revision_to_trace(self.test.trace_file, browser) |
| 640 file_path = os.path.join( | 482 file_path = os.path.join( |
| 641 os.getcwd(), 'internal', 'browserBenchmarks', | 483 os.getcwd(), 'internal', 'browserBenchmarks', |
| 642 'benchmark_page_%s.html' % version) | 484 'benchmark_page_%s.html' % version) |
| 643 self.test.test_runner.run_cmd( | 485 self.test.test_runner.run_cmd( |
| 644 ['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 486 ['python', os.path.join('tools', 'testing', 'run_selenium.py'), |
| 645 '--out', file_path, '--browser', browser, | 487 '--out', file_path, '--browser', browser, |
| 646 '--timeout', '600', '--mode', 'perf'], self.test.trace_file, | 488 '--timeout', '600', '--mode', 'perf'], self.test.trace_file, |
| 647 append=True) | 489 append=True) |
| 648 | 490 |
| 649 class CommonBrowserFileProcessor(Processor): | 491 class CommonBrowserFileProcessor(Processor): |
| 492 | |
| 650 def process_file(self, afile, should_post_file): | 493 def process_file(self, afile, should_post_file): |
| 651 """Comb through the html to find the performance results. | 494 """Comb through the html to find the performance results. |
| 652 Returns: True if we successfully posted our data to storage and/or we can | 495 Returns: True if we successfully posted our data to storage and/or we can |
| 653 delete the trace file.""" | 496 delete the trace file.""" |
| 654 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', | 497 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', |
| 655 'testing', 'perf_testing')) | 498 'testing', 'perf_testing')) |
| 656 parts = afile.split('-') | 499 parts = afile.split('-') |
| 657 browser = parts[2] | 500 browser = parts[2] |
| 658 version = parts[3] | 501 version = parts[3] |
| 659 f = self.open_trace_file(afile, should_post_file) | 502 f = self.open_trace_file(afile, should_post_file) |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 687 break | 530 break |
| 688 name = name_and_score[0].strip() | 531 name = name_and_score[0].strip() |
| 689 score = name_and_score[1].strip() | 532 score = name_and_score[1].strip() |
| 690 if version == 'js' or version == 'v8': | 533 if version == 'js' or version == 'v8': |
| 691 version = 'js' | 534 version = 'js' |
| 692 bench_dict = self.test.values_dict[browser][version] | 535 bench_dict = self.test.values_dict[browser][version] |
| 693 bench_dict[name] += [float(score)] | 536 bench_dict[name] += [float(score)] |
| 694 self.test.revision_dict[browser][version][name] += [revision_num] | 537 self.test.revision_dict[browser][version][name] += [revision_num] |
| 695 if not self.test.test_runner.no_upload and should_post_file: | 538 if not self.test.test_runner.no_upload and should_post_file: |
| 696 upload_success = upload_success and self.report_results( | 539 upload_success = upload_success and self.report_results( |
| 697 name, score, browser, version, revision_num, self.SCORE) | 540 name, score, browser, version, revision_num, |
| 541 self.get_score_type(name)) | |
| 698 else: | 542 else: |
| 699 upload_success = False | 543 upload_success = False |
| 700 | 544 |
| 701 f.close() | 545 f.close() |
| 702 self.calculate_geometric_mean(browser, version, revision_num) | 546 self.calculate_geometric_mean(browser, version, revision_num) |
| 703 return upload_success | 547 return upload_success |
| 704 | 548 |
| 705 | 549 |
| 706 class DromaeoTester(Tester): | 550 class DromaeoTester(Tester): |
| 707 DROMAEO_BENCHMARKS = { | 551 DROMAEO_BENCHMARKS = { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 771 @staticmethod | 615 @staticmethod |
| 772 def get_dromaeo_versions(): | 616 def get_dromaeo_versions(): |
| 773 return ['js', 'frog_dom', 'frog_html', 'dart2js_dom', 'dart2js_html'] | 617 return ['js', 'frog_dom', 'frog_html', 'dart2js_dom', 'dart2js_html'] |
| 774 | 618 |
| 775 | 619 |
| 776 class DromaeoTest(RuntimePerformanceTest): | 620 class DromaeoTest(RuntimePerformanceTest): |
| 777 """Runs Dromaeo tests, in the browser.""" | 621 """Runs Dromaeo tests, in the browser.""" |
| 778 def __init__(self, test_runner): | 622 def __init__(self, test_runner): |
| 779 super(DromaeoTest, self).__init__( | 623 super(DromaeoTest, self).__init__( |
| 780 self.name(), | 624 self.name(), |
| 781 filter(lambda x: x != 'ie', BrowserTester.get_browsers()), | 625 BrowserTester.get_browsers(), |
| 782 'browser', | 626 'browser', |
| 783 DromaeoTester.get_dromaeo_versions(), | 627 DromaeoTester.get_dromaeo_versions(), |
| 784 DromaeoTester.get_dromaeo_benchmarks(), test_runner, | 628 DromaeoTester.get_dromaeo_benchmarks(), test_runner, |
| 785 self.DromaeoPerfTester(self), | 629 self.DromaeoPerfTester(self), |
| 786 self.DromaeoFileProcessor(self)) | 630 self.DromaeoFileProcessor(self)) |
| 787 # TODO(vsm): These tester/grapher/processor classes should be | |
| 788 # cleaner to override. | |
| 789 self.grapher = self.DromaeoPerfGrapher(self) | |
| 790 | 631 |
| 791 @staticmethod | 632 @staticmethod |
| 792 def name(): | 633 def name(): |
| 793 return 'dromaeo' | 634 return 'dromaeo' |
| 794 | 635 |
| 795 def is_valid_combination(self, browser, version): | 636 def is_valid_combination(self, browser, version): |
| 796 # TODO(vsm): This avoids a bug in 32-bit Chrome (dartium) | 637 # TODO(vsm): This avoids a bug in 32-bit Chrome (dartium) |
| 797 # running JS dromaeo. | 638 # running JS dromaeo. |
| 798 if browser == 'dartium' and version == 'js': | 639 if browser == 'dartium' and version == 'js': |
| 799 return False | 640 return False |
| 800 # dart:dom has been removed from Dartium. | 641 # dart:dom has been removed from Dartium. |
| 801 if browser == 'dartium' and 'dom' in version: | 642 if browser == 'dartium' and 'dom' in version: |
| 802 return False | 643 return False |
| 803 if browser == 'ff': | |
| 804 # TODO(vsm): We are waiting on a fix from Issue 3152 from dart2js. | |
| 805 return False | |
| 806 return True | 644 return True |
| 807 | 645 |
| 808 class DromaeoPerfGrapher(RuntimePerfGrapher): | |
| 809 def plot_results(self, png_filename): | |
| 810 self.plot_all_perf(png_filename) | |
| 811 self.plot_avg_perf('2' + png_filename) | |
| 812 self.plot_avg_perf('3' + png_filename, ['chrome', 'dartium'], | |
| 813 ['js', 'frog_dom', 'frog_html']) | |
| 814 self.plot_avg_perf('4' + png_filename, ['chrome'], | |
| 815 ['js', 'frog_dom', 'dart2js_dom']) | |
| 816 self.plot_avg_perf('5' + png_filename, ['chrome'], | |
| 817 ['js', 'dart2js_dom', 'dart2js_html']) | |
| 818 self.plot_avg_perf('6' + png_filename, ['chrome'], | |
| 819 ['js', 'frog_dom', 'frog_html', 'dart2js_dom', | |
| 820 'dart2js_html']) | |
| 821 | 646 |
| 822 class DromaeoPerfTester(DromaeoTester): | 647 class DromaeoPerfTester(DromaeoTester): |
| 823 def move_chrome_driver_if_needed(self, browser): | 648 def move_chrome_driver_if_needed(self, browser): |
| 824 """Move the appropriate version of ChromeDriver onto the path. | 649 """Move the appropriate version of ChromeDriver onto the path. |
| 825 TODO(efortuna): This is a total hack because the latest version of Chrome | 650 TODO(efortuna): This is a total hack because the latest version of Chrome |
| 826 (Dartium builds) requires a different version of ChromeDriver, that is | 651 (Dartium builds) requires a different version of ChromeDriver, that is |
| 827 incompatible with the release or beta Chrome and vice versa. Remove these | 652 incompatible with the release or beta Chrome and vice versa. Remove these |
| 828 shenanigans once we're back to both versions of Chrome using the same | 653 shenanigans once we're back to both versions of Chrome using the same |
| 829 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is | 654 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is |
| 830 in the default location (inside depot_tools). | 655 in the default location (inside depot_tools). |
| 831 """ | 656 """ |
| 832 current_dir = os.getcwd() | 657 current_dir = os.getcwd() |
| 833 os.chdir(DART_INSTALL_LOCATION) | 658 os.chdir(DART_INSTALL_LOCATION) |
| 834 self.test.test_runner.run_cmd(['python', os.path.join( | 659 self.test.test_runner.run_cmd(['python', os.path.join( |
| 835 'tools', 'get_drt.py'), '--chromedriver']) | 660 'tools', 'get_archive.py'), 'chromedriver']) |
| 836 path = os.environ['PATH'].split(os.pathsep) | 661 path = os.environ['PATH'].split(os.pathsep) |
| 837 orig_chromedriver_path = os.path.join('tools', 'testing', | 662 orig_chromedriver_path = os.path.join('tools', 'testing', |
| 838 'orig-chromedriver') | 663 'orig-chromedriver') |
| 839 dartium_chromedriver_path = os.path.join('tools', 'testing', | 664 dartium_chromedriver_path = os.path.join('tools', 'testing', |
| 840 'dartium-chromedriver') | 665 'dartium-chromedriver') |
| 841 extension = '' | 666 extension = '' |
| 842 if platform.system() == 'Windows': | 667 if platform.system() == 'Windows': |
| 843 extension = '.exe' | 668 extension = '.exe' |
| 844 | 669 |
| 845 def move_chromedriver(depot_tools, copy_to_depot_tools_dir=True, | 670 def move_chromedriver(depot_tools, copy_to_depot_tools_dir=True, |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 861 shutil.copyfile(from_dir, to_dir) | 686 shutil.copyfile(from_dir, to_dir) |
| 862 | 687 |
| 863 for loc in path: | 688 for loc in path: |
| 864 if 'depot_tools' in loc: | 689 if 'depot_tools' in loc: |
| 865 if browser == 'chrome': | 690 if browser == 'chrome': |
| 866 if os.path.exists(orig_chromedriver_path): | 691 if os.path.exists(orig_chromedriver_path): |
| 867 move_chromedriver(loc) | 692 move_chromedriver(loc) |
| 868 elif browser == 'dartium': | 693 elif browser == 'dartium': |
| 869 if not os.path.exists(dartium_chromedriver_path): | 694 if not os.path.exists(dartium_chromedriver_path): |
| 870 self.test.test_runner.run_cmd(['python', | 695 self.test.test_runner.run_cmd(['python', |
| 871 os.path.join('tools', 'get_drt.py'), '--chromedriver']) | 696 os.path.join('tools', 'get_archive.py'), 'chromedriver']) |
| 872 # Move original chromedriver for storage. | 697 # Move original chromedriver for storage. |
| 873 if not os.path.exists(orig_chromedriver_path): | 698 if not os.path.exists(orig_chromedriver_path): |
| 874 move_chromedriver(loc, copy_to_depot_tools_dir=False) | 699 move_chromedriver(loc, copy_to_depot_tools_dir=False) |
| 875 # Copy Dartium chromedriver into depot_tools | 700 # Copy Dartium chromedriver into depot_tools |
| 876 move_chromedriver(loc, from_path=os.path.join( | 701 move_chromedriver(loc, from_path=os.path.join( |
| 877 dartium_chromedriver_path, 'chromedriver')) | 702 dartium_chromedriver_path, 'chromedriver')) |
| 878 os.chdir(current_dir) | 703 os.chdir(current_dir) |
| 879 | 704 |
| 880 def run_tests(self): | 705 def run_tests(self): |
| 881 """Run dromaeo in the browser.""" | 706 """Run dromaeo in the browser.""" |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 951 if results: | 776 if results: |
| 952 for result in results: | 777 for result in results: |
| 953 r = re.match(result_pattern, result) | 778 r = re.match(result_pattern, result) |
| 954 name = DromaeoTester.legalize_filename(r.group(1).strip(':')) | 779 name = DromaeoTester.legalize_filename(r.group(1).strip(':')) |
| 955 score = float(r.group(2)) | 780 score = float(r.group(2)) |
| 956 bench_dict[name] += [float(score)] | 781 bench_dict[name] += [float(score)] |
| 957 self.test.revision_dict[browser][version][name] += \ | 782 self.test.revision_dict[browser][version][name] += \ |
| 958 [revision_num] | 783 [revision_num] |
| 959 if not self.test.test_runner.no_upload and should_post_file: | 784 if not self.test.test_runner.no_upload and should_post_file: |
| 960 upload_success = upload_success and self.report_results( | 785 upload_success = upload_success and self.report_results( |
| 961 name, score, browser, version, revision_num, self.SCORE) | 786 name, score, browser, version, revision_num, |
| 787 self.get_score_type(name)) | |
| 962 else: | 788 else: |
| 963 upload_success = False | 789 upload_success = False |
| 964 | 790 |
| 965 f.close() | 791 f.close() |
| 966 self.calculate_geometric_mean(browser, version, revision_num) | 792 self.calculate_geometric_mean(browser, version, revision_num) |
| 967 return upload_success | 793 return upload_success |
| 968 | 794 |
| 969 | 795 |
| 970 class DromaeoSizeTest(Test): | 796 class DromaeoSizeTest(Test): |
| 971 """Run tests to determine the compiled file output size of Dromaeo.""" | 797 """Run tests to determine the compiled file output size of Dromaeo.""" |
| 972 def __init__(self, test_runner): | 798 def __init__(self, test_runner): |
| 973 super(DromaeoSizeTest, self).__init__( | 799 super(DromaeoSizeTest, self).__init__( |
| 974 self.name(), | 800 self.name(), |
| 975 ['commandline'], ['dart', 'frog_dom', 'frog_html', | 801 ['commandline'], ['dart', 'frog_dom', 'frog_html', |
| 976 'frog_htmlidiomatic'], | 802 'frog_htmlidiomatic'], |
| 977 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, | 803 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, |
| 978 self.DromaeoSizeTester(self), | 804 self.DromaeoSizeTester(self), |
| 979 self.DromaeoSizeProcessor(self), | 805 self.DromaeoSizeProcessor(self), extra_metrics=['sum']) |
| 980 self.DromaeoSizeGrapher(self), extra_metrics=['sum']) | |
| 981 | 806 |
| 982 @staticmethod | 807 @staticmethod |
| 983 def name(): | 808 def name(): |
| 984 return 'dromaeo-size' | 809 return 'dromaeo-size' |
| 985 | 810 |
| 986 | 811 |
| 987 class DromaeoSizeTester(DromaeoTester): | 812 class DromaeoSizeTester(DromaeoTester): |
| 988 def run_tests(self): | 813 def run_tests(self): |
| 989 # Build tests. | 814 # Build tests. |
| 990 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 815 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1078 if num.find('.') == -1: | 903 if num.find('.') == -1: |
| 1079 num = int(num) | 904 num = int(num) |
| 1080 else: | 905 else: |
| 1081 num = float(num) | 906 num = float(num) |
| 1082 self.test.values_dict['commandline'][variant][metric] += [num] | 907 self.test.values_dict['commandline'][variant][metric] += [num] |
| 1083 self.test.revision_dict['commandline'][variant][metric] += \ | 908 self.test.revision_dict['commandline'][variant][metric] += \ |
| 1084 [revision_num] | 909 [revision_num] |
| 1085 if not self.test.test_runner.no_upload and should_post_file: | 910 if not self.test.test_runner.no_upload and should_post_file: |
| 1086 upload_success = upload_success and self.report_results( | 911 upload_success = upload_success and self.report_results( |
| 1087 metric, num, 'commandline', variant, revision_num, | 912 metric, num, 'commandline', variant, revision_num, |
| 1088 self.CODE_SIZE) | 913 self.get_score_type(metric)) |
| 1089 else: | 914 else: |
| 1090 upload_success = False | 915 upload_success = False |
| 1091 | 916 |
| 1092 f.close() | 917 f.close() |
| 1093 return upload_success | 918 return upload_success |
| 919 | |
| 920 def get_score_type(self, metric): | |
| 921 return self.CODE_SIZE | |
| 1094 | 922 |
| 1095 class DromaeoSizeGrapher(Grapher): | |
| 1096 def plot_results(self, png_filename): | |
| 1097 self.style_and_save_perf_plot( | |
| 1098 'Compiled Dromaeo Sizes', | |
| 1099 'Size (in bytes)', 10, 10, 'lower left', png_filename, | |
| 1100 ['commandline'], | |
| 1101 ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1102 DromaeoTester.DROMAEO_BENCHMARKS.keys()) | |
| 1103 | |
| 1104 self.style_and_save_perf_plot( | |
| 1105 'Compiled Dromaeo Sizes', | |
| 1106 'Size (in bytes)', 10, 10, 'lower left', '2' + png_filename, | |
| 1107 ['commandline'], | |
| 1108 ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1109 [self.test.extra_metrics[0]]) | |
| 1110 | 923 |
| 1111 class CompileTimeAndSizeTest(Test): | 924 class CompileTimeAndSizeTest(Test): |
| 1112 """Run tests to determine how long frogc takes to compile, and the compiled | 925 """Run tests to determine how long frogc takes to compile, and the compiled |
| 1113 file output size of some benchmarking files. | 926 file output size of some benchmarking files. |
| 1114 Note: This test is now 'deprecated' since frog is no longer in the sdk. We | 927 Note: This test is now 'deprecated' since frog is no longer in the sdk. We |
| 1115 just return the last numbers found for frog.""" | 928 just return the last numbers found for frog.""" |
| 1116 def __init__(self, test_runner): | 929 def __init__(self, test_runner): |
| 1117 """Reference to the test_runner object that notifies us when to begin | 930 """Reference to the test_runner object that notifies us when to begin |
| 1118 testing.""" | 931 testing.""" |
| 1119 super(CompileTimeAndSizeTest, self).__init__( | 932 super(CompileTimeAndSizeTest, self).__init__( |
| 1120 self.name(), ['commandline'], ['frog'], ['swarm', 'total'], | 933 self.name(), ['commandline'], ['frog'], ['swarm', 'total'], |
| 1121 test_runner, self.CompileTester(self), | 934 test_runner, self.CompileTester(self), |
| 1122 self.CompileProcessor(self), self.CompileGrapher(self)) | 935 self.CompileProcessor(self)) |
| 1123 self.dart_compiler = os.path.join( | 936 self.dart_compiler = os.path.join( |
| 1124 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | 937 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), |
| 1125 'release', 'ia32'), 'dart-sdk', 'bin', 'frogc') | 938 'release', 'ia32'), 'dart-sdk', 'bin', 'frogc') |
| 1126 _suffix = '' | 939 _suffix = '' |
| 1127 if platform.system() == 'Windows': | 940 if platform.system() == 'Windows': |
| 1128 _suffix = '.exe' | 941 _suffix = '.exe' |
| 1129 self.dart_vm = os.path.join( | 942 self.dart_vm = os.path.join( |
| 1130 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | 943 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), |
| 1131 'release', 'ia32'), 'dart-sdk', 'bin','dart' + _suffix) | 944 'release', 'ia32'), 'dart-sdk', 'bin','dart' + _suffix) |
| 1132 self.failure_threshold = {'swarm' : 100, 'total' : 100} | 945 self.failure_threshold = {'swarm' : 100, 'total' : 100} |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1190 for metric in self.test.values_list: | 1003 for metric in self.test.values_list: |
| 1191 if metric in line: | 1004 if metric in line: |
| 1192 num = tokens[0] | 1005 num = tokens[0] |
| 1193 if num.find('.') == -1: | 1006 if num.find('.') == -1: |
| 1194 num = int(num) | 1007 num = int(num) |
| 1195 else: | 1008 else: |
| 1196 num = float(num) | 1009 num = float(num) |
| 1197 self.test.values_dict['commandline']['frog'][metric] += [num] | 1010 self.test.values_dict['commandline']['frog'][metric] += [num] |
| 1198 self.test.revision_dict['commandline']['frog'][metric] += \ | 1011 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 1199 [revision_num] | 1012 [revision_num] |
| 1200 score_type = self.CODE_SIZE | 1013 score_type = self.get_score_type(metric) |
| 1201 if 'Compiling' in metric or 'Bootstrapping' in metric: | |
| 1202 score_type = self.COMPILE_TIME | |
| 1203 if not self.test.test_runner.no_upload and should_post_file: | 1014 if not self.test.test_runner.no_upload and should_post_file: |
| 1204 if num < self.test.failure_threshold[metric]: | 1015 if num < self.test.failure_threshold[metric]: |
| 1205 num = 0 | 1016 num = 0 |
| 1206 upload_success = upload_success and self.report_results( | 1017 upload_success = upload_success and self.report_results( |
| 1207 metric, num, 'commandline', 'frog', revision_num, | 1018 metric, num, 'commandline', 'frog', revision_num, |
| 1208 score_type) | 1019 score_type) |
| 1209 else: | 1020 else: |
| 1210 upload_success = False | 1021 upload_success = False |
| 1211 if revision_num != 0: | 1022 if revision_num != 0: |
| 1212 for metric in self.test.values_list: | 1023 for metric in self.test.values_list: |
| 1213 try: | 1024 try: |
| 1214 self.test.revision_dict['commandline']['frog'][metric].pop() | 1025 self.test.revision_dict['commandline']['frog'][metric].pop() |
| 1215 self.test.revision_dict['commandline']['frog'][metric] += \ | 1026 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 1216 [revision_num] | 1027 [revision_num] |
| 1217 # Fill in 0 if compilation failed. | 1028 # Fill in 0 if compilation failed. |
| 1218 if self.test.values_dict['commandline']['frog'][metric][-1] < \ | 1029 if self.test.values_dict['commandline']['frog'][metric][-1] < \ |
| 1219 self.test.failure_threshold[metric]: | 1030 self.test.failure_threshold[metric]: |
| 1220 self.test.values_dict['commandline']['frog'][metric] += [0] | 1031 self.test.values_dict['commandline']['frog'][metric] += [0] |
| 1221 self.test.revision_dict['commandline']['frog'][metric] += \ | 1032 self.test.revision_dict['commandline']['frog'][metric] += \ |
| 1222 [revision_num] | 1033 [revision_num] |
| 1223 except IndexError: | 1034 except IndexError: |
| 1224 # We tried to pop from an empty list. This happens if the first | 1035 # We tried to pop from an empty list. This happens if the first |
| 1225 # trace file we encounter is incomplete. | 1036 # trace file we encounter is incomplete. |
| 1226 pass | 1037 pass |
| 1227 | 1038 |
| 1228 f.close() | 1039 f.close() |
| 1229 return upload_success | 1040 return upload_success |
| 1230 | 1041 |
| 1231 class CompileGrapher(Grapher): | 1042 def get_score_type(self, metric): |
| 1232 | 1043 if 'Compiling' in metric or 'Bootstrapping' in metric: |
| 1233 def plot_results(self, png_filename):» » | 1044 return self.COMPILE_TIME |
| 1234 self.style_and_save_perf_plot(» » | 1045 return self.CODE_SIZE |
| 1235 'Compiled frog sizes', 'Size (in bytes)', 10, 10, 'lower left', | |
| 1236 png_filename, ['commandline'], ['frog'], ['swarm', 'total']) | |
| 1237 » » | |
| 1238 | 1046 |
| 1239 class TestBuilder(object): | 1047 class TestBuilder(object): |
| 1240 """Construct the desired test object.""" | 1048 """Construct the desired test object.""" |
| 1241 available_suites = dict((suite.name(), suite) for suite in [ | 1049 available_suites = dict((suite.name(), suite) for suite in [ |
| 1242 CompileTimeAndSizeTest, CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) | 1050 CompileTimeAndSizeTest, CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) |
| 1243 | 1051 |
| 1244 @staticmethod | 1052 @staticmethod |
| 1245 def make_test(test_name, test_runner): | 1053 def make_test(test_name, test_runner): |
| 1246 return TestBuilder.available_suites[test_name](test_runner) | 1054 return TestBuilder.available_suites[test_name](test_runner) |
| 1247 | 1055 |
| 1248 @staticmethod | 1056 @staticmethod |
| 1249 def available_suite_names(): | 1057 def available_suite_names(): |
| 1250 return TestBuilder.available_suites.keys() | 1058 return TestBuilder.available_suites.keys() |
| 1251 | 1059 |
| 1060 def search_for_revision(svn_info_command): | |
| 1061 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | |
| 1062 stderr = subprocess.STDOUT, | |
| 1063 shell = (platform.system() == 'Windows')) | |
| 1064 output, _ = p.communicate() | |
| 1065 for line in output.split('\n'): | |
| 1066 if 'Revision' in line: | |
| 1067 return line.split()[1] | |
| 1068 return -1 | |
| 1069 | |
| 1070 def update_set_of_done_cls(revision_num=None): | |
| 1071 """Update the set of CLs that do not need additional performance runs. | |
| 1072 Args: | |
| 1073 revision_num: an additional number to be added to the 'done set' | |
| 1074 """ | |
| 1075 filename = os.path.join(dirname(abspath(__file__)), 'cached_results.txt') | |
| 1076 if not os.path.exists(filename): | |
| 1077 f = open(filename, 'w') | |
| 1078 results = set() | |
| 1079 pickle.dump(results, f) | |
| 1080 f.close() | |
| 1081 f = open(filename) | |
| 1082 result_set = pickle.load(f) | |
| 1083 if revision_num: | |
| 1084 f.seek(0) | |
| 1085 result_set.add(revision_num) | |
| 1086 pickle.dump(result_set, f) | |
| 1087 f.close() | |
| 1088 return result_set | |
| 1252 | 1089 |
| 1253 def main(): | 1090 def main(): |
| 1254 runner = TestRunner() | 1091 runner = TestRunner() |
| 1255 continuous = runner.parse_args() | 1092 continuous = runner.parse_args() |
| 1256 if continuous: | 1093 if continuous: |
| 1257 while True: | 1094 while True: |
| 1095 results_set = update_set_of_done_cls() | |
| 1258 if runner.has_new_code(): | 1096 if runner.has_new_code(): |
| 1259 runner.run_test_sequence() | 1097 runner.run_test_sequence() |
| 1260 else: | 1098 else: |
| 1099 # Try to get up to 10 runs of each CL, starting with the most recent CL | |
| 1100 # that does not yet have 10 runs. But only perform a set of extra runs | |
| 1101 # at most 10 at a time (get all the extra runs for one CL) before | |
| 1102 # checking to see if new code has been checked in. | |
| 1103 has_run_extra = False | |
| 1104 revision_num = int(search_for_revision(['svn', 'info'])) | |
| 1105 if revision_num == -1: | |
| 1106 revision_num = int(search_for_revision(['git', 'svn', 'info'])) | |
| 1107 | |
| 1108 # No need to track the performance before revision 3000. That's way in | |
| 1109 # the past. | |
| 1110 while revision_num > 3000 and not has_run_extra: | |
|
ricow1
2012/08/06 11:10:03
This will take us back in time really really slow,
Emily Fortuna
2012/08/06 17:39:34
Right, so when this was originally designed, I tho
| |
| 1111 if revision_num not in results_set: | |
| 1112 a_test = TestBuilder.make_test(runner.suite_names[0], runner) | |
| 1113 benchmark_name = a_test.values_list[0] | |
| 1114 platform_name = a_test.platform_list[0] | |
| 1115 variant = a_test.values_dict[platform_name].keys()[0] | |
| 1116 number_of_results = post_results.get_num_results(benchmark_name, | |
| 1117 platform_name, variant, revision_num, | |
| 1118 a_test.file_processor.get_score_type(benchmark_name)) | |
| 1119 if number_of_results < 10 and number_of_results >= 0: | |
| 1120 run = runner.run_test_sequence(revision_num=str(revision_num), | |
| 1121 num_reruns=(10-number_of_results)) | |
| 1122 if run == 0: | |
| 1123 has_run_extra = True | |
| 1124 results_set = update_set_of_done_cls(revision_num) | |
| 1125 elif run == -1: | |
| 1126 # This revision is a broken build. Don't try to run it again. | |
| 1127 results_set = update_set_of_done_cls(revision_num) | |
| 1128 revision_num -= 1 | |
| 1129 # No more extra back-runs to do (for now). Wait for new code. | |
| 1261 time.sleep(200) | 1130 time.sleep(200) |
| 1262 else: | 1131 else: |
| 1263 runner.run_test_sequence() | 1132 runner.run_test_sequence() |
| 1264 | 1133 |
| 1265 if __name__ == '__main__': | 1134 if __name__ == '__main__': |
| 1266 main() | 1135 main() |
| OLD | NEW |