Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: tools/testing/perf_testing/run_perf_tests.py

Issue 10836249: More tweaks to perf script (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 18 matching lines...) Expand all
168 our output to be placed. 169 our output to be placed.
169 170
170 Args: 171 Args:
171 dir_name: the directory we will create if it does not exist.""" 172 dir_name: the directory we will create if it does not exist."""
172 dir_path = os.path.join(TOP_LEVEL_DIR, 'tools', 173 dir_path = os.path.join(TOP_LEVEL_DIR, 'tools',
173 'testing', 'perf_testing', dir_name) 174 'testing', 'perf_testing', dir_name)
174 if not os.path.exists(dir_path): 175 if not os.path.exists(dir_path):
175 os.makedirs(dir_path) 176 os.makedirs(dir_path)
176 print 'Creating output directory ', dir_path 177 print 'Creating output directory ', dir_path
177 178
178 def has_new_code(self): 179 def has_interesting_code(self, past_revision_num=None):
179 """Tests if there are any newer versions of files on the server.""" 180 """Tests if there are any versions of files that might change performance
181 results on the server."""
180 if not os.path.exists(DART_REPO_LOC): 182 if not os.path.exists(DART_REPO_LOC):
181 return True 183 return True
182 os.chdir(DART_REPO_LOC) 184 os.chdir(DART_REPO_LOC)
185 no_effect = ['client', 'compiler', 'editor', 'pkg', 'samples', 'tests',
186 'third_party', 'tools', 'utils']
183 # Pass 'p' in if we have a new certificate for the svn server, we want to 187 # Pass 'p' in if we have a new certificate for the svn server, we want to
184 # (p)ermanently accept it. 188 # (p)ermanently accept it.
185 results, _ = self.run_cmd(['svn', 'st', '-u'], std_in='p\r\n') 189 if past_revision_num:
190 # TODO(efortuna): This assumes you're using svn. Have a git fallback as
191 # well.
192 results, _ = self.run_cmd(['svn', 'log', '-v', '-r',
193 str(past_revision_num)], std_in='p\r\n')
194 results = results.split('\n')
195 if len(results) <= 3:
196 results = []
197 else:
198 # Trim off the details about revision number and commit message. We're
199 # only interested in the files that are changed.
200 results = results[3:]
201 changed_files = []
202 for result in results:
203 if result == '':
204 break
205 changed_files += [result.replace('/branches/bleeding_edge/dart/', '')]
206 results = changed_files
207 else:
208 results, _ = self.run_cmd(['svn', 'st', '-u'], std_in='p\r\n')
209 results = results.split('\n')
186 for line in results: 210 for line in results:
187 if '*' in line: 211 tokens = line.split()
188 return True 212 if past_revision_num or len(tokens) >= 3 and '*' in tokens[-3]:
213 # Loop through the changed files to see if it contains any files that
214 # are NOT listed in the no_effect list (directories not listed in
215 # the "no_effect" list are assumed to potentially affect performance.
216 if not reduce(lambda x, y: x or y,
217 [tokens[-1].startswith(item) for item in no_effect], False):
218 return True
189 return False 219 return False
190 220
191 def get_os_directory(self): 221 def get_os_directory(self):
192 """Specifies the name of the directory for the testing build of dart, which 222 """Specifies the name of the directory for the testing build of dart, which
193 has yet a different naming convention from utils.getBuildRoot(...).""" 223 has yet a different naming convention from utils.getBuildRoot(...)."""
194 if platform.system() == 'Windows': 224 if platform.system() == 'Windows':
195 return 'windows' 225 return 'windows'
196 elif platform.system() == 'Darwin': 226 elif platform.system() == 'Darwin':
197 return 'macos' 227 return 'macos'
198 else: 228 else:
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 270
241 def run_test_sequence(self, revision_num='', num_reruns=1): 271 def run_test_sequence(self, revision_num='', num_reruns=1):
242 """Run the set of commands to (possibly) build, run, and post the results 272 """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 273 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. 274 the run failed, -1 if the build is broken.
245 """ 275 """
246 suites = [] 276 suites = []
247 success = True 277 success = True
248 if not self.no_build and self.sync_and_build(suites, revision_num) == 1: 278 if not self.no_build and self.sync_and_build(suites, revision_num) == 1:
249 return -1 # The build is broken. 279 return -1 # The build is broken.
250 280
251 for name in self.suite_names: 281 for name in self.suite_names:
252 for run in range(num_reruns): 282 for run in range(num_reruns):
253 suites += [TestBuilder.make_test(name, self)] 283 suites += [TestBuilder.make_test(name, self)]
254 284
255 for test in suites: 285 for test in suites:
256 success = success and test.run() 286 success = success and test.run()
257 if success: 287 if success:
258 return 0 288 return 0
259 else: 289 else:
260 return 1 290 return 1
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 'dartium', 'LAST_VERSION') 396 'dartium', 'LAST_VERSION')
367 version_file = open(version_file_name, 'r') 397 version_file = open(version_file_name, 'r')
368 version = version_file.read().split('.')[-2] 398 version = version_file.read().split('.')[-2]
369 version_file.close() 399 version_file.close()
370 return version 400 return version
371 401
372 if browser and browser == 'dartium': 402 if browser and browser == 'dartium':
373 revision = get_dartium_revision() 403 revision = get_dartium_revision()
374 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile) 404 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile)
375 else: 405 else:
376 revision = search_for_revision(['svn', 'info']) 406 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) 407 self.test.test_runner.run_cmd(['echo', 'Revision: ' + revision], outfile)
380 408
381 409
382 class Processor(object): 410 class Processor(object):
383 """The base level vistor class that processes tests. It contains convenience 411 """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 412 methods that many File Processor objects use. Any class that would like to be
385 a ProcessorVisitor must implement the process_file() method.""" 413 a ProcessorVisitor must implement the process_file() method."""
386 414
387 SCORE = 'Score' 415 SCORE = 'Score'
388 COMPILE_TIME = 'CompileTime' 416 COMPILE_TIME = 'CompileTime'
(...skipping 28 matching lines...) Expand all
417 variant: Specifies whether the data was about generated Frog, js, a 445 variant: Specifies whether the data was about generated Frog, js, a
418 combination of both, or Dart depending on the test. 446 combination of both, or Dart depending on the test.
419 revision_number: The revision of the code (and sometimes the revision of 447 revision_number: The revision of the code (and sometimes the revision of
420 dartium). 448 dartium).
421 449
422 Returns: True if the post was successful file.""" 450 Returns: True if the post was successful file."""
423 return post_results.report_results(benchmark_name, score, platform, variant, 451 return post_results.report_results(benchmark_name, score, platform, variant,
424 revision_number, metric) 452 revision_number, metric)
425 453
426 def calculate_geometric_mean(self, platform, variant, svn_revision): 454 def calculate_geometric_mean(self, platform, variant, svn_revision):
427 """Calculate the aggregate geometric mean for JS and frog benchmark sets, 455 """Calculate the aggregate geometric mean for JS and dart2js benchmark sets,
428 given two benchmark dictionaries.""" 456 given two benchmark dictionaries."""
429 geo_mean = 0 457 geo_mean = 0
430 if self.test.is_valid_combination(platform, variant): 458 if self.test.is_valid_combination(platform, variant):
431 for benchmark in self.test.values_list: 459 for benchmark in self.test.values_list:
432 geo_mean += math.log( 460 geo_mean += math.log(
433 self.test.values_dict[platform][variant][benchmark][ 461 self.test.values_dict[platform][variant][benchmark][
434 len(self.test.values_dict[platform][variant][benchmark]) - 1]) 462 len(self.test.values_dict[platform][variant][benchmark]) - 1])
435 463
436 self.test.values_dict[platform][variant]['Geo-Mean'] += \ 464 self.test.values_dict[platform][variant]['Geo-Mean'] += \
437 [math.pow(math.e, geo_mean / len(self.test.values_list))] 465 [math.pow(math.e, geo_mean / len(self.test.values_list))]
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 519
492 class CommonBrowserTest(RuntimePerformanceTest): 520 class CommonBrowserTest(RuntimePerformanceTest):
493 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the 521 """Runs this basic performance tests (Benchpress, some V8 benchmarks) in the
494 browser.""" 522 browser."""
495 523
496 def __init__(self, test_runner): 524 def __init__(self, test_runner):
497 """Args: 525 """Args:
498 test_runner: Reference to the object that notifies us when to run.""" 526 test_runner: Reference to the object that notifies us when to run."""
499 super(CommonBrowserTest, self).__init__( 527 super(CommonBrowserTest, self).__init__(
500 self.name(), BrowserTester.get_browsers(False), 528 self.name(), BrowserTester.get_browsers(False),
501 'browser', ['js', 'frog', 'dart2js'], 529 'browser', ['js', 'dart2js'],
502 self.get_standalone_benchmarks(), test_runner, 530 self.get_standalone_benchmarks(), test_runner,
503 self.CommonBrowserTester(self), 531 self.CommonBrowserTester(self),
504 self.CommonBrowserFileProcessor(self)) 532 self.CommonBrowserFileProcessor(self))
505 533
506 @staticmethod 534 @staticmethod
507 def name(): 535 def name():
508 return 'browser-perf' 536 return 'browser-perf'
509 537
510 @staticmethod 538 @staticmethod
511 def get_standalone_benchmarks(): 539 def get_standalone_benchmarks():
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
658 def get_dromaeo_benchmarks(): 686 def get_dromaeo_benchmarks():
659 valid = DromaeoTester.get_valid_dromaeo_tags() 687 valid = DromaeoTester.get_valid_dromaeo_tags()
660 benchmarks = reduce(lambda l1,l2: l1+l2, 688 benchmarks = reduce(lambda l1,l2: l1+l2,
661 [tests for (tag, tests) in 689 [tests for (tag, tests) in
662 DromaeoTester.DROMAEO_BENCHMARKS.values() 690 DromaeoTester.DROMAEO_BENCHMARKS.values()
663 if tag in valid]) 691 if tag in valid])
664 return map(DromaeoTester.legalize_filename, benchmarks) 692 return map(DromaeoTester.legalize_filename, benchmarks)
665 693
666 @staticmethod 694 @staticmethod
667 def get_dromaeo_versions(): 695 def get_dromaeo_versions():
668 return ['js', 'dart2js_dom', 'dart2js_html'] 696 return ['js', 'dart2js_html']
669 697
670 698
671 class DromaeoTest(RuntimePerformanceTest): 699 class DromaeoTest(RuntimePerformanceTest):
672 """Runs Dromaeo tests, in the browser.""" 700 """Runs Dromaeo tests, in the browser."""
673 def __init__(self, test_runner): 701 def __init__(self, test_runner):
674 super(DromaeoTest, self).__init__( 702 super(DromaeoTest, self).__init__(
675 self.name(), 703 self.name(),
676 BrowserTester.get_browsers(True), 704 BrowserTester.get_browsers(True),
677 'browser', 705 'browser',
678 DromaeoTester.get_dromaeo_versions(), 706 DromaeoTester.get_dromaeo_versions(),
(...skipping 21 matching lines...) Expand all
700 """Move the appropriate version of ChromeDriver onto the path. 728 """Move the appropriate version of ChromeDriver onto the path.
701 TODO(efortuna): This is a total hack because the latest version of Chrome 729 TODO(efortuna): This is a total hack because the latest version of Chrome
702 (Dartium builds) requires a different version of ChromeDriver, that is 730 (Dartium builds) requires a different version of ChromeDriver, that is
703 incompatible with the release or beta Chrome and vice versa. Remove these 731 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 732 shenanigans once we're back to both versions of Chrome using the same
705 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is 733 version of ChromeDriver. IMPORTANT NOTE: This assumes your chromedriver is
706 in the default location (inside depot_tools). 734 in the default location (inside depot_tools).
707 """ 735 """
708 current_dir = os.getcwd() 736 current_dir = os.getcwd()
709 self.test.test_runner.get_archive('chromedriver') 737 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) 738 path = os.environ['PATH'].split(os.pathsep)
712 orig_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 'testing', 739 orig_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 'testing',
713 'orig-chromedriver') 740 'orig-chromedriver')
714 dartium_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools', 741 dartium_chromedriver_path = os.path.join(DART_REPO_LOC, 'tools',
715 'testing', 742 'testing',
716 'dartium-chromedriver') 743 'dartium-chromedriver')
717 extension = '' 744 extension = ''
718 if platform.system() == 'Windows': 745 if platform.system() == 'Windows':
719 extension = '.exe' 746 extension = '.exe'
720 747
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
841 upload_success = upload_success and self.report_results( 868 upload_success = upload_success and self.report_results(
842 name, score, browser, version, revision_num, 869 name, score, browser, version, revision_num,
843 self.get_score_type(name)) 870 self.get_score_type(name))
844 else: 871 else:
845 upload_success = False 872 upload_success = False
846 873
847 f.close() 874 f.close()
848 self.calculate_geometric_mean(browser, version, revision_num) 875 self.calculate_geometric_mean(browser, version, revision_num)
849 return upload_success 876 return upload_success
850 877
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): 878 class TestBuilder(object):
1090 """Construct the desired test object.""" 879 """Construct the desired test object."""
1091 available_suites = dict((suite.name(), suite) for suite in [ 880 available_suites = dict((suite.name(), suite) for suite in [
1092 CompileTimeAndSizeTest, CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) 881 CommonBrowserTest, DromaeoTest])
1093 882
1094 @staticmethod 883 @staticmethod
1095 def make_test(test_name, test_runner): 884 def make_test(test_name, test_runner):
1096 return TestBuilder.available_suites[test_name](test_runner) 885 return TestBuilder.available_suites[test_name](test_runner)
1097 886
1098 @staticmethod 887 @staticmethod
1099 def available_suite_names(): 888 def available_suite_names():
1100 return TestBuilder.available_suites.keys() 889 return TestBuilder.available_suites.keys()
1101 890
1102 def search_for_revision(svn_info_command): 891 def search_for_revision(directory = None):
1103 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, 892 """Find the current revision number in the desired directory. If directory is
1104 stderr = subprocess.STDOUT, 893 None, find the revision number in the current directory."""
1105 shell = (platform.system() == 'Windows')) 894 def find_revision(svn_info_command):
1106 output, _ = p.communicate() 895 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE,
1107 for line in output.split('\n'): 896 stderr = subprocess.STDOUT,
1108 if 'Revision' in line: 897 shell = (platform.system() == 'Windows'))
1109 return line.split()[1] 898 output, _ = p.communicate()
1110 return -1 899 for line in output.split('\n'):
900 if 'Revision' in line:
901 return int(line.split()[1])
902 return -1
903
904 cwd = os.getcwd()
905 if not directory:
906 directory = cwd
907 os.chdir(directory)
908 revision_num = int(find_revision(['svn', 'info']))
909 if revision_num == -1:
910 revision_num = int(find_revision(['git', 'svn', 'info']))
911 os.chdir(cwd)
912 return str(revision_num)
1111 913
1112 def update_set_of_done_cls(revision_num=None): 914 def update_set_of_done_cls(revision_num=None):
1113 """Update the set of CLs that do not need additional performance runs. 915 """Update the set of CLs that do not need additional performance runs.
1114 Args: 916 Args:
1115 revision_num: an additional number to be added to the 'done set' 917 revision_num: an additional number to be added to the 'done set'
1116 """ 918 """
1117 filename = os.path.join(TOP_LEVEL_DIR, 'cached_results.txt') 919 filename = os.path.join(TOP_LEVEL_DIR, 'cached_results.txt')
1118 if not os.path.exists(filename): 920 if not os.path.exists(filename):
1119 f = open(filename, 'w') 921 f = open(filename, 'w')
1120 results = set() 922 results = set()
1121 pickle.dump(results, f) 923 pickle.dump(results, f)
1122 f.close() 924 f.close()
1123 f = open(filename, 'r+') 925 f = open(filename, 'r+')
1124 result_set = pickle.load(f) 926 result_set = pickle.load(f)
1125 if revision_num: 927 if revision_num:
1126 f.seek(0) 928 f.seek(0)
1127 result_set.add(revision_num) 929 result_set.add(revision_num)
1128 pickle.dump(result_set, f) 930 pickle.dump(result_set, f)
1129 f.close() 931 f.close()
1130 return result_set 932 return result_set
1131 933
934 def fill_in_back_history(results_set, runner):
935 """Fill in back history performance data. This is done one of two ways, with
936 equal probability of trying each way (falling back on the sequential version
937 as our data becomes more densely populated)."""
938 has_run_extra = False
939 revision_num = int(search_for_revision(DART_REPO_LOC))
940
941 def try_to_run_additional(revision_number):
942 """Determine the number of results we have stored for a particular revision
943 number, and if it is less than 10, run some extra tests.
944 Args:
945 - revision_number: the revision whose performance we want to potentially
946 test.
947 Returns: True if we successfully ran some additional tests."""
948 if not runner.has_interesting_code(revision_number):
949 results_set = update_set_of_done_cls(revision_number)
950 return False
951 a_test = TestBuilder.make_test(runner.suite_names[0], runner)
952 benchmark_name = a_test.values_list[0]
953 platform_name = a_test.platform_list[0]
954 variant = a_test.values_dict[platform_name].keys()[0]
955 num_results = post_results.get_num_results(benchmark_name,
956 platform_name, variant, revision_number,
957 a_test.file_processor.get_score_type(benchmark_name))
958 if num_results < 10:
959 # Run at most two more times.
960 if num_results > 8:
961 reruns = 10 - num_results
962 else:
963 reruns = 2
964 run = runner.run_test_sequence(revision_num=str(revision_number),
965 num_reruns=reruns)
966 if num_results >= 10 or run == 0 and num_results + reruns >= 10:
967 results_set = update_set_of_done_cls(revision_number)
968 else:
969 return False
970 return True
971
972 if random.choice([True, False]):
973 # Select a random CL number, with greater likelihood of selecting a CL in
974 # the more recent history than the distant past (using a simplified weighted
975 # bucket algorithm). If that CL has less than 10 runs, run additional. If it
976 # already has 10 runs, look for another CL number that is not yet have all
977 # of its additional runs (do this up to 15 times).
978 tries = 0
979 # Select which "thousands bucket" we're going to run additional tests for.
980 bucket_size = 1000
981 thousands_list = range(1, int(revision_num)/bucket_size + 1)
982 weighted_total = sum(thousands_list)
983 generated_random_number = random.randint(0, weighted_total - 1)
984 for i in list(reversed(thousands_list)):
985 thousands = thousands_list[i - 1]
986 weighted_total -= thousands_list[i - 1]
987 if weighted_total <= generated_random_number:
988 break
989 while tries < 15 and not has_run_extra:
990 # Now select a particular revision in that bucket.
991 if thousands == int(revision_num)/bucket_size:
992 max_range = 1 + revision_num % bucket_size
993 else:
994 max_range = bucket_size
995 rev = thousands * bucket_size + random.randrange(0, max_range)
996 if rev not in results_set:
997 has_run_extra = try_to_run_additional(rev)
998 tries += 1
999
1000 if not has_run_extra:
1001 # Try to get up to 10 runs of each CL, starting with the most recent
1002 # CL that does not yet have 10 runs. But only perform a set of extra
1003 # runs at most 2 at a time before checking to see if new code has been
1004 # checked in.
1005 while revision_num > 0 and not has_run_extra:
1006 if revision_num not in results_set:
1007 has_run_extra = try_to_run_additional(revision_num)
1008 revision_num -= 1
1009 if not has_run_extra:
1010 # No more extra back-runs to do (for now). Wait for new code.
1011 time.sleep(200)
1012 return results_set
1013
1132 def main(): 1014 def main():
1133 runner = TestRunner() 1015 runner = TestRunner()
1134 continuous = runner.parse_args() 1016 continuous = runner.parse_args()
1135 1017
1136 if not os.path.exists(DART_REPO_LOC): 1018 if not os.path.exists(DART_REPO_LOC):
1137 os.mkdir(dirname(DART_REPO_LOC)) 1019 os.mkdir(dirname(DART_REPO_LOC))
1138 os.chdir(dirname(DART_REPO_LOC)) 1020 os.chdir(dirname(DART_REPO_LOC))
1139 p = subprocess.Popen('gclient config https://dart.googlecode.com/svn/' + 1021 p = subprocess.Popen('gclient config https://dart.googlecode.com/svn/' +
1140 'branches/bleeding_edge/deps/all.deps', 1022 'branches/bleeding_edge/deps/all.deps',
1141 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 1023 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1142 shell=True) 1024 shell=True)
1143 p.communicate() 1025 p.communicate()
1144 if continuous: 1026 if continuous:
1145 while True: 1027 while True:
1146 results_set = update_set_of_done_cls() 1028 results_set = update_set_of_done_cls()
1147 if runner.has_new_code(): 1029 if runner.has_interesting_code():
1148 runner.run_test_sequence() 1030 runner.run_test_sequence()
1149 else: 1031 else:
1150 # Try to get up to 10 runs of each CL, starting with the most recent CL 1032 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: 1033 else:
1180 runner.run_test_sequence() 1034 runner.run_test_sequence()
1181 1035
1182 if __name__ == '__main__': 1036 if __name__ == '__main__':
1183 main() 1037 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698