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

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 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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()
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