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 import optparse | 9 import optparse |
| 10 import os | 10 import os |
| 11 from os.path import dirname, abspath | 11 from os.path import dirname, abspath |
| 12 import pickle | 12 import pickle |
| 13 import platform | 13 import platform |
| 14 import random | |
| 14 import re | 15 import re |
| 15 import shutil | 16 import shutil |
| 16 import stat | 17 import stat |
| 17 import subprocess | 18 import subprocess |
| 18 import sys | 19 import sys |
| 19 import time | 20 import time |
| 20 | 21 |
| 21 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) | 22 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) |
| 22 TOP_LEVEL_DIR = abspath(os.path.join(dirname(abspath(__file__)), '..', '..', | 23 TOP_LEVEL_DIR = abspath(os.path.join(dirname(abspath(__file__)), '..', '..', |
| 23 '..')) | 24 '..')) |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 51 stdout to | 52 stdout to |
| 52 append: True if we want to append to the file instead of overwriting it | 53 append: True if we want to append to the file instead of overwriting it |
| 53 std_in: a string that should be written to the process executing to | 54 std_in: a string that should be written to the process executing to |
| 54 interact with it (if needed)""" | 55 interact with it (if needed)""" |
| 55 if self.verbose: | 56 if self.verbose: |
| 56 print ' '.join(cmd_list) | 57 print ' '.join(cmd_list) |
| 57 out = subprocess.PIPE | 58 out = subprocess.PIPE |
| 58 if outfile: | 59 if outfile: |
| 59 mode = 'w' | 60 mode = 'w' |
| 60 if append: | 61 if append: |
| 61 mode = 'a' | 62 mode = 'a+' |
| 62 out = open(outfile, mode) | 63 out = open(outfile, mode) |
| 63 if append: | 64 if append: |
| 64 # Annoying Windows "feature" -- append doesn't actually append unless | 65 # Annoying Windows "feature" -- append doesn't actually append unless |
| 65 # you explicitly go to the end of the file. | 66 # you explicitly go to the end of the file. |
| 66 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html | 67 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html |
| 67 out.seek(0, os.SEEK_END) | 68 out.seek(0, os.SEEK_END) |
| 68 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, | 69 p = subprocess.Popen(cmd_list, stdout = out, stderr=subprocess.PIPE, |
| 69 stdin=subprocess.PIPE, shell=self.has_shell) | 70 stdin=subprocess.PIPE, shell=self.has_shell) |
| 70 output, stderr = p.communicate(std_in) | 71 output, stderr = p.communicate(std_in) |
| 71 if output: | 72 if output: |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 87 os.chdir(DART_REPO_LOC) | 88 os.chdir(DART_REPO_LOC) |
| 88 results, _ = self.run_cmd(['svn', 'st']) | 89 results, _ = self.run_cmd(['svn', 'st']) |
| 89 for line in results.split('\n'): | 90 for line in results.split('\n'): |
| 90 if line.startswith('?'): | 91 if line.startswith('?'): |
| 91 to_remove = line.split()[1] | 92 to_remove = line.split()[1] |
| 92 if os.path.isdir(to_remove): | 93 if os.path.isdir(to_remove): |
| 93 shutil.rmtree(to_remove)#, ignore_errors=True) | 94 shutil.rmtree(to_remove)#, ignore_errors=True) |
| 94 else: | 95 else: |
| 95 os.remove(to_remove) | 96 os.remove(to_remove) |
| 96 | 97 |
| 97 def get_archive(archive_name): | 98 def get_archive(self, archive_name): |
| 98 """Wrapper around the pulling down a specific archive from Google Storage. | 99 """Wrapper around the pulling down a specific archive from Google Storage. |
| 99 Adds a specific revision argument as needed. | 100 Adds a specific revision argument as needed. |
| 100 Returns: The stderr from running this command.""" | 101 Returns: The stderr from running this command.""" |
| 101 cmd = ['python', os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py'), | 102 cmd = ['python', os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py'), |
| 102 archive_name] | 103 archive_name] |
| 103 if self.current_revision_num != -1: | 104 if self.current_revision_num != -1: |
| 104 cmd += ['-r', revision_num] | 105 cmd += ['-r', self.current_revision_num] |
| 105 _, stderr = self.test.test_runner.run_cmd(cmd) | 106 _, stderr = self.run_cmd(cmd) |
| 106 return stderr | 107 return stderr |
| 107 | 108 |
| 108 def sync_and_build(self, suites, revision_num=''): | 109 def sync_and_build(self, suites, revision_num=''): |
| 109 """Make sure we have the latest version of of the repo, and build it. We | 110 """Make sure we have the latest version of of the repo, and build it. We |
| 110 begin and end standing in DART_REPO_LOC. | 111 begin and end standing in DART_REPO_LOC. |
| 111 | 112 |
| 112 Args: | 113 Args: |
| 113 suites: The set of suites that we wish to build. | 114 suites: The set of suites that we wish to build. |
| 114 | 115 |
| 115 Returns: | 116 Returns: |
| 116 err_code = 1 if there was a problem building.""" | 117 err_code = 1 if there was a problem building.""" |
| 117 os.chdir(dirname(DART_REPO_LOC)) | 118 os.chdir(dirname(DART_REPO_LOC)) |
| 118 self.clear_out_unversioned_files() | 119 self.clear_out_unversioned_files() |
| 119 if revision_num == '': | 120 if revision_num == '': |
| 120 self.run_cmd(['gclient', 'sync']) | 121 self.run_cmd(['gclient', 'sync']) |
| 121 else: | 122 else: |
| 122 self.run_cmd(['gclient', 'sync', '-r', revision_num, '-t']) | 123 self.run_cmd(['gclient', 'sync', '-r', revision_num, '-t']) |
| 123 | 124 |
| 124 shutil.copytree(os.path.join(TOP_LEVEL_DIR, 'internal'), | 125 shutil.copytree(os.path.join(TOP_LEVEL_DIR, 'internal'), |
| 125 os.path.join(DART_REPO_LOC, 'internal')) | 126 os.path.join(DART_REPO_LOC, 'internal')) |
| 126 shutil.copy(os.path.join(TOP_LEVEL_DIR, 'tools', 'get_archive.py'), | 127 shutil.copy(os.path.join(TOP_LEVEL_DIR, 'tools', 'get_archive.py'), |
| 127 os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py')) | 128 os.path.join(DART_REPO_LOC, 'tools', 'get_archive.py')) |
| 128 shutil.copy( | 129 shutil.copy( |
| 129 os.path.join(TOP_LEVEL_DIR, 'tools', 'testing', 'run_selenium.py'), | 130 os.path.join(TOP_LEVEL_DIR, 'tools', 'testing', 'run_selenium.py'), |
| 130 os.path.join(DART_REPO_LOC, 'tools', 'testing', 'run_selenium.py')) | 131 os.path.join(DART_REPO_LOC, 'tools', 'testing', 'run_selenium.py')) |
| 131 | 132 |
| 132 if revision_num == '': | 133 if revision_num == '': |
| 133 revision_num = search_for_revision(['svn', 'info']) | 134 revision_num = search_for_revision() |
| 134 if revision_num == -1: | |
| 135 revision_num = search_for_revision(['git', 'svn', 'info']) | |
| 136 | 135 |
| 137 self.current_revision_num = revision_num | 136 self.current_revision_num = revision_num |
| 138 stderr = get_archive('sdk') | 137 stderr = self.get_archive('sdk') |
| 139 if not os.path.exists(get_archive_path) or 'InvalidUriError' in stderr: | 138 if not os.path.exists(os.path.join( |
| 139 DART_REPO_LOC, 'tools', 'get_archive.py')) \ | |
| 140 or 'InvalidUriError' in stderr: | |
| 140 # Couldn't find the SDK on Google Storage. Build it locally. | 141 # Couldn't find the SDK on Google Storage. Build it locally. |
| 141 | 142 |
| 142 # On Windows, the output directory is marked as "Read Only," which causes | 143 # On Windows, the output directory is marked as "Read Only," which causes |
| 143 # an error to be thrown when we use shutil.rmtree. This helper function | 144 # an error to be thrown when we use shutil.rmtree. This helper function |
| 144 # changes the permissions so we can still delete the directory. | 145 # changes the permissions so we can still delete the directory. |
| 145 def on_rm_error(func, path, exc_info): | 146 def on_rm_error(func, path, exc_info): |
| 146 if os.path.exists(path): | 147 if os.path.exists(path): |
| 147 os.chmod(path, stat.S_IWRITE) | 148 os.chmod(path, stat.S_IWRITE) |
| 148 os.unlink(path) | 149 os.unlink(path) |
| 149 # TODO(efortuna): Currently always building ia32 architecture because we | 150 # TODO(efortuna): Currently always building ia32 architecture because we |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 | 241 |
| 241 def run_test_sequence(self, revision_num='', num_reruns=1): | 242 def run_test_sequence(self, revision_num='', num_reruns=1): |
| 242 """Run the set of commands to (possibly) build, run, and post the results | 243 """Run the set of commands to (possibly) build, run, and post the results |
| 243 of our tests. Returns 0 on a successful run, 1 if we fail to post results or | 244 of our tests. Returns 0 on a successful run, 1 if we fail to post results or |
| 244 the run failed, -1 if the build is broken. | 245 the run failed, -1 if the build is broken. |
| 245 """ | 246 """ |
| 246 suites = [] | 247 suites = [] |
| 247 success = True | 248 success = True |
| 248 if not self.no_build and self.sync_and_build(suites, revision_num) == 1: | 249 if not self.no_build and self.sync_and_build(suites, revision_num) == 1: |
| 249 return -1 # The build is broken. | 250 return -1 # The build is broken. |
| 250 | 251 |
| 251 for name in self.suite_names: | 252 for name in self.suite_names: |
| 252 for run in range(num_reruns): | 253 for run in range(num_reruns): |
| 253 suites += [TestBuilder.make_test(name, self)] | 254 suites += [TestBuilder.make_test(name, self)] |
| 254 | 255 |
| 255 for test in suites: | 256 for test in suites: |
| 256 success = success and test.run() | 257 success = success and test.run() |
| 257 if success: | 258 if success: |
| 258 return 0 | 259 return 0 |
| 259 else: | 260 else: |
| 260 return 1 | 261 return 1 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 366 'dartium', 'LAST_VERSION') | 367 'dartium', 'LAST_VERSION') |
| 367 version_file = open(version_file_name, 'r') | 368 version_file = open(version_file_name, 'r') |
| 368 version = version_file.read().split('.')[-2] | 369 version = version_file.read().split('.')[-2] |
| 369 version_file.close() | 370 version_file.close() |
| 370 return version | 371 return version |
| 371 | 372 |
| 372 if browser and browser == 'dartium': | 373 if browser and browser == 'dartium': |
| 373 revision = get_dartium_revision() | 374 revision = get_dartium_revision() |
| 374 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | 375 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) |
| 375 else: | 376 else: |
| 376 revision = search_for_revision(['svn', 'info']) | 377 revision = search_for_revision() |
| 377 if revision == -1: | |
| 378 revision = search_for_revision(['git', 'svn', 'info']) | |
| 379 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) | 378 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) |
| 380 | 379 |
| 381 | 380 |
| 382 class Processor(object): | 381 class Processor(object): |
| 383 """The base level vistor class that processes tests. It contains convenience | 382 """The base level vistor class that processes tests. It contains convenience |
| 384 methods that many File Processor objects use. Any class that would like to be | 383 methods that many File Processor objects use. Any class that would like to be |
| 385 a ProcessorVisitor must implement the process_file() method.""" | 384 a ProcessorVisitor must implement the process_file() method.""" |
| 386 | 385 |
| 387 SCORE = 'Score' | 386 SCORE = 'Score' |
| 388 COMPILE_TIME = 'CompileTime' | 387 COMPILE_TIME = 'CompileTime' |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 417 variant: Specifies whether the data was about generated Frog, js, a | 416 variant: Specifies whether the data was about generated Frog, js, a |
| 418 combination of both, or Dart depending on the test. | 417 combination of both, or Dart depending on the test. |
| 419 revision_number: The revision of the code (and sometimes the revision of | 418 revision_number: The revision of the code (and sometimes the revision of |
| 420 dartium). | 419 dartium). |
| 421 | 420 |
| 422 Returns: True if the post was successful file.""" | 421 Returns: True if the post was successful file.""" |
| 423 return post_results.report_results(benchmark_name, score, platform, variant, | 422 return post_results.report_results(benchmark_name, score, platform, variant, |
| 424 revision_number, metric) | 423 revision_number, metric) |
| 425 | 424 |
| 426 def calculate_geometric_mean(self, platform, variant, svn_revision): | 425 def calculate_geometric_mean(self, platform, variant, svn_revision): |
| 427 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | 426 """Calculate the aggregate geometric mean for JS and dart2js benchmark sets, |
| 428 given two benchmark dictionaries.""" | 427 given two benchmark dictionaries.""" |
| 429 geo_mean = 0 | 428 geo_mean = 0 |
| 430 if self.test.is_valid_combination(platform, variant): | 429 if self.test.is_valid_combination(platform, variant): |
| 431 for benchmark in self.test.values_list: | 430 for benchmark in self.test.values_list: |
| 432 geo_mean += math.log( | 431 geo_mean += math.log( |
| 433 self.test.values_dict[platform][variant][benchmark][ | 432 self.test.values_dict[platform][variant][benchmark][ |
| 434 len(self.test.values_dict[platform][variant][benchmark]) - 1]) | 433 len(self.test.values_dict[platform][variant][benchmark]) - 1]) |
| 435 | 434 |
| 436 self.test.values_dict[platform][variant]['Geo-Mean'] += \ | 435 self.test.values_dict[platform][variant]['Geo-Mean'] += \ |
| 437 [math.pow(math.e, geo_mean / len(self.test.values_list))] | 436 [math.pow(math.e, geo_mean / len(self.test.values_list))] |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 491 | 490 |
| 492 class CommonBrowserTest(RuntimePerformanceTest): | 491 class CommonBrowserTest(RuntimePerformanceTest): |
| 493 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the | 492 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the |
| 494 browser.""" | 493 browser.""" |
| 495 | 494 |
| 496 def __init__(self, test_runner): | 495 def __init__(self, test_runner): |
| 497 """Args: | 496 """Args: |
| 498 test_runner: Reference to the object that notifies us when to run.""" | 497 test_runner: Reference to the object that notifies us when to run.""" |
| 499 super(CommonBrowserTest, self).__init__( | 498 super(CommonBrowserTest, self).__init__( |
| 500 self.name(), BrowserTester.get_browsers(False), | 499 self.name(), BrowserTester.get_browsers(False), |
| 501 'browser', ['js', 'frog', 'dart2js'], | 500 'browser', ['js', 'dart2js'], |
| 502 self.get_standalone_benchmarks(), test_runner, | 501 self.get_standalone_benchmarks(), test_runner, |
| 503 self.CommonBrowserTester(self), | 502 self.CommonBrowserTester(self), |
| 504 self.CommonBrowserFileProcessor(self)) | 503 self.CommonBrowserFileProcessor(self)) |
| 505 | 504 |
| 506 @staticmethod | 505 @staticmethod |
| 507 def name(): | 506 def name(): |
| 508 return 'browser-perf' | 507 return 'browser-perf' |
| 509 | 508 |
| 510 @staticmethod | 509 @staticmethod |
| 511 def get_standalone_benchmarks(): | 510 def get_standalone_benchmarks(): |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 658 def get_dromaeo_benchmarks(): | 657 def get_dromaeo_benchmarks(): |
| 659 valid = DromaeoTester.get_valid_dromaeo_tags() | 658 valid = DromaeoTester.get_valid_dromaeo_tags() |
| 660 benchmarks = reduce(lambda l1,l2: l1+l2, | 659 benchmarks = reduce(lambda l1,l2: l1+l2, |
| 661 [tests for (tag, tests) in | 660 [tests for (tag, tests) in |
| 662 DromaeoTester.DROMAEO_BENCHMARKS.values() | 661 DromaeoTester.DROMAEO_BENCHMARKS.values() |
| 663 if tag in valid]) | 662 if tag in valid]) |
| 664 return map(DromaeoTester.legalize_filename, benchmarks) | 663 return map(DromaeoTester.legalize_filename, benchmarks) |
| 665 | 664 |
| 666 @staticmethod | 665 @staticmethod |
| 667 def get_dromaeo_versions(): | 666 def get_dromaeo_versions(): |
| 668 return ['js', 'dart2js_dom', 'dart2js_html'] | 667 return ['js', 'dart2js_html'] |
| 669 | 668 |
| 670 | 669 |
| 671 class DromaeoTest(RuntimePerformanceTest): | 670 class DromaeoTest(RuntimePerformanceTest): |
| 672 """Runs Dromaeo tests, in the browser.""" | 671 """Runs Dromaeo tests, in the browser.""" |
| 673 def __init__(self, test_runner): | 672 def __init__(self, test_runner): |
| 674 super(DromaeoTest, self).__init__( | 673 super(DromaeoTest, self).__init__( |
| 675 self.name(), | 674 self.name(), |
| 676 BrowserTester.get_browsers(True), | 675 BrowserTester.get_browsers(True), |
| 677 'browser', | 676 'browser', |
| 678 DromaeoTester.get_dromaeo_versions(), | 677 DromaeoTester.get_dromaeo_versions(), |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 700 """Move the appropriate version of ChromeDriver onto the path. | 699 """Move the appropriate version of ChromeDriver onto the path. |
| 701 TODO(efortuna): This is a total hack because the latest version of Chrome | 700 TODO(efortuna): This is a total hack because the latest version of Chrome |
| 702 (Dartium builds) requires a different version of ChromeDriver, that is | 701 (Dartium builds) requires a different version of ChromeDriver, that is |
| 703 incompatible with the release or beta Chrome and vice versa. Remove these | 702 incompatible with the release or beta Chrome and vice versa. Remove these |
| 704 shenanigans once we're back to both versions of Chrome using the same | 703 shenanigans once we're back to both versions of Chrome using the same |
| 705 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is | 704 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is |
| 706 in the default location (inside depot_tools). | 705 in the default location (inside depot_tools). |
| 707 """ | 706 """ |
| 708 current_dir = os.getcwd() | 707 current_dir = os.getcwd() |
| 709 self.test.test_runner.get_archive('chromedriver') | 708 self.test.test_runner.get_archive('chromedriver') |
| 710 self.test.test_runner.run_cmd(['python', os.path.join( | |
| 711 path = os.environ['PATH'].split(os.pathsep) | 709 path = os.environ['PATH'].split(os.pathsep) |
| 712 orig_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 'testing', | 710 orig_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 'testing', |
| 713 'orig-chromedriver') | 711 'orig-chromedriver') |
| 714 dartium_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', | 712 dartium_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', |
| 715 'testing', | 713 'testing', |
| 716 'dartium-chromedriver') | 714 'dartium-chromedriver') |
| 717 extension = '' | 715 extension = '' |
| 718 if platform.system() == 'Windows': | 716 if platform.system() == 'Windows': |
| 719 extension = '.exe' | 717 extension = '.exe' |
| 720 | 718 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 841 upload_success = upload_success and self.report_results( | 839 upload_success = upload_success and self.report_results( |
| 842 name, score, browser, version, revision_num, | 840 name, score, browser, version, revision_num, |
| 843 self.get_score_type(name)) | 841 self.get_score_type(name)) |
| 844 else: | 842 else: |
| 845 upload_success = False | 843 upload_success = False |
| 846 | 844 |
| 847 f.close() | 845 f.close() |
| 848 self.calculate_geometric_mean(browser, version, revision_num) | 846 self.calculate_geometric_mean(browser, version, revision_num) |
| 849 return upload_success | 847 return upload_success |
| 850 | 848 |
| 851 | |
| 852 class DromaeoSizeTest(Test): | |
| 853 """Run tests to determine the compiled file output size of Dromaeo.""" | |
| 854 def __init__(self, test_runner): | |
| 855 super(DromaeoSizeTest, self).__init__( | |
| 856 self.name(), | |
| 857 ['commandline'], ['dart', 'frog_dom', 'frog_html', | |
| 858 'frog_htmlidiomatic'], | |
| 859 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, | |
| 860 self.DromaeoSizeTester(self), | |
| 861 self.DromaeoSizeProcessor(self), extra_metrics=['sum']) | |
| 862 | |
| 863 @staticmethod | |
| 864 def name(): | |
| 865 return 'dromaeo-size' | |
| 866 | |
| 867 | |
| 868 class DromaeoSizeTester(DromaeoTester): | |
| 869 def run_tests(self): | |
| 870 # Build tests. | |
| 871 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | |
| 872 current_path = os.getcwd() | |
| 873 os.chdir(dromaeo_path) | |
| 874 self.test.test_runner.run_cmd( | |
| 875 ['python', os.path.join('generate_dart2js_tests.py')]) | |
| 876 self.test.test_runner.get_archive('dartium') | |
| 877 os.chdir(current_path) | |
| 878 | |
| 879 self.test.trace_file = os.path.join(TOP_LEVEL_DIR, | |
| 880 'tools', 'testing', 'perf_testing', self.test.result_folder_name, | |
| 881 self.test.result_folder_name + self.test.cur_time) | |
| 882 self.add_svn_revision_to_trace(self.test.trace_file) | |
| 883 | |
| 884 variants = [ | |
| 885 ('frog_dom', ''), | |
| 886 ('frog_html', '-html'), | |
| 887 ('frog_htmlidiomatic', '-htmlidiomatic')] | |
| 888 | |
| 889 test_path = os.path.join(dromaeo_path, 'tests') | |
| 890 frog_path = os.path.join(test_path, 'frog') | |
| 891 total_size = {} | |
| 892 for (variant, _) in variants: | |
| 893 total_size[variant] = 0 | |
| 894 total_dart_size = 0 | |
| 895 for suite in DromaeoTester.DROMAEO_BENCHMARKS.keys(): | |
| 896 dart_size = 0 | |
| 897 try: | |
| 898 dart_size = os.path.getsize(os.path.join(test_path, | |
| 899 'dom-%s.dart' % suite)) | |
| 900 except OSError: | |
| 901 pass #If compilation failed, continue on running other tests. | |
| 902 | |
| 903 total_dart_size += dart_size | |
| 904 self.test.test_runner.run_cmd( | |
| 905 ['echo', 'Size (dart, %s): %s' % (suite, str(dart_size))], | |
| 906 self.test.trace_file, append=True) | |
| 907 | |
| 908 for (variant, suffix) in variants: | |
| 909 name = 'dom-%s%s.dart.js' % (suite, suffix) | |
| 910 js_size = 0 | |
| 911 try: | |
| 912 # TODO(vsm): Strip comments at least. Consider compression. | |
| 913 js_size = os.path.getsize(os.path.join(frog_path, name)) | |
| 914 except OSError: | |
| 915 pass #If compilation failed, continue on running other tests. | |
| 916 | |
| 917 total_size[variant] += js_size | |
| 918 self.test.test_runner.run_cmd( | |
| 919 ['echo', 'Size (%s, %s): %s' % (variant, suite, str(js_size))], | |
| 920 self.test.trace_file, append=True) | |
| 921 | |
| 922 self.test.test_runner.run_cmd( | |
| 923 ['echo', 'Size (dart, %s): %s' % (total_dart_size, | |
| 924 self.test.extra_metrics[0])], | |
| 925 self.test.trace_file, append=True) | |
| 926 for (variant, _) in variants: | |
| 927 self.test.test_runner.run_cmd( | |
| 928 ['echo', 'Size (%s, %s): %s' % (variant, self.test.extra_metrics[0], | |
| 929 total_size[variant])], | |
| 930 self.test.trace_file, append=True) | |
| 931 | |
| 932 | |
| 933 class DromaeoSizeProcessor(Processor): | |
| 934 def process_file(self, afile, should_post_file): | |
| 935 """Pull all the relevant information out of a given tracefile. | |
| 936 | |
| 937 Args: | |
| 938 afile: is the filename string we will be processing. | |
| 939 Returns: True if we successfully posted our data to storage.""" | |
| 940 os.chdir(os.path.join(TOP_LEVEL_DIR, 'tools', | |
| 941 'testing', 'perf_testing')) | |
| 942 f = self.open_trace_file(afile, should_post_file) | |
| 943 tabulate_data = False | |
| 944 revision_num = 0 | |
| 945 revision_pattern = r'Revision: (\d+)' | |
| 946 result_pattern = r'Size \((\w+), ([a-zA-Z0-9-]+)\): (\d+)' | |
| 947 | |
| 948 upload_success = True | |
| 949 for line in f.readlines(): | |
| 950 rev = re.match(revision_pattern, line.strip()) | |
| 951 if rev: | |
| 952 revision_num = int(rev.group(1)) | |
| 953 continue | |
| 954 | |
| 955 result = re.match(result_pattern, line.strip()) | |
| 956 if result: | |
| 957 variant = result.group(1) | |
| 958 metric = result.group(2) | |
| 959 num = result.group(3) | |
| 960 if num.find('.') == -1: | |
| 961 num = int(num) | |
| 962 else: | |
| 963 num = float(num) | |
| 964 self.test.values_dict['commandline'][variant][metric] += [num] | |
| 965 self.test.revision_dict['commandline'][variant][metric] += \ | |
| 966 [revision_num] | |
| 967 if not self.test.test_runner.no_upload and should_post_file: | |
| 968 upload_success = upload_success and self.report_results( | |
| 969 metric, num, 'commandline', variant, revision_num, | |
| 970 self.get_score_type(metric)) | |
| 971 else: | |
| 972 upload_success = False | |
| 973 | |
| 974 f.close() | |
| 975 return upload_success | |
| 976 | |
| 977 def get_score_type(self, metric): | |
| 978 return self.CODE_SIZE | |
| 979 | |
| 980 | |
| 981 class CompileTimeAndSizeTest(Test): | |
| 982 """Run tests to determine how long frogc takes to compile, and the compiled | |
| 983 file output size of some benchmarking files. | |
| 984 Note: This test is now 'deprecated' since frog is no longer in the sdk. We | |
| 985 just return the last numbers found for frog.""" | |
| 986 def __init__(self, test_runner): | |
| 987 """Reference to the test_runner object that notifies us when to begin | |
| 988 testing.""" | |
| 989 super(CompileTimeAndSizeTest, self).__init__( | |
| 990 self.name(), ['commandline'], ['dart2js'], ['swarm'], | |
| 991 test_runner, self.CompileTester(self), | |
| 992 self.CompileProcessor(self)) | |
| 993 self.dart_compiler = os.path.join( | |
| 994 DART_REPO_LOC, utils.GetBuildRoot(utils.GuessOS(), | |
| 995 'release', 'ia32'), 'dart-sdk', 'bin', 'dart2js') | |
| 996 _suffix = '' | |
| 997 if platform.system() == 'Windows': | |
| 998 _suffix = '.exe' | |
| 999 self.failure_threshold = {'swarm' : 100} | |
| 1000 | |
| 1001 @staticmethod | |
| 1002 def name(): | |
| 1003 return 'time-size' | |
| 1004 | |
| 1005 class CompileTester(Tester): | |
| 1006 def run_tests(self): | |
| 1007 self.test.trace_file = os.path.join(TOP_LEVEL_DIR, | |
| 1008 'tools', 'testing', 'perf_testing', | |
| 1009 self.test.result_folder_name, | |
| 1010 self.test.result_folder_name + self.test.cur_time) | |
| 1011 | |
| 1012 self.add_svn_revision_to_trace(self.test.trace_file) | |
| 1013 | |
| 1014 self.test.test_runner.run_cmd( | |
| 1015 ['./xcodebuild/ReleaseIA32/dart-sdk/dart2js', '-c', '-o', | |
| 1016 'swarm-result', os.path.join('samples', 'swarm', 'swarm.dart')]) | |
| 1017 swarm_size = 0 | |
| 1018 try: | |
| 1019 swarm_size = os.path.getsize('swarm-result') | |
| 1020 except OSError: | |
| 1021 pass #If compilation failed, continue on running other tests. | |
| 1022 | |
| 1023 self.test.test_runner.run_cmd( | |
| 1024 ['echo', '%d Generated checked swarm size' % swarm_size], | |
| 1025 self.test.trace_file, append=True) | |
| 1026 | |
| 1027 class CompileProcessor(Processor): | |
| 1028 def process_file(self, afile, should_post_file): | |
| 1029 """Pull all the relevant information out of a given tracefile. | |
| 1030 | |
| 1031 Args: | |
| 1032 afile: is the filename string we will be processing. | |
| 1033 Returns: True if we successfully posted our data to storage.""" | |
| 1034 os.chdir(os.path.join(TOP_LEVEL_DIR, 'tools', | |
| 1035 'testing', 'perf_testing')) | |
| 1036 f = self.open_trace_file(afile, should_post_file) | |
| 1037 tabulate_data = False | |
| 1038 revision_num = 0 | |
| 1039 upload_success = True | |
| 1040 for line in f.readlines(): | |
| 1041 tokens = line.split() | |
| 1042 if 'Revision' in line: | |
| 1043 revision_num = int(line.split()[1]) | |
| 1044 else: | |
| 1045 for metric in self.test.values_list: | |
| 1046 if metric in line: | |
| 1047 num = tokens[0] | |
| 1048 if num.find('.') == -1: | |
| 1049 num = int(num) | |
| 1050 else: | |
| 1051 num = float(num) | |
| 1052 self.test.values_dict['commandline']['dart2js'][metric] += [num] | |
| 1053 self.test.revision_dict['commandline']['dart2js'][metric] += \ | |
| 1054 [revision_num] | |
| 1055 score_type = self.get_score_type(metric) | |
| 1056 if not self.test.test_runner.no_upload and should_post_file: | |
| 1057 if num < self.test.failure_threshold[metric]: | |
| 1058 num = 0 | |
| 1059 upload_success = upload_success and self.report_results( | |
| 1060 metric, num, 'commandline', 'dart2js', revision_num, | |
| 1061 score_type) | |
| 1062 else: | |
| 1063 upload_success = False | |
| 1064 if revision_num != 0: | |
| 1065 for metric in self.test.values_list: | |
| 1066 try: | |
| 1067 self.test.revision_dict['commandline']['dart2js'][metric].pop() | |
| 1068 self.test.revision_dict['commandline']['dart2js'][metric] += \ | |
| 1069 [revision_num] | |
| 1070 # Fill in 0 if compilation failed. | |
| 1071 if self.test.values_dict['commandline']['dart2js'][metric][-1] < \ | |
| 1072 self.test.failure_threshold[metric]: | |
| 1073 self.test.values_dict['commandline']['dart2js'][metric] += [0] | |
| 1074 self.test.revision_dict['commandline']['dart2js'][metric] += \ | |
| 1075 [revision_num] | |
| 1076 except IndexError: | |
| 1077 # We tried to pop from an empty list. This happens if the first | |
| 1078 # trace file we encounter is incomplete. | |
| 1079 pass | |
| 1080 | |
| 1081 f.close() | |
| 1082 return upload_success | |
| 1083 | |
| 1084 def get_score_type(self, metric): | |
| 1085 if 'Compiling' in metric or 'Bootstrapping' in metric: | |
| 1086 return self.COMPILE_TIME | |
| 1087 return self.CODE_SIZE | |
| 1088 | |
| 1089 class TestBuilder(object): | 849 class TestBuilder(object): |
| 1090 """Construct the desired test object.""" | 850 """Construct the desired test object.""" |
| 1091 available_suites = dict((suite.name(), suite) for suite in [ | 851 available_suites = dict((suite.name(), suite) for suite in [ |
| 1092 CompileTimeAndSizeTest, CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) | 852 CommonBrowserTest, DromaeoTest]) |
| 1093 | 853 |
| 1094 @staticmethod | 854 @staticmethod |
| 1095 def make_test(test_name, test_runner): | 855 def make_test(test_name, test_runner): |
| 1096 return TestBuilder.available_suites[test_name](test_runner) | 856 return TestBuilder.available_suites[test_name](test_runner) |
| 1097 | 857 |
| 1098 @staticmethod | 858 @staticmethod |
| 1099 def available_suite_names(): | 859 def available_suite_names(): |
| 1100 return TestBuilder.available_suites.keys() | 860 return TestBuilder.available_suites.keys() |
| 1101 | 861 |
| 1102 def search_for_revision(svn_info_command): | 862 def search_for_revision(directory = None): |
| 1103 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | 863 """Find the current revision number in the desired directory. If directory is |
| 1104 stderr = subprocess.STDOUT, | 864 None, find the revision number in the current directory.""" |
| 1105 shell = (platform.system() == 'Windows')) | 865 def find_revision(svn_info_command): |
| 1106 output, _ = p.communicate() | 866 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, |
| 1107 for line in output.split('\n'): | 867 stderr = subprocess.STDOUT, |
| 1108 if 'Revision' in line: | 868 shell = (platform.system() == 'Windows')) |
| 1109 return line.split()[1] | 869 output, _ = p.communicate() |
| 1110 return -1 | 870 for line in output.split('\n'): |
| 871 if 'Revision' in line: | |
| 872 return int(line.split()[1]) | |
| 873 return -1 | |
| 874 | |
| 875 cwd = os.getcwd() | |
| 876 if not directory: | |
| 877 directory = cwd | |
| 878 os.chdir(directory) | |
| 879 revision_num = int(find_revision(['svn', 'info'])) | |
| 880 if revision_num == -1: | |
| 881 revision_num = int(find_revision(['git', 'svn', 'info'])) | |
| 882 os.chdir(cwd) | |
| 883 return revision_num | |
| 1111 | 884 |
| 1112 def update_set_of_done_cls(revision_num=None): | 885 def update_set_of_done_cls(revision_num=None): |
| 1113 """Update the set of CLs that do not need additional performance runs. | 886 """Update the set of CLs that do not need additional performance runs. |
| 1114 Args: | 887 Args: |
| 1115 revision_num: an additional number to be added to the 'done set' | 888 revision_num: an additional number to be added to the 'done set' |
| 1116 """ | 889 """ |
| 1117 filename = os.path.join(TOP_LEVEL_DIR, 'cached_results.txt') | 890 filename = os.path.join(TOP_LEVEL_DIR, 'cached_results.txt') |
| 1118 if not os.path.exists(filename): | 891 if not os.path.exists(filename): |
| 1119 f = open(filename, 'w') | 892 f = open(filename, 'w') |
| 1120 results = set() | 893 results = set() |
| 1121 pickle.dump(results, f) | 894 pickle.dump(results, f) |
| 1122 f.close() | 895 f.close() |
| 1123 f = open(filename, 'r+') | 896 f = open(filename, 'r+') |
| 1124 result_set = pickle.load(f) | 897 result_set = pickle.load(f) |
| 1125 if revision_num: | 898 if revision_num: |
| 1126 f.seek(0) | 899 f.seek(0) |
| 1127 result_set.add(revision_num) | 900 result_set.add(revision_num) |
| 1128 pickle.dump(result_set, f) | 901 pickle.dump(result_set, f) |
| 1129 f.close() | 902 f.close() |
| 1130 return result_set | 903 return result_set |
| 1131 | 904 |
| 905 def fill_in_back_history(results_set, runner): | |
| 906 """ Fill in back history performance data. This is done one of two ways, with | |
| 907 equal probability of trying each way (falling back on the sequential version | |
| 908 as our data becomes more densely populated).""" | |
| 909 has_run_extra = False | |
| 910 revision_num = search_for_revision(DART_REPO_LOC) | |
| 911 | |
| 912 def try_to_run_additional(revision_number): | |
| 913 """Determine the number of results we have stored for a particular revision | |
| 914 number, and if it is less than 10, run some extra tests. | |
| 915 Args: | |
| 916 - revision_number: the revision whose performance we want to potentially | |
| 917 test.""" | |
| 918 a_test = TestBuilder.make_test(runner.suite_names[0], runner) | |
| 919 benchmark_name = a_test.values_list[0] | |
| 920 platform_name = a_test.platform_list[0] | |
| 921 variant = a_test.values_dict[platform_name].keys()[0] | |
| 922 num_results = post_results.get_num_results(benchmark_name, | |
| 923 platform_name, variant, revision_number, | |
| 924 a_test.file_processor.get_score_type(benchmark_name)) | |
| 925 if 10 - num_results < 2 and num_results != -1: | |
|
vsm
2012/08/16 00:20:22
"if num_results > 8:" is more readable. Also, a c
Emily Fortuna
2012/08/16 21:41:08
good point. fixed.
| |
| 926 reruns = 10 - num_results | |
|
vsm
2012/08/16 00:20:22
What happens if reruns is less than 0?
Emily Fortuna
2012/08/16 21:41:08
Fixed.
| |
| 927 else: | |
| 928 reruns = 2 | |
| 929 run = runner.run_test_sequence(revision_num=str(revision_number), | |
| 930 num_reruns=reruns) | |
| 931 if run == 0 and num_results + reruns >= 10: | |
| 932 results_set = update_set_of_done_cls(revision_number) | |
| 933 else: | |
| 934 return False | |
|
vsm
2012/08/16 00:20:22
Can you add a comment on what the return value mea
Emily Fortuna
2012/08/16 21:41:08
Done.
| |
| 935 return True | |
| 936 | |
| 937 if random.choice([True, False]): | |
| 938 # Select a random CL number, with greater likelihood of selecting a CL in | |
| 939 # the more recent history than the distant past (using a simplified weighted | |
| 940 # bucket algorithm). If that CL has less than 10 runs, run additional. If it | |
| 941 # already has 10 runs, look for another CL number that is not yet have all | |
| 942 # of its additional runs (do this up to 15 times). | |
| 943 tries = 0 | |
| 944 thousands_list = range(1, int(revision_num)/1000 + 1) | |
| 945 weighted_total = sum(thousands_list) | |
| 946 generated_random_number = random.randint(0, weighted_total - 1) | |
| 947 for i in list(reversed(thousands_list)): | |
| 948 thousands = thousands_list[i - 1] | |
| 949 weighted_total -= thousands_list[i - 1] | |
| 950 if weighted_total <= generated_random_number: | |
| 951 break | |
| 952 while tries < 15 and not has_run_extra: | |
| 953 rev = thousands * 1000 + random.randrange(0, | |
| 954 int(revision_num) - | |
| 955 int(math.pow(10, int(math.floor(math.log10(int(revision_num))))) + 1)) | |
|
vsm
2012/08/16 00:20:22
Can you add some comments here? What is the inten
| |
| 956 has_run_extra = try_to_run_additional(rev) | |
| 957 tries += 1 | |
| 958 | |
| 959 if not has_run_extra: | |
| 960 # Try to get up to 10 runs of each CL, starting with the most recent | |
| 961 # CL that does not yet have 10 runs. But only perform a set of extra | |
| 962 # runs at most 2 at a time before | |
| 963 # checking to see if new code has been checked in. | |
| 964 while revision_num > 0 and not has_run_extra: | |
| 965 if revision_num not in results_set: | |
| 966 has_run_extra = try_to_run_additional(revision_num) | |
| 967 revision_num -= 1 | |
| 968 if not has_run_extra: | |
| 969 # No more extra back-runs to do (for now). Wait for new code. | |
| 970 time.sleep(200) | |
| 971 return results_set | |
| 972 | |
| 1132 def main(): | 973 def main(): |
| 1133 runner = TestRunner() | 974 runner = TestRunner() |
| 1134 continuous = runner.parse_args() | 975 continuous = runner.parse_args() |
| 1135 | 976 |
| 1136 if not os.path.exists(DART_REPO_LOC): | 977 if not os.path.exists(DART_REPO_LOC): |
| 1137 os.mkdir(dirname(DART_REPO_LOC)) | 978 os.mkdir(dirname(DART_REPO_LOC)) |
| 1138 os.chdir(dirname(DART_REPO_LOC)) | 979 os.chdir(dirname(DART_REPO_LOC)) |
| 1139 p = subprocess.Popen('gclient config https://dart.googlecode.com/svn/' + | 980 p = subprocess.Popen('gclient config https://dart.googlecode.com/svn/' + |
| 1140 'branches/bleeding_edge/deps/all.deps', | 981 'branches/bleeding_edge/deps/all.deps', |
| 1141 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 982 stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 1142 shell=True) | 983 shell=True) |
| 1143 p.communicate() | 984 p.communicate() |
| 1144 if continuous: | 985 if continuous: |
| 1145 while True: | 986 while True: |
| 1146 results_set = update_set_of_done_cls() | 987 results_set = update_set_of_done_cls() |
| 1147 if runner.has_new_code(): | 988 if runner.has_new_code(): |
| 1148 runner.run_test_sequence() | 989 runner.run_test_sequence() |
| 1149 else: | 990 else: |
| 1150 # Try to get up to 10 runs of each CL, starting with the most recent CL | 991 results_set = fill_in_back_history(results_set, runner) |
| 1151 # that does not yet have 10 runs. But only perform a set of extra runs | |
| 1152 # at most 10 at a time (get all the extra runs for one CL) before | |
| 1153 # checking to see if new code has been checked in. | |
| 1154 has_run_extra = False | |
| 1155 revision_num = int(search_for_revision(['svn', 'info'])) | |
| 1156 if revision_num == -1: | |
| 1157 revision_num = int(search_for_revision(['git', 'svn', 'info'])) | |
| 1158 | |
| 1159 # No need to track the performance before revision 3000. That's way in | |
| 1160 # the past. | |
| 1161 while revision_num > 3000 and not has_run_extra: | |
| 1162 if revision_num not in results_set: | |
| 1163 a_test = TestBuilder.make_test(runner.suite_names[0], runner) | |
| 1164 benchmark_name = a_test.values_list[0] | |
| 1165 platform_name = a_test.platform_list[0] | |
| 1166 variant = a_test.values_dict[platform_name].keys()[0] | |
| 1167 number_of_results = post_results.get_num_results(benchmark_name, | |
| 1168 platform_name, variant, revision_num, | |
| 1169 a_test.file_processor.get_score_type(benchmark_name)) | |
| 1170 if number_of_results < 10 and number_of_results >= 0: | |
| 1171 run = runner.run_test_sequence(revision_num=str(revision_num), | |
| 1172 num_reruns=(10-number_of_results)) | |
| 1173 if run == 0: | |
| 1174 has_run_extra = True | |
| 1175 results_set = update_set_of_done_cls(revision_num) | |
| 1176 revision_num -= 1 | |
| 1177 # No more extra back-runs to do (for now). Wait for new code. | |
| 1178 time.sleep(200) | |
| 1179 else: | 992 else: |
| 1180 runner.run_test_sequence() | 993 runner.run_test_sequence() |
| 1181 | 994 |
| 1182 if __name__ == '__main__': | 995 if __name__ == '__main__': |
| 1183 main() | 996 main() |
| OLD | NEW |