| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 3 # Copyright (c) 2011, 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 getpass | |
| 9 import math | |
| 10 try: | |
| 11 from matplotlib.font_manager import FontProperties | |
| 12 import matplotlib.pyplot as plt | |
| 13 except ImportError: | |
| 14 pass # Only needed if we want to make graphs. | |
| 15 import optparse | 8 import optparse |
| 16 import os | 9 import os |
| 17 from os.path import dirname, abspath | 10 from os.path import dirname, abspath |
| 18 import platform | 11 import platform |
| 19 import re | 12 import re |
| 20 import shutil | 13 import shutil |
| 21 import stat | 14 import stat |
| 22 import subprocess | 15 import subprocess |
| 23 import sys | 16 import sys |
| 24 import time | 17 import time |
| 25 import traceback | |
| 26 | 18 |
| 27 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) | 19 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) |
| 28 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), | 20 DART_INSTALL_LOCATION = abspath(os.path.join(dirname(abspath(__file__)), |
| 29 '..', '..', '..')) | 21 '..', '..', '..')) |
| 30 sys.path.append(TOOLS_PATH) | 22 sys.path.append(TOOLS_PATH) |
| 23 sys.path.append(os.path.join(DART_INSTALL_LOCATION, 'internal', 'tests')) |
| 24 import post_results |
| 31 import utils | 25 import utils |
| 32 | 26 |
| 33 """This script runs to track performance and size progress of | 27 """This script runs to track performance and size progress of |
| 34 different svn revisions. It tests to see if there a newer version of the code on | 28 different svn revisions. It tests to see if there a newer version of the code on |
| 35 the server, and will sync and run the performance tests if so.""" | 29 the server, and will sync and run the performance tests if so.""" |
| 36 class TestRunner(object): | 30 class TestRunner(object): |
| 37 | 31 |
| 38 def __init__(self): | 32 def __init__(self): |
| 39 self.verbose = False | 33 self.verbose = False |
| 40 self.has_shell = False | 34 self.has_shell = False |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 | 102 |
| 109 self.run_cmd(['gclient', 'sync']) | 103 self.run_cmd(['gclient', 'sync']) |
| 110 | 104 |
| 111 # On Windows, the output directory is marked as "Read Only," which causes an | 105 # On Windows, the output directory is marked as "Read Only," which causes an |
| 112 # error to be thrown when we use shutil.rmtree. This helper function changes | 106 # error to be thrown when we use shutil.rmtree. This helper function changes |
| 113 # the permissions so we can still delete the directory. | 107 # the permissions so we can still delete the directory. |
| 114 def on_rm_error(func, path, exc_info): | 108 def on_rm_error(func, path, exc_info): |
| 115 if os.path.exists(path): | 109 if os.path.exists(path): |
| 116 os.chmod(path, stat.S_IWRITE) | 110 os.chmod(path, stat.S_IWRITE) |
| 117 os.unlink(path) | 111 os.unlink(path) |
| 118 # TODO(efortuna): building the sdk locally is a band-aid until all build | 112 # TODO(efortuna): building the sdk locally is a band-aid until all build XXX |
| 119 # platform SDKs are hosted in Google storage. Pull from https://sandbox. | 113 # platform SDKs are hosted in Google storage. Pull from https://sandbox. |
| 120 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk | 114 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk |
| 121 # eventually. | 115 # eventually. |
| 122 # TODO(efortuna): Currently always building ia32 architecture because we | 116 # TODO(efortuna): Currently always building ia32 architecture because we |
| 123 # don't have test statistics for what's passing on x64. Eliminate arch | 117 # don't have test statistics for what's passing on x64. Eliminate arch |
| 124 # specification when we have tests running on x64, too. | 118 # specification when we have tests running on x64, too. |
| 125 shutil.rmtree(os.path.join(os.getcwd(), | 119 shutil.rmtree(os.path.join(os.getcwd(), |
| 126 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | 120 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), |
| 127 onerror=on_rm_error) | 121 onerror=on_rm_error) |
| 128 | 122 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 def get_os_directory(self): | 158 def get_os_directory(self): |
| 165 """Specifies the name of the directory for the testing build of dart, which | 159 """Specifies the name of the directory for the testing build of dart, which |
| 166 has yet a different naming convention from utils.getBuildRoot(...).""" | 160 has yet a different naming convention from utils.getBuildRoot(...).""" |
| 167 if platform.system() == 'Windows': | 161 if platform.system() == 'Windows': |
| 168 return 'windows' | 162 return 'windows' |
| 169 elif platform.system() == 'Darwin': | 163 elif platform.system() == 'Darwin': |
| 170 return 'macos' | 164 return 'macos' |
| 171 else: | 165 else: |
| 172 return 'linux' | 166 return 'linux' |
| 173 | 167 |
| 174 def upload_to_app_engine(self, suite_names): | |
| 175 """Upload our results to our appengine server. | |
| 176 Arguments: | |
| 177 suite_names: Directories to upload data from (should match directory | |
| 178 names).""" | |
| 179 # TODO(efortuna): This is the most basic way to get the data up | |
| 180 # for others to view. Revisit this once we're serving nicer graphs (Google | |
| 181 # Chart Tools) and from multiple perfbots and once we're in a position to | |
| 182 # organize the data in a useful manner(!!). | |
| 183 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 184 'perf_testing')) | |
| 185 for data in suite_names: | |
| 186 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 187 shutil.rmtree(path, ignore_errors=True) | |
| 188 os.makedirs(path) | |
| 189 files = [] | |
| 190 # Copy the 1000 most recent trace files to be uploaded. | |
| 191 for f in os.listdir(data): | |
| 192 files += [(os.path.getmtime(os.path.join(data, f)), f)] | |
| 193 files.sort() | |
| 194 for f in files[-1000:]: | |
| 195 shutil.copyfile(os.path.join(data, f[1]), | |
| 196 os.path.join(path, f[1]+'.txt')) | |
| 197 # Generate directory listing. | |
| 198 for data in suite_names: | |
| 199 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | |
| 200 out = open(os.path.join('appengine', 'static', | |
| 201 '%s-%s.html' % (data, utils.GuessOS())), 'w') | |
| 202 out.write('<html>\n <body>\n <ul>\n') | |
| 203 for f in os.listdir(path): | |
| 204 if not f.startswith('.'): | |
| 205 out.write(' <li><a href=data' + \ | |
| 206 '''/%(data)s/%(os)s/%(file)s>%(file)s</a></li>\n''' % \ | |
| 207 {'data': data, 'os': utils.GuessOS(), 'file': f}) | |
| 208 out.write(' </ul>\n </body>\n</html>') | |
| 209 out.close() | |
| 210 | |
| 211 shutil.rmtree(os.path.join('appengine', 'static', 'graphs'), | |
| 212 ignore_errors=True) | |
| 213 shutil.copytree('graphs', os.path.join('appengine', 'static', 'graphs')) | |
| 214 shutil.copyfile('index.html', os.path.join('appengine', 'static', | |
| 215 'index.html')) | |
| 216 shutil.copyfile('dromaeo.html', os.path.join('appengine', 'static', | |
| 217 'dromaeo.html')) | |
| 218 shutil.copyfile('data.html', os.path.join('appengine', 'static', | |
| 219 'data.html')) | |
| 220 self.run_cmd([os.path.join('..', '..', '..', 'third_party', | |
| 221 'appengine-python', 'appcfg.py'), '--oauth2', | |
| 222 'update', 'appengine/']) | |
| 223 | |
| 224 def parse_args(self): | 168 def parse_args(self): |
| 225 parser = optparse.OptionParser() | 169 parser = optparse.OptionParser() |
| 226 parser.add_option('--suites', '-s', dest='suites', help='Run the specified ' | 170 parser.add_option('--suites', '-s', dest='suites', help='Run the specified ' |
| 227 'comma-separated test suites from set: %s' % \ | 171 'comma-separated test suites from set: %s' % \ |
| 228 ','.join(TestBuilder.available_suite_names()), | 172 ','.join(TestBuilder.available_suite_names()), |
| 229 action='store', default=None) | 173 action='store', default=None) |
| 230 parser.add_option('--forever', '-f', dest='continuous', help='Run this scri' | 174 parser.add_option('--forever', '-f', dest='continuous', help='Run this scri' |
| 231 'pt forever, always checking for the next svn checkin', | 175 'pt forever, always checking for the next svn checkin', |
| 232 action='store_true', default=False) | 176 action='store_true', default=False) |
| 233 parser.add_option('--graph-only', '-g', dest='graph_only', default=False, | |
| 234 help='Do not run tests, only regenerate graphs', | |
| 235 action='store_true') | |
| 236 parser.add_option('--nobuild', '-n', dest='no_build', action='store_true', | 177 parser.add_option('--nobuild', '-n', dest='no_build', action='store_true', |
| 237 help='Do not sync with the repository and do not ' | 178 help='Do not sync with the repository and do not ' |
| 238 'rebuild.', default=False) | 179 'rebuild.', default=False) |
| 239 parser.add_option('--upload', '-u', dest='upload', help='Upload data to ' | |
| 240 'app engine (will require authentication).', | |
| 241 action='store_true', default=False) | |
| 242 parser.add_option('--verbose', '-v', dest='verbose', help='Print extra ' | 180 parser.add_option('--verbose', '-v', dest='verbose', help='Print extra ' |
| 243 'debug output', action='store_true', default=False) | 181 'debug output', action='store_true', default=False) |
| 244 | 182 |
| 245 args, ignored = parser.parse_args() | 183 args, ignored = parser.parse_args() |
| 246 | 184 |
| 247 if not args.suites: | 185 if not args.suites: |
| 248 suites = TestBuilder.available_suite_names() | 186 suites = TestBuilder.available_suite_names() |
| 249 else: | 187 else: |
| 250 suites = [] | 188 suites = [] |
| 251 suitelist = args.suites.split(',') | 189 suitelist = args.suites.split(',') |
| 252 for name in suitelist: | 190 for name in suitelist: |
| 253 if name in TestBuilder.available_suite_names(): | 191 if name in TestBuilder.available_suite_names(): |
| 254 suites.append(name) | 192 suites.append(name) |
| 255 else: | 193 else: |
| 256 print ('Error: Invalid suite %s not in ' % name) + \ | 194 print ('Error: Invalid suite %s not in ' % name) + \ |
| 257 '%s' % ','.join(TestBuilder.available_suite_names()) | 195 '%s' % ','.join(TestBuilder.available_suite_names()) |
| 258 sys.exit(1) | 196 sys.exit(1) |
| 259 self.suite_names = suites | 197 self.suite_names = suites |
| 260 self.no_build = args.no_build | 198 self.no_build = args.no_build |
| 261 self.graph_only = args.graph_only | |
| 262 self.upload = args.upload | |
| 263 self.verbose = args.verbose | 199 self.verbose = args.verbose |
| 264 return args.continuous | 200 return args.continuous |
| 265 | 201 |
| 266 def run_test_sequence(self): | 202 def run_test_sequence(self): |
| 267 """Run the set of commands to (possibly) build, run, and graph the results | 203 """Run the set of commands to (possibly) build, run, and graph the results |
| 268 of our tests. | 204 of our tests. |
| 269 | 205 |
| 270 Args: | 206 Args: |
| 271 suite_names: The "display name" the user enters to specify which | 207 suite_names: The "display name" the user enters to specify which |
| 272 benchmark(s) to run. | 208 benchmark(s) to run. |
| 273 no_build: True if we should not check the repository and build the latest | 209 no_build: True if we should not check the repository and build the latest |
| 274 version. | 210 version. |
| 275 graph_only: True if we should not run the tests, just (re)generate graphs. | 211 """ |
| 276 upload: True if we should upload our results to appengine.""" | |
| 277 suites = [] | 212 suites = [] |
| 278 for name in self.suite_names: | 213 for name in self.suite_names: |
| 279 suites += [TestBuilder.make_test(name, self)] | 214 suites += [TestBuilder.make_test(name, self)] |
| 280 | 215 |
| 281 if not self.no_build and self.sync_and_build(suites) == 1: | 216 if not self.no_build and self.sync_and_build(suites) == 1: |
| 282 return # The build is broken. | 217 return # The build is broken. |
| 283 | 218 |
| 284 for test in suites: | 219 for test in suites: |
| 285 test.run(self.graph_only) | 220 test.run() |
| 286 | |
| 287 if self.upload: | |
| 288 self.upload_to_app_engine(TestBuilder.available_suite_names()) | |
| 289 | 221 |
| 290 | 222 |
| 291 class Test(object): | 223 class Test(object): |
| 292 """The base class to provide shared code for different tests we will run and | 224 """The base class to provide shared code for different tests we will run and |
| 293 graph. At a high level, each test has three visitors (the tester, the | 225 graph. At a high level, each test has three visitors (the tester, the |
| 294 file_processor, and the grapher) that perform operations on the test | 226 file_processor that perform operations on the test object.""" |
| 295 object.""" | |
| 296 | 227 |
| 297 def __init__(self, result_folder_name, platform_list, variants, | 228 def __init__(self, result_folder_name, platform_list, variants, |
| 298 values_list, test_runner, tester, file_processor, grapher, | 229 values_list, test_runner, tester, file_processor, |
| 299 extra_metrics=['Geo-Mean'], build_targets=['create_sdk']): | 230 build_targets=['create_sdk']): |
| 300 """Args: | 231 """Args: |
| 301 result_folder_name: The name of the folder where a tracefile of | 232 result_folder_name: The name of the folder where a tracefile of |
| 302 performance results will be stored. | 233 performance results will be stored. |
| 303 platform_list: A list containing the platform(s) that our data has been | 234 platform_list: A list containing the platform(s) that our data has been |
| 304 run on. (command line, firefox, chrome, etc) | 235 run on. (command line, firefox, chrome, etc) |
| 305 variants: A list specifying whether we hold data about Frog | 236 variants: A list specifying whether we hold data about Frog |
| 306 generated code, plain JS code, or a combination of both, or | 237 generated code, plain JS code, or a combination of both, or |
| 307 Dart depending on the test. | 238 Dart depending on the test. |
| 308 values_list: A list containing the type of data we will be graphing | 239 values_list: A list containing the type of data we will be graphing |
| 309 (benchmarks, percentage passing, etc). | 240 (benchmarks, percentage passing, etc). |
| 310 test_runner: Reference to the parent test runner object that notifies a | 241 test_runner: Reference to the parent test runner object that notifies a |
| 311 test when to run. | 242 test when to run. |
| 312 tester: The visitor that actually performs the test running mechanics. | 243 tester: The visitor that actually performs the test running mechanics. |
| 313 file_processor: The visitor that processes files in the format | 244 file_processor: The visitor that processes files in the format |
| 314 appropriate for this test. | 245 appropriate for this test. |
| 315 grapher: The visitor that generates graphs given our test result data. | |
| 316 extra_metrics: A list of any additional measurements we wish to keep | |
| 317 track of (such as the geometric mean of a set, the sum, etc). | |
| 318 build_targets: The targets necessary to build to run these tests | 246 build_targets: The targets necessary to build to run these tests |
| 319 (default target is create_sdk).""" | 247 (default target is create_sdk).""" |
| 320 self.result_folder_name = result_folder_name | 248 self.result_folder_name = result_folder_name |
| 321 # cur_time is used as a timestamp of when this performance test was run. | 249 # cur_time is used as a timestamp of when this performance test was run. |
| 322 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) | 250 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) |
| 323 self.values_list = values_list | 251 self.values_list = values_list |
| 324 self.platform_list = platform_list | 252 self.platform_list = platform_list |
| 325 self.revision_dict = dict() | |
| 326 self.values_dict = dict() | |
| 327 self.test_runner = test_runner | 253 self.test_runner = test_runner |
| 328 self.tester = tester | 254 self.tester = tester |
| 329 self.file_processor = file_processor | 255 self.file_processor = file_processor |
| 330 self.grapher = grapher | |
| 331 self.extra_metrics = extra_metrics | |
| 332 self.build_targets = build_targets | 256 self.build_targets = build_targets |
| 333 # Initialize our values store. | |
| 334 for platform in platform_list: | |
| 335 self.revision_dict[platform] = dict() | |
| 336 self.values_dict[platform] = dict() | |
| 337 for f in variants: | |
| 338 self.revision_dict[platform][f] = dict() | |
| 339 self.values_dict[platform][f] = dict() | |
| 340 for val in values_list: | |
| 341 self.revision_dict[platform][f][val] = [] | |
| 342 self.values_dict[platform][f][val] = [] | |
| 343 for extra_metric in extra_metrics: | |
| 344 self.revision_dict[platform][f][extra_metric] = [] | |
| 345 self.values_dict[platform][f][extra_metric] = [] | |
| 346 | 257 |
| 347 def is_valid_combination(self, platform, variant): | 258 def is_valid_combination(self, platform, variant): |
| 348 """Check whether data should be captured for this platform/variant | 259 """Check whether data should be captured for this platform/variant |
| 349 combination. | 260 combination. |
| 350 """ | 261 """ |
| 351 return True | 262 return True |
| 352 | 263 |
| 353 def run(self, graph_only): | 264 def run(self): |
| 354 """Run the benchmarks/tests from the command line and plot the | 265 """Run the benchmarks/tests from the command line and plot the |
| 355 results. | 266 results. |
| 356 | 267 """ |
| 357 Args: | 268 for visitor in [self.tester, self.file_processor]: |
| 358 graph_only: True if we should just graph the results instead of also | |
| 359 running tests.""" | |
| 360 for visitor in [self.tester, self.file_processor, self.grapher]: | |
| 361 visitor.prepare() | 269 visitor.prepare() |
| 362 | 270 |
| 363 os.chdir(DART_INSTALL_LOCATION) | 271 os.chdir(DART_INSTALL_LOCATION) |
| 364 self.test_runner.ensure_output_directory(self.result_folder_name) | 272 self.test_runner.ensure_output_directory(self.result_folder_name) |
| 365 if not graph_only: | 273 self.tester.run_tests() |
| 366 self.tester.run_tests() | |
| 367 | 274 |
| 368 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) | 275 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) |
| 369 | 276 |
| 370 # TODO(efortuna): You will want to make this only use a subset of the files | 277 # TODO(efortuna): Remove trace files once uploaded. This will happen in a |
| 371 # eventually. | 278 # future CL. |
| 372 files = os.listdir(self.result_folder_name) | 279 files = os.listdir(self.result_folder_name) |
| 373 | |
| 374 for afile in files: | 280 for afile in files: |
| 375 if not afile.startswith('.'): | 281 if not afile.startswith('.'): |
| 376 self.file_processor.process_file(afile) | 282 self.file_processor.process_file(afile) |
| 377 | 283 |
| 378 if 'plt' in globals(): | |
| 379 # Only run Matplotlib if it is installed. | |
| 380 self.grapher.plot_results('%s.png' % self.result_folder_name) | |
| 381 | |
| 382 | 284 |
| 383 class Tester(object): | 285 class Tester(object): |
| 384 """The base level visitor class that runs tests. It contains convenience | 286 """The base level visitor class that runs tests. It contains convenience |
| 385 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 |
| 386 TesterVisitor must implement the run_tests() method.""" | 288 TesterVisitor must implement the run_tests() method.""" |
| 387 | 289 |
| 388 def __init__(self, test): | 290 def __init__(self, test): |
| 389 self.test = test | 291 self.test = test |
| 390 | 292 |
| 391 def prepare(self): | 293 def prepare(self): |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 methods that many File Processor objects use. Any class that would like to be | 328 methods that many File Processor objects use. Any class that would like to be |
| 427 a ProcessorVisitor must implement the process_file() method.""" | 329 a ProcessorVisitor must implement the process_file() method.""" |
| 428 | 330 |
| 429 def __init__(self, test): | 331 def __init__(self, test): |
| 430 self.test = test | 332 self.test = test |
| 431 | 333 |
| 432 def prepare(self): | 334 def prepare(self): |
| 433 """Perform any initial setup required before the test is run.""" | 335 """Perform any initial setup required before the test is run.""" |
| 434 pass | 336 pass |
| 435 | 337 |
| 436 def calculate_geometric_mean(self, platform, variant, svn_revision): | 338 def report_results(self, benchmark_name, score, platform, variant, |
| 437 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | 339 revision_number): |
| 438 given two benchmark dictionaries.""" | 340 """Store the results of the benchmark run. |
| 439 geo_mean = 0 | 341 Args: |
| 440 # TODO(vsm): Suppress graphing this combination altogether. For | 342 benchmark_name: The name of the individual benchmark. |
| 441 # now, we feed a geomean of 0. | 343 score: The numerical value of this benchmark. |
| 442 if self.test.is_valid_combination(platform, variant): | 344 platform: The platform the test was run on (firefox, command line, etc). |
| 443 for benchmark in self.test.values_list: | 345 variant: Specifies whether the data was about generated Frog, js, a |
| 444 geo_mean += math.log( | 346 combination of both, or Dart depending on the test. |
| 445 self.test.values_dict[platform][variant][benchmark][ | 347 revision_number: The revision of the code (and sometimes the revision of |
| 446 len(self.test.values_dict[platform][variant][benchmark]) - 1]) | 348 dartium). |
| 447 | |
| 448 self.test.values_dict[platform][variant]['Geo-Mean'] += \ | |
| 449 [math.pow(math.e, geo_mean / len(self.test.values_list))] | |
| 450 self.test.revision_dict[platform][variant]['Geo-Mean'] += [svn_revision] | |
| 451 | |
| 452 | |
| 453 class Grapher(object): | |
| 454 """The base level visitor class that generates graphs for data. It contains | |
| 455 convenience methods that many Grapher objects use. Any class that would like | |
| 456 to be a GrapherVisitor must implement the plot_results() method.""" | |
| 457 | 349 |
| 458 graph_out_dir = 'graphs' | 350 Returns: True if the post was successful.""" |
| 459 | 351 return post_results.report_results(benchmark_name, score, platform, variant, |
| 460 def __init__(self, test): | 352 revision_number) |
| 461 self.color_index = 0 | |
| 462 self.test = test | |
| 463 | |
| 464 def prepare(self): | |
| 465 """Perform any initial setup required before the test is run.""" | |
| 466 if 'plt' in globals(): | |
| 467 plt.cla() # cla = clear current axes | |
| 468 else: | |
| 469 print 'Unable to import Matplotlib and therefore unable to generate ' + \ | |
| 470 'graphs. Please install it for this version of Python.' | |
| 471 self.test.test_runner.ensure_output_directory(Grapher.graph_out_dir) | |
| 472 | |
| 473 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, | |
| 474 legend_loc, filename, platform_list, variants, | |
| 475 values_list, should_clear_axes=True): | |
| 476 """Sets style preferences for chart boilerplate that is consistent across | |
| 477 all charts, and saves the chart as a png. | |
| 478 | |
| 479 Args: | |
| 480 size_x: the size of the printed chart, in inches, in the horizontal | |
| 481 direction | |
| 482 size_y: the size of the printed chart, in inches in the vertical direction | |
| 483 legend_loc: the location of the legend in on the chart. See suitable | |
| 484 arguments for the loc argument in matplotlib | |
| 485 filename: the filename that we want to save the resulting chart as | |
| 486 platform_list: a list containing the platform(s) that our data has been | |
| 487 run on. (command line, firefox, chrome, etc) | |
| 488 values_list: a list containing the type of data we will be graphing | |
| 489 (performance, percentage passing, etc) | |
| 490 should_clear_axes: True if we want to create a fresh graph, instead of | |
| 491 plotting additional lines on the current graph.""" | |
| 492 if should_clear_axes: | |
| 493 plt.cla() # cla = clear current axes | |
| 494 for platform in platform_list: | |
| 495 for f in variants: | |
| 496 for val in values_list: | |
| 497 plt.plot(self.test.revision_dict[platform][f][val], | |
| 498 self.test.values_dict[platform][f][val], | |
| 499 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) | |
| 500 | |
| 501 plt.xlabel('Revision Number') | |
| 502 plt.ylabel(y_axis_label) | |
| 503 plt.title(chart_title) | |
| 504 fontP = FontProperties() | |
| 505 fontP.set_size('small') | |
| 506 plt.legend(loc=legend_loc, prop = fontP) | |
| 507 | |
| 508 fig = plt.gcf() | |
| 509 fig.set_size_inches(size_x, size_y) | |
| 510 fig.savefig(os.path.join(Grapher.graph_out_dir, filename)) | |
| 511 | |
| 512 def get_color(self): | |
| 513 # Just a bunch of distinct colors for a potentially large number of values | |
| 514 # we wish to graph. | |
| 515 colors = [ | |
| 516 'blue', 'green', 'red', 'cyan', 'magenta', 'black', '#3366CC', | |
| 517 '#DC3912', '#FF9900', '#109618', '#990099', '#0099C6', '#DD4477', | |
| 518 '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11', | |
| 519 '#6633CC', '#E67300', '#8B0707', '#651067', '#329262', '#5574A6', | |
| 520 '#3B3EAC', '#B77322', '#16D620', '#B91383', '#F4359E', '#9C5935', | |
| 521 '#A9C413', '#2A778D', '#668D1C', '#BEA413', '#0C5922', '#743411', | |
| 522 '#45AFE2', '#FF3300', '#FFCC00', '#14C21D', '#DF51FD', '#15CBFF', | |
| 523 '#FF97D2', '#97FB00', '#DB6651', '#518BC6', '#BD6CBD', '#35D7C2', | |
| 524 '#E9E91F', '#9877DD', '#FF8F20', '#D20B0B', '#B61DBA', '#40BD7E', | |
| 525 '#6AA7C4', '#6D70CD', '#DA9136', '#2DEA36', '#E81EA6', '#F558AE', | |
| 526 '#C07145', '#D7EE53', '#3EA7C6', '#97D129', '#E9CA1D', '#149638', | |
| 527 '#C5571D'] | |
| 528 color = colors[self.color_index] | |
| 529 self.color_index = (self.color_index + 1) % len(colors) | |
| 530 return color | |
| 531 | 353 |
| 532 | 354 |
| 533 class RuntimePerformanceTest(Test): | 355 class RuntimePerformanceTest(Test): |
| 534 """Super class for all runtime performance testing.""" | 356 """Super class for all runtime performance testing.""" |
| 535 | 357 |
| 536 def __init__(self, result_folder_name, platform_list, platform_type, | 358 def __init__(self, result_folder_name, platform_list, platform_type, |
| 537 versions, benchmarks, test_runner, tester, file_processor, | 359 versions, benchmarks, test_runner, tester, file_processor, |
| 538 build_targets=['create_sdk']): | 360 build_targets=['create_sdk']): |
| 539 """Args: | 361 """Args: |
| 540 result_folder_name: The name of the folder where a tracefile of | 362 result_folder_name: The name of the folder where a tracefile of |
| 541 performance results will be stored. | 363 performance results will be stored. |
| 542 platform_list: A list containing the platform(s) that our data has been | 364 platform_list: A list containing the platform(s) that our data has been |
| 543 run on. (command line, firefox, chrome, etc) | 365 run on. (command line, firefox, chrome, etc) |
| 544 variants: A list specifying whether we hold data about Frog | 366 variants: A list specifying whether we hold data about Frog |
| 545 generated code, plain JS code, or a combination of both, or | 367 generated code, plain JS code, or a combination of both, or |
| 546 Dart depending on the test. | 368 Dart depending on the test. |
| 547 values_list: A list containing the type of data we will be graphing | 369 values_list: A list containing the type of data we will be graphing |
| 548 (benchmarks, percentage passing, etc). | 370 (benchmarks, percentage passing, etc). |
| 549 test_runner: Reference to the parent test runner object that notifies a | 371 test_runner: Reference to the parent test runner object that notifies a |
| 550 test when to run. | 372 test when to run. |
| 551 tester: The visitor that actually performs the test running mechanics. | 373 tester: The visitor that actually performs the test running mechanics. |
| 552 file_processor: The visitor that processes files in the format | 374 file_processor: The visitor that processes files in the format |
| 553 appropriate for this test. | 375 appropriate for this test. |
| 554 grapher: The visitor that generates graphs given our test result data. | |
| 555 extra_metrics: A list of any additional measurements we wish to keep | |
| 556 track of (such as the geometric mean of a set, the sum, etc). | |
| 557 build_targets: The targets necessary to build to run these tests | 376 build_targets: The targets necessary to build to run these tests |
| 558 (default target is create_sdk).""" | 377 (default target is create_sdk).""" |
| 559 super(RuntimePerformanceTest, self).__init__(result_folder_name, | 378 super(RuntimePerformanceTest, self).__init__(result_folder_name, |
| 560 platform_list, versions, benchmarks, test_runner, tester, | 379 platform_list, versions, benchmarks, test_runner, tester, |
| 561 file_processor, self.RuntimePerfGrapher(self), | 380 file_processor, build_targets=build_targets) |
| 562 build_targets=build_targets) | |
| 563 self.platform_list = platform_list | 381 self.platform_list = platform_list |
| 564 self.platform_type = platform_type | 382 self.platform_type = platform_type |
| 565 self.versions = versions | 383 self.versions = versions |
| 566 self.benchmarks = benchmarks | 384 self.benchmarks = benchmarks |
| 567 | 385 |
| 568 class RuntimePerfGrapher(Grapher): | |
| 569 def plot_all_perf(self, png_filename): | |
| 570 """Create a plot that shows the performance changes of individual | |
| 571 benchmarks run by JS and generated by frog, over svn history.""" | |
| 572 for benchmark in self.test.benchmarks: | |
| 573 self.style_and_save_perf_plot( | |
| 574 'Performance of %s over time on the %s on %s' % (benchmark, | |
| 575 self.test.platform_type, utils.GuessOS()), | |
| 576 'Speed (bigger = better)', 16, 14, 'lower left', | |
| 577 benchmark + png_filename, self.test.platform_list, | |
| 578 self.test.versions, [benchmark]) | |
| 579 | |
| 580 def plot_avg_perf(self, png_filename): | |
| 581 """Generate a plot that shows the performance changes of the geomentric | |
| 582 mean of JS and frog benchmark performance over svn history.""" | |
| 583 (title, y_axis, size_x, size_y, loc, filename) = \ | |
| 584 ('Geometric Mean of benchmark %s performance on %s ' % | |
| 585 (self.test.platform_type, utils.GuessOS()), 'Speed (bigger = better)', | |
| 586 16, 5, 'lower left', 'avg'+png_filename) | |
| 587 clear_axis = True | |
| 588 for platform in self.test.platform_list: | |
| 589 for version in self.test.versions: | |
| 590 if self.test.is_valid_combination(platform, version): | |
| 591 for metric in self.test.extra_metrics: | |
| 592 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, | |
| 593 filename, [platform], [version], | |
| 594 [metric], clear_axis) | |
| 595 clear_axis = False | |
| 596 | |
| 597 def plot_results(self, png_filename): | |
| 598 self.plot_all_perf(png_filename) | |
| 599 self.plot_avg_perf('2' + png_filename) | |
| 600 | |
| 601 | |
| 602 class CommonCommandLineTest(RuntimePerformanceTest): | |
| 603 """Run the basic performance tests (Benchpress, some V8 benchmarks) from the | |
| 604 command line.""" | |
| 605 | |
| 606 def __init__(self, test_runner): | |
| 607 """Args: | |
| 608 test_runner: Reference to the object that notfies this test when to | |
| 609 run.""" | |
| 610 super(CommonCommandLineTest, self).__init__( | |
| 611 self.name(), ['commandline'], | |
| 612 'command line', ['js', 'frog'], self.get_standalone_benchmarks(), | |
| 613 test_runner, self.CommonCommandLineTester(self), | |
| 614 self.CommonCommandLineFileProcessor(self), | |
| 615 build_targets=['create_sdk', 'dart2js']) | |
| 616 | |
| 617 @staticmethod | |
| 618 def name(): | |
| 619 return 'cl-perf' | |
| 620 | |
| 621 @staticmethod | |
| 622 def get_standalone_benchmarks(): | |
| 623 return ['Mandelbrot', 'DeltaBlue', 'Richards', 'NBody', 'BinaryTrees', | |
| 624 'Fannkuch', 'Meteor', 'BubbleSort', 'Fibonacci', 'Loop', 'Permute', | |
| 625 'Queens', 'QuickSort', 'Recurse', 'Sieve', 'Sum', 'Tak', 'Takl', 'Towers', | |
| 626 'TreeSort'] | |
| 627 | |
| 628 class CommonCommandLineTester(Tester): | |
| 629 def run_tests(self): | |
| 630 """Run a performance test on our updated system.""" | |
| 631 os.chdir('frog') | |
| 632 self.test.trace_file = os.path.join( | |
| 633 '..', 'tools', 'testing', 'perf_testing', | |
| 634 self.test.result_folder_name, 'result' + self.test.cur_time) | |
| 635 self.test.test_runner.run_cmd(['python', os.path.join('benchmarks', | |
| 636 'perf_tests.py')], self.test.trace_file) | |
| 637 os.chdir('..') | |
| 638 | |
| 639 class CommonCommandLineFileProcessor(Processor): | |
| 640 def process_file(self, afile): | |
| 641 """Pull all the relevant information out of a given tracefile. | |
| 642 | |
| 643 Args: | |
| 644 afile: The filename string we will be processing.""" | |
| 645 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', | |
| 646 'testing', 'perf_testing')) | |
| 647 f = open(os.path.join(self.test.result_folder_name, afile)) | |
| 648 tabulate_data = False | |
| 649 revision_num = 0 | |
| 650 for line in f.readlines(): | |
| 651 if 'Revision' in line: | |
| 652 revision_num = int(line.split()[1]) | |
| 653 elif 'Benchmark' in line: | |
| 654 tabulate_data = True | |
| 655 elif tabulate_data: | |
| 656 tokens = line.split() | |
| 657 if len(tokens) < 4 or tokens[0] not in self.test.benchmarks: | |
| 658 #Done tabulating data. | |
| 659 break | |
| 660 js_value = float(tokens[1]) | |
| 661 frog_value = float(tokens[3]) | |
| 662 if js_value == 0 or frog_value == 0: | |
| 663 #Then there was an error when this performance test was run. Do not | |
| 664 #count it in our numbers. | |
| 665 return | |
| 666 benchmark = tokens[0] | |
| 667 self.test.revision_dict['commandline']['js'][benchmark] += \ | |
| 668 [revision_num] | |
| 669 self.test.values_dict['commandline']['js'][benchmark] += [js_value] | |
| 670 self.test.revision_dict['commandline']['frog'][benchmark] += \ | |
| 671 [revision_num] | |
| 672 self.test.values_dict['commandline']['frog'][benchmark] += \ | |
| 673 [frog_value] | |
| 674 f.close() | |
| 675 | |
| 676 self.calculate_geometric_mean('commandline', 'frog', revision_num) | |
| 677 self.calculate_geometric_mean('commandline', 'js', revision_num) | |
| 678 | |
| 679 | 386 |
| 680 class BrowserTester(Tester): | 387 class BrowserTester(Tester): |
| 681 @staticmethod | 388 @staticmethod |
| 682 def get_browsers(): | 389 def get_browsers(): |
| 683 browsers = ['dartium', 'ff', 'chrome'] | 390 browsers = ['dartium', 'ff', 'chrome'] |
| 684 has_shell = False | 391 has_shell = False |
| 685 if platform.system() == 'Darwin': | 392 if platform.system() == 'Darwin': |
| 686 browsers += ['safari'] | 393 browsers += ['safari'] |
| 687 if platform.system() == 'Windows': | 394 if platform.system() == 'Windows': |
| 688 browsers += ['ie'] | 395 browsers += ['ie'] |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 779 else: | 486 else: |
| 780 results = line.split('<br />') | 487 results = line.split('<br />') |
| 781 for result in results: | 488 for result in results: |
| 782 name_and_score = result.split(':') | 489 name_and_score = result.split(':') |
| 783 if len(name_and_score) < 2: | 490 if len(name_and_score) < 2: |
| 784 break | 491 break |
| 785 name = name_and_score[0].strip() | 492 name = name_and_score[0].strip() |
| 786 score = name_and_score[1].strip() | 493 score = name_and_score[1].strip() |
| 787 if version == 'js' or version == 'v8': | 494 if version == 'js' or version == 'v8': |
| 788 version = 'js' | 495 version = 'js' |
| 789 bench_dict = self.test.values_dict[browser]['js'] | 496 self.report_results(name, score, browser, version, revision_num) |
| 790 else: | |
| 791 bench_dict = self.test.values_dict[browser]['frog'] | |
| 792 bench_dict[name] += [float(score)] | |
| 793 self.test.revision_dict[browser][version][name] += [revision_num] | |
| 794 | 497 |
| 795 f.close() | 498 f.close() |
| 796 self.calculate_geometric_mean(browser, version, revision_num) | 499 |
| 797 | 500 |
| 798 class DromaeoTester(Tester): | 501 class DromaeoTester(Tester): |
| 799 DROMAEO_BENCHMARKS = { | 502 DROMAEO_BENCHMARKS = { |
| 800 'attr': ('attributes', [ | 503 'attr': ('attributes', [ |
| 801 'getAttribute', | 504 'getAttribute', |
| 802 'element.property', | 505 'element.property', |
| 803 'setAttribute', | 506 'setAttribute', |
| 804 'element.property = value']), | 507 'element.property = value']), |
| 805 'modify': ('modify', [ | 508 'modify': ('modify', [ |
| 806 'createElement', | 509 'createElement', |
| (...skipping 13 matching lines...) Expand all Loading... |
| 820 'getElementsByName', | 523 'getElementsByName', |
| 821 'getElementsByName (not in document)']), | 524 'getElementsByName (not in document)']), |
| 822 'traverse': ('traverse', [ | 525 'traverse': ('traverse', [ |
| 823 'firstChild', | 526 'firstChild', |
| 824 'lastChild', | 527 'lastChild', |
| 825 'nextSibling', | 528 'nextSibling', |
| 826 'previousSibling', | 529 'previousSibling', |
| 827 'childNodes']) | 530 'childNodes']) |
| 828 } | 531 } |
| 829 | 532 |
| 830 # Use legal appengine filenames for benchmark names. | 533 # Use filenames that don't have unusual characters for benchmark names.» |
| 831 @staticmethod | 534 @staticmethod» |
| 832 def legalize_filename(str): | 535 def legalize_filename(str):» |
| 833 remap = { | 536 remap = {» |
| 834 ' ': '_', | 537 ' ': '_',» |
| 835 '(': '_', | 538 '(': '_',» |
| 836 ')': '_', | 539 ')': '_',» |
| 837 '*': 'ALL', | 540 '*': 'ALL',» |
| 838 '=': 'ASSIGN', | 541 '=': 'ASSIGN',» |
| 839 } | 542 }» |
| 840 for (old, new) in remap.iteritems(): | 543 for (old, new) in remap.iteritems():» |
| 841 str = str.replace(old, new) | 544 str = str.replace(old, new)» |
| 842 return str | 545 return str |
| 843 | 546 |
| 844 # TODO(vsm): This is a hack to skip breaking tests. Triage this | 547 # TODO(vsm): This is a hack to skip breaking tests. Triage this |
| 845 # failure properly. The modify suite fails on 32-bit chrome on | 548 # failure properly. The modify suite fails on 32-bit chrome on |
| 846 # the mac. | 549 # the mac. |
| 847 @staticmethod | 550 @staticmethod |
| 848 def get_valid_dromaeo_tags(): | 551 def get_valid_dromaeo_tags(): |
| 849 tags = [tag for (tag, _) in DromaeoTester.DROMAEO_BENCHMARKS.values()] | 552 tags = [tag for (tag, _) in DromaeoTester.DROMAEO_BENCHMARKS.values()] |
| 850 if platform.system() == 'Darwin': | 553 if platform.system() == 'Darwin': |
| 851 tags.remove('modify') | 554 tags.remove('modify') |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 894 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 597 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') |
| 895 current_path = os.getcwd() | 598 current_path = os.getcwd() |
| 896 os.chdir(dromaeo_path) | 599 os.chdir(dromaeo_path) |
| 897 self.test.test_runner.run_cmd(['python', 'generate_frog_tests.py']) | 600 self.test.test_runner.run_cmd(['python', 'generate_frog_tests.py']) |
| 898 os.chdir(current_path) | 601 os.chdir(current_path) |
| 899 | 602 |
| 900 versions = DromaeoTester.get_dromaeo_versions() | 603 versions = DromaeoTester.get_dromaeo_versions() |
| 901 | 604 |
| 902 for browser in BrowserTester.get_browsers(): | 605 for browser in BrowserTester.get_browsers(): |
| 903 for version_name in versions: | 606 for version_name in versions: |
| 904 if not self.test.is_valid_combination(browser, version): | 607 if not self.test.is_valid_combination(browser, version_name): |
| 905 continue | 608 continue |
| 906 version = DromaeoTest.DromaeoPerfTester.get_dromaeo_url_query( | 609 version = DromaeoTest.DromaeoPerfTester.get_dromaeo_url_query( |
| 907 browser, version_name) | 610 browser, version_name) |
| 908 self.test.trace_file = os.path.join( | 611 self.test.trace_file = os.path.join( |
| 909 'tools', 'testing', 'perf_testing', self.test.result_folder_name, | 612 'tools', 'testing', 'perf_testing', self.test.result_folder_name, |
| 910 'dromaeo-%s-%s-%s' % (self.test.cur_time, browser, version_name)) | 613 'dromaeo-%s-%s-%s' % (self.test.cur_time, browser, version_name)) |
| 911 self.add_svn_revision_to_trace(self.test.trace_file, browser) | 614 self.add_svn_revision_to_trace(self.test.trace_file, browser) |
| 912 file_path = '"%s"' % os.path.join(os.getcwd(), dromaeo_path, | 615 file_path = '"%s"' % os.path.join(os.getcwd(), dromaeo_path, |
| 913 'index-js.html?%s' % version) | 616 'index-js.html?%s' % version) |
| 914 self.test.test_runner.run_cmd( | 617 self.test.test_runner.run_cmd( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 926 return '|'.join([ '%s&%s' % (version, tag) for tag in tags]) | 629 return '|'.join([ '%s&%s' % (version, tag) for tag in tags]) |
| 927 | 630 |
| 928 | 631 |
| 929 class DromaeoFileProcessor(Processor): | 632 class DromaeoFileProcessor(Processor): |
| 930 def process_file(self, afile): | 633 def process_file(self, afile): |
| 931 """Comb through the html to find the performance results.""" | 634 """Comb through the html to find the performance results.""" |
| 932 parts = afile.split('-') | 635 parts = afile.split('-') |
| 933 browser = parts[2] | 636 browser = parts[2] |
| 934 version = parts[3] | 637 version = parts[3] |
| 935 | 638 |
| 936 bench_dict = self.test.values_dict[browser][version] | |
| 937 | |
| 938 f = open(os.path.join(self.test.result_folder_name, afile)) | 639 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 939 lines = f.readlines() | 640 lines = f.readlines() |
| 940 i = 0 | 641 i = 0 |
| 941 revision_num = 0 | 642 revision_num = 0 |
| 942 revision_pattern = r'Revision: (\d+)' | 643 revision_pattern = r'Revision: (\d+)' |
| 943 suite_pattern = r'<div class="result-item done">(.+?)</ol></div>' | 644 suite_pattern = r'<div class="result-item done">(.+?)</ol></div>' |
| 944 result_pattern = r'<b>(.+?)</b>(.+?)<small> runs/s(.+)' | 645 result_pattern = r'<b>(.+?)</b>(.+?)<small> runs/s(.+)' |
| 945 | 646 |
| 946 for line in lines: | 647 for line in lines: |
| 947 rev = re.match(revision_pattern, line.strip()) | 648 rev = re.match(revision_pattern, line.strip()) |
| 948 if rev: | 649 if rev: |
| 949 revision_num = int(rev.group(1)) | 650 revision_num = int(rev.group(1)) |
| 950 continue | 651 continue |
| 951 | 652 |
| 952 suite_results = re.findall(suite_pattern, line) | 653 suite_results = re.findall(suite_pattern, line) |
| 953 if suite_results: | 654 if suite_results: |
| 954 for suite_result in suite_results: | 655 for suite_result in suite_results: |
| 955 results = re.findall(r'<li>(.*?)</li>', suite_result) | 656 results = re.findall(r'<li>(.*?)</li>', suite_result) |
| 956 if results: | 657 if results: |
| 957 for result in results: | 658 for result in results: |
| 958 r = re.match(result_pattern, result) | 659 r = re.match(result_pattern, result) |
| 959 name = DromaeoTester.legalize_filename( | 660 name = DromaeoTester.legalize_filename(r.group(1).strip(':')) |
| 960 r.group(1).strip(':')) | |
| 961 score = float(r.group(2)) | 661 score = float(r.group(2)) |
| 962 bench_dict[name] += [float(score)] | 662 self.report_results(name, score, browser, version, revision_num) |
| 963 self.test.revision_dict[browser][version][name] += \ | |
| 964 [revision_num] | |
| 965 | 663 |
| 966 f.close() | 664 f.close() |
| 967 self.calculate_geometric_mean(browser, version, revision_num) | |
| 968 | 665 |
| 969 | 666 |
| 970 class DromaeoSizeTest(Test): | 667 class DromaeoSizeTest(Test): |
| 971 """Run tests to determine the compiled file output size of Dromaeo.""" | 668 """Run tests to determine the compiled file output size of Dromaeo.""" |
| 972 def __init__(self, test_runner): | 669 def __init__(self, test_runner): |
| 973 super(DromaeoSizeTest, self).__init__( | 670 super(DromaeoSizeTest, self).__init__( |
| 974 self.name(), | 671 self.name(), |
| 975 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | 672 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], |
| 976 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, | 673 DromaeoTester.DROMAEO_BENCHMARKS.keys(), test_runner, |
| 977 self.DromaeoSizeTester(self), | 674 self.DromaeoSizeTester(self), |
| 978 self.DromaeoSizeProcessor(self), | 675 self.DromaeoSizeProcessor(self)) |
| 979 self.DromaeoSizeGrapher(self), extra_metrics=['sum']) | |
| 980 | 676 |
| 981 @staticmethod | 677 @staticmethod |
| 982 def name(): | 678 def name(): |
| 983 return 'dromaeo-size' | 679 return 'dromaeo-size' |
| 984 | 680 |
| 985 | 681 |
| 986 class DromaeoSizeTester(DromaeoTester): | 682 class DromaeoSizeTester(DromaeoTester): |
| 987 def run_tests(self): | 683 def run_tests(self): |
| 988 # Build tests. | 684 # Build tests. |
| 989 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') | 685 dromaeo_path = os.path.join('samples', 'third_party', 'dromaeo') |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1030 js_size = os.path.getsize(os.path.join(frog_path, name)) | 726 js_size = os.path.getsize(os.path.join(frog_path, name)) |
| 1031 except OSError: | 727 except OSError: |
| 1032 pass #If compilation failed, continue on running other tests. | 728 pass #If compilation failed, continue on running other tests. |
| 1033 | 729 |
| 1034 total_size[variant] += js_size | 730 total_size[variant] += js_size |
| 1035 self.test.test_runner.run_cmd( | 731 self.test.test_runner.run_cmd( |
| 1036 ['echo', 'Size (%s, %s): %s' % (variant, suite, str(js_size))], | 732 ['echo', 'Size (%s, %s): %s' % (variant, suite, str(js_size))], |
| 1037 self.test.trace_file, append=True) | 733 self.test.trace_file, append=True) |
| 1038 | 734 |
| 1039 self.test.test_runner.run_cmd( | 735 self.test.test_runner.run_cmd( |
| 1040 ['echo', 'Size (dart, %s): %s' % (total_dart_size, | 736 ['echo', 'Size (dart, %s): %s' % (total_dart_size, 'sum')], |
| 1041 self.test.extra_metrics[0])], | |
| 1042 self.test.trace_file, append=True) | 737 self.test.trace_file, append=True) |
| 1043 for (variant, _) in variants: | 738 for (variant, _) in variants: |
| 1044 self.test.test_runner.run_cmd( | 739 self.test.test_runner.run_cmd( |
| 1045 ['echo', 'Size (%s, %s): %s' % (variant, self.test.extra_metrics[0], | 740 ['echo', 'Size (%s, %s): %s' % (variant, 'sum', |
| 1046 total_size[variant])], | 741 total_size[variant])], |
| 1047 self.test.trace_file, append=True) | 742 self.test.trace_file, append=True) |
| 1048 | 743 |
| 744 |
| 1049 class DromaeoSizeProcessor(Processor): | 745 class DromaeoSizeProcessor(Processor): |
| 1050 def process_file(self, afile): | 746 def process_file(self, afile): |
| 1051 """Pull all the relevant information out of a given tracefile. | 747 """Pull all the relevant information out of a given tracefile. |
| 1052 | 748 |
| 1053 Args: | 749 Args: |
| 1054 afile: is the filename string we will be processing.""" | 750 afile: is the filename string we will be processing.""" |
| 1055 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', | 751 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', |
| 1056 'testing', 'perf_testing')) | 752 'testing', 'perf_testing')) |
| 1057 f = open(os.path.join(self.test.result_folder_name, afile)) | 753 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 1058 tabulate_data = False | 754 tabulate_data = False |
| 1059 revision_num = 0 | 755 revision_num = 0 |
| 1060 revision_pattern = r'Revision: (\d+)' | 756 revision_pattern = r'Revision: (\d+)' |
| 1061 result_pattern = r'Size \((\w+), ([a-zA-Z0-9-]+)\): (\d+)' | 757 result_pattern = r'Size \((\w+), ([a-zA-Z0-9-]+)\): (\d+)' |
| 1062 | 758 |
| 1063 for line in f.readlines(): | 759 for line in f.readlines(): |
| 1064 rev = re.match(revision_pattern, line.strip()) | 760 rev = re.match(revision_pattern, line.strip()) |
| 1065 if rev: | 761 if rev: |
| 1066 revision_num = int(rev.group(1)) | 762 revision_num = int(rev.group(1)) |
| 1067 continue | 763 continue |
| 1068 | 764 |
| 1069 result = re.match(result_pattern, line.strip()) | 765 result = re.match(result_pattern, line.strip()) |
| 1070 if result: | 766 if result: |
| 1071 variant = result.group(1) | 767 variant = result.group(1) |
| 1072 metric = result.group(2) | 768 metric = result.group(2) |
| 1073 num = result.group(3) | 769 num = result.group(3) |
| 1074 if num.find('.') == -1: | 770 if num.find('.') == -1: |
| 1075 num = int(num) | 771 num = int(num) |
| 1076 else: | 772 else: |
| 1077 num = float(num) | 773 num = float(num) |
| 1078 self.test.values_dict['browser'][variant][metric] += [num] | 774 self.report_results(metric, num, 'browser', variant, revision_num) |
| 1079 self.test.revision_dict['browser'][variant][metric] += [revision_num] | |
| 1080 | 775 |
| 1081 f.close() | 776 f.close() |
| 1082 class DromaeoSizeGrapher(Grapher): | |
| 1083 def plot_results(self, png_filename): | |
| 1084 self.style_and_save_perf_plot( | |
| 1085 'Compiled Dromaeo Sizes', | |
| 1086 'Size (in bytes)', 10, 10, 'lower left', png_filename, | |
| 1087 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1088 DromaeoTester.DROMAEO_BENCHMARKS.keys()) | |
| 1089 | |
| 1090 self.style_and_save_perf_plot( | |
| 1091 'Compiled Dromaeo Sizes', | |
| 1092 'Size (in bytes)', 10, 10, 'lower left', '2' + png_filename, | |
| 1093 ['browser'], ['dart', 'frog_dom', 'frog_html', 'frog_htmlidiomatic'], | |
| 1094 [self.test.extra_metrics[0]]) | |
| 1095 | 777 |
| 1096 | 778 |
| 1097 class CompileTimeAndSizeTest(Test): | 779 class CompileTimeAndSizeTest(Test): |
| 1098 """Run tests to determine how long minfrog takes to compile, and the compiled | 780 """Run tests to determine how long minfrog takes to compile, and the compiled |
| 1099 file output size of some benchmarking files.""" | 781 file output size of some benchmarking files.""" |
| 1100 def __init__(self, test_runner): | 782 def __init__(self, test_runner): |
| 1101 """Reference to the test_runner object that notifies us when to begin | 783 """Reference to the test_runner object that notifies us when to begin |
| 1102 testing.""" | 784 testing.""" |
| 1103 super(CompileTimeAndSizeTest, self).__init__( | 785 super(CompileTimeAndSizeTest, self).__init__( |
| 1104 self.name(), ['commandline'], ['frog'], | 786 self.name(), ['commandline'], ['frog'], |
| 1105 ['Compiling on Dart VM', 'Bootstrapping', 'minfrog', 'swarm', 'total'], | 787 ['Compiling on Dart VM', 'Bootstrapping', 'minfrog', 'swarm', 'total'], |
| 1106 test_runner, self.CompileTester(self), | 788 test_runner, self.CompileTester(self), |
| 1107 self.CompileProcessor(self), self.CompileGrapher(self)) | 789 self.CompileProcessor(self)) |
| 1108 self.dart_compiler = os.path.join( | 790 self.dart_compiler = os.path.join( |
| 1109 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | 791 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), |
| 1110 'release', 'ia32'), 'dart-sdk', 'bin', 'frogc') | 792 'release', 'ia32'), 'dart-sdk', 'bin', 'frogc') |
| 1111 _suffix = '' | 793 _suffix = '' |
| 1112 if platform.system() == 'Windows': | 794 if platform.system() == 'Windows': |
| 1113 _suffix = '.exe' | 795 _suffix = '.exe' |
| 1114 self.dart_vm = os.path.join( | 796 self.dart_vm = os.path.join( |
| 1115 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), | 797 DART_INSTALL_LOCATION, utils.GetBuildRoot(utils.GuessOS(), |
| 1116 'release', 'ia32'), 'dart-sdk', 'bin','dart' + _suffix) | 798 'release', 'ia32'), 'dart-sdk', 'bin','dart' + _suffix) |
| 1117 self.failure_threshold = { | 799 self.failure_threshold = { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1177 self.test.test_runner.run_cmd( | 859 self.test.test_runner.run_cmd( |
| 1178 ['echo', '%d Generated checked swarm size' % swarm_size], | 860 ['echo', '%d Generated checked swarm size' % swarm_size], |
| 1179 self.test.trace_file, append=True) | 861 self.test.trace_file, append=True) |
| 1180 | 862 |
| 1181 self.test.test_runner.run_cmd( | 863 self.test.test_runner.run_cmd( |
| 1182 ['echo', '%d Generated checked total size' % total_size], | 864 ['echo', '%d Generated checked total size' % total_size], |
| 1183 self.test.trace_file, append=True) | 865 self.test.trace_file, append=True) |
| 1184 | 866 |
| 1185 #Revert our newly built minfrog to prevent conflicts when we update | 867 #Revert our newly built minfrog to prevent conflicts when we update |
| 1186 self.test.test_runner.run_cmd( | 868 self.test.test_runner.run_cmd( |
| 1187 ['svn', 'revert', os.path.join(os.getcwd(), 'frog', 'minfrog')]) | 869 ['svn', 'revert', os.path.join(os.getcwd(), 'minfrog')]) |
| 1188 | |
| 1189 os.chdir('..') | 870 os.chdir('..') |
| 1190 | 871 |
| 872 |
| 1191 class CompileProcessor(Processor): | 873 class CompileProcessor(Processor): |
| 874 |
| 1192 def process_file(self, afile): | 875 def process_file(self, afile): |
| 1193 """Pull all the relevant information out of a given tracefile. | 876 """Pull all the relevant information out of a given tracefile. |
| 1194 | 877 |
| 1195 Args: | 878 Args: |
| 1196 afile: is the filename string we will be processing.""" | 879 afile: is the filename string we will be processing.""" |
| 1197 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', | 880 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', |
| 1198 'testing', 'perf_testing')) | 881 'testing', 'perf_testing')) |
| 1199 f = open(os.path.join(self.test.result_folder_name, afile)) | 882 f = open(os.path.join(self.test.result_folder_name, afile)) |
| 1200 tabulate_data = False | 883 tabulate_data = False |
| 1201 revision_num = 0 | 884 revision_num = 0 |
| 1202 for line in f.readlines(): | 885 for line in f.readlines(): |
| 1203 tokens = line.split() | 886 tokens = line.split() |
| 1204 if 'Revision' in line: | 887 if 'Revision' in line: |
| 1205 revision_num = int(line.split()[1]) | 888 revision_num = int(line.split()[1]) |
| 1206 else: | 889 else: |
| 1207 for metric in self.test.values_list: | 890 for metric in self.test.values_list: |
| 1208 if metric in line: | 891 if metric in line: |
| 1209 num = tokens[0] | 892 num = tokens[0] |
| 1210 if num.find('.') == -1: | 893 if num.find('.') == -1: |
| 1211 num = int(num) | 894 num = int(num) |
| 1212 else: | 895 else: |
| 1213 num = float(num) | 896 num = float(num) |
| 1214 self.test.values_dict['commandline']['frog'][metric] += [num] | 897 self.report_results(metric, num, 'commandline', 'frog', |
| 1215 self.test.revision_dict['commandline']['frog'][metric] += \ | 898 revision_num) |
| 1216 [revision_num] | |
| 1217 | |
| 1218 if revision_num != 0: | |
| 1219 for metric in self.test.values_list: | |
| 1220 self.test.revision_dict['commandline']['frog'][metric].pop() | |
| 1221 self.test.revision_dict['commandline']['frog'][metric] += \ | |
| 1222 [revision_num] | |
| 1223 # Fill in 0 if compilation failed. | |
| 1224 if self.test.values_dict['commandline']['frog'][metric][-1] < \ | |
| 1225 self.test.failure_threshold[metric]: | |
| 1226 self.test.values_dict['commandline']['frog'][metric] += [0] | |
| 1227 self.test.revision_dict['commandline']['frog'][metric] += \ | |
| 1228 [revision_num] | |
| 1229 | 899 |
| 1230 f.close() | 900 f.close() |
| 1231 | 901 |
| 1232 class CompileGrapher(Grapher): | |
| 1233 | |
| 1234 def plot_results(self, png_filename): | |
| 1235 self.style_and_save_perf_plot( | |
| 1236 'Compiled minfrog Sizes', 'Size (in bytes)', 10, 10, 'lower left', | |
| 1237 png_filename, ['commandline'], ['frog'], | |
| 1238 ['swarm', 'total', 'minfrog']) | |
| 1239 | |
| 1240 self.style_and_save_perf_plot( | |
| 1241 'Time to compile and bootstrap', | |
| 1242 'Seconds', 10, 10, 'lower left', '2' + png_filename, ['commandline'], | |
| 1243 ['frog'], ['Bootstrapping', 'Compiling on Dart VM']) | |
| 1244 | |
| 1245 | 902 |
| 1246 class TestBuilder(object): | 903 class TestBuilder(object): |
| 1247 """Construct the desired test object.""" | 904 """Construct the desired test object.""" |
| 1248 available_suites = dict((suite.name(), suite) for suite in [ | 905 available_suites = dict((suite.name(), suite) for suite in [ |
| 1249 CommonCommandLineTest, CompileTimeAndSizeTest, | 906 CompileTimeAndSizeTest, CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) |
| 1250 CommonBrowserTest, DromaeoTest, DromaeoSizeTest]) | |
| 1251 | 907 |
| 1252 @staticmethod | 908 @staticmethod |
| 1253 def make_test(test_name, test_runner): | 909 def make_test(test_name, test_runner): |
| 1254 return TestBuilder.available_suites[test_name](test_runner) | 910 return TestBuilder.available_suites[test_name](test_runner) |
| 1255 | 911 |
| 1256 @staticmethod | 912 @staticmethod |
| 1257 def available_suite_names(): | 913 def available_suite_names(): |
| 1258 return TestBuilder.available_suites.keys() | 914 return TestBuilder.available_suites.keys() |
| 1259 | 915 |
| 1260 | 916 |
| 1261 def main(): | 917 def main(): |
| 1262 runner = TestRunner() | 918 runner = TestRunner() |
| 1263 continuous = runner.parse_args() | 919 continuous = runner.parse_args() |
| 1264 if continuous: | 920 if continuous: |
| 1265 while True: | 921 while True: |
| 1266 if runner.has_new_code(): | 922 if runner.has_new_code(): |
| 1267 runner.run_test_sequence() | 923 runner.run_test_sequence() |
| 1268 else: | 924 else: |
| 1269 time.sleep(200) | 925 time.sleep(200) |
| 1270 else: | 926 else: |
| 1271 runner.run_test_sequence() | 927 runner.run_test_sequence() |
| 1272 | 928 |
| 1273 if __name__ == '__main__': | 929 if __name__ == '__main__': |
| 1274 main() | 930 main() |
| OLD | NEW |