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