| 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 | 8 import getpass |
| 9 import math | 9 import math |
| 10 from matplotlib.font_manager import FontProperties | 10 from matplotlib.font_manager import FontProperties |
| 11 import matplotlib.pyplot as plt | 11 import matplotlib.pyplot as plt |
| 12 import optparse | 12 import optparse |
| 13 import os | 13 import os |
| 14 from os.path import dirname, abspath | 14 from os.path import dirname, abspath |
| 15 import platform | 15 import platform |
| 16 import shutil | 16 import shutil |
| 17 import stat | 17 import stat |
| 18 import subprocess | 18 import subprocess |
| 19 import sys | 19 import sys |
| 20 import time | 20 import time |
| 21 import traceback | 21 import traceback |
| 22 | 22 |
| 23 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) | 23 TOOLS_PATH = os.path.join(dirname(dirname(dirname(abspath(__file__))))) |
| 24 sys.path.append(TOOLS_PATH) | 24 sys.path.append(TOOLS_PATH) |
| 25 import utils | 25 import utils |
| 26 | 26 |
| 27 """This script runs to track performance and correctness progress of | 27 """This script runs to track performance and size progress of |
| 28 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 |
| 29 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.""" |
| 30 | 30 |
| 31 DART_INSTALL_LOCATION = os.path.join(dirname(abspath(__file__)), | 31 DART_INSTALL_LOCATION = os.path.join(dirname(abspath(__file__)), |
| 32 '..', '..', '..') | 32 '..', '..', '..') |
| 33 JS_MEAN = 'JS Mean' | 33 JS_MEAN = 'JS Mean' |
| 34 FROG_MEAN = 'frog js Mean' | 34 FROG_MEAN = 'frog js Mean' |
| 35 COMMAND_LINE = 'commandline' | 35 COMMAND_LINE = 'commandline' |
| 36 JS = 'js' | 36 JS = 'js' |
| 37 FROG = 'frog' | 37 FROG = 'frog' |
| 38 JS_AND_FROG = [JS, FROG] | 38 JS_AND_FROG = [JS, FROG] |
| 39 CORRECTNESS = 'Percent passing' | |
| 40 COLORS = ['blue', 'green', 'red', 'cyan', 'magenta', 'black'] | 39 COLORS = ['blue', 'green', 'red', 'cyan', 'magenta', 'black'] |
| 41 GRAPH_OUT_DIR = 'graphs' | 40 GRAPH_OUT_DIR = 'graphs' |
| 42 | 41 |
| 43 BROWSER_PERF = 'browser-perf' | 42 BROWSER_PERF = 'browser-perf' |
| 44 TIME_SIZE = 'code-time-size' | 43 TIME_SIZE = 'code-time-size' |
| 45 CL_PERF = 'cl-results' | 44 CL_PERF = 'cl-results' |
| 46 BROWSER_CORRECTNESS = 'browser-correctness' | |
| 47 | 45 |
| 48 SLEEP_TIME = 200 | 46 SLEEP_TIME = 200 |
| 49 VERBOSE = False | 47 VERBOSE = False |
| 50 HAS_SHELL = False | 48 HAS_SHELL = False |
| 51 if platform.system() == 'Windows': | 49 if platform.system() == 'Windows': |
| 52 # On Windows, shell must be true to get the correct environment variables. | 50 # On Windows, shell must be true to get the correct environment variables. |
| 53 HAS_SHELL = True | 51 HAS_SHELL = True |
| 54 | 52 |
| 55 """First, some utility methods.""" | 53 """First, some utility methods.""" |
| 56 | 54 |
| 57 def run_cmd(cmd_list, outfile=None, append=False): | 55 def run_cmd(cmd_list, outfile=None, append=False): |
| 58 """Run the specified command and print out any output to stdout. | 56 """Run the specified command and print out any output to stdout. |
| 59 | 57 |
| 60 Args: | 58 Args: |
| 61 cmd_list: a list of strings that make up the command to run | 59 cmd_list: a list of strings that make up the command to run |
| 62 outfile: a string indicating the name of the file that we should write | 60 outfile: a string indicating the name of the file that we should write |
| 63 stdout to | 61 stdout to |
| 64 append: True if we want to append to the file instead of overwriting it""" | 62 append: True if we want to append to the file instead of overwriting it""" |
| 65 if VERBOSE: | 63 if VERBOSE: |
| 66 print ' '.join(cmd_list) | 64 print ' '.join(cmd_list) |
| 67 out = subprocess.PIPE | 65 out = subprocess.PIPE |
| 68 if outfile: | 66 if outfile: |
| 69 mode = 'w' | 67 mode = 'w' |
| 70 if append: | 68 if append: |
| 71 mode = 'a' | 69 mode = 'a' |
| 72 out = open(outfile, mode) | 70 out = open(outfile, mode) |
| 73 if append: | 71 if append: |
| 74 # Annoying Windows "feature" -- append doesn't actually append unless you | 72 # Annoying Windows "feature" -- append doesn't actually append unless you |
| 75 # explicitly go to the end of the file. | 73 # explicitly go to the end of the file. |
| 76 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html | 74 # http://mail.python.org/pipermail/python-list/2009-October/1221859.html |
| 77 out.seek(0, os.SEEK_END) | 75 out.seek(0, os.SEEK_END) |
| 78 p = subprocess.Popen(cmd_list, stdout = out, stderr = subprocess.PIPE, | 76 p = subprocess.Popen(cmd_list, stdout = out, stderr = subprocess.PIPE, |
| 79 shell=HAS_SHELL) | 77 shell=HAS_SHELL) |
| 80 output, not_used = p.communicate(); | 78 output, not_used = p.communicate(); |
| 81 if output: | 79 if output: |
| 82 print output | 80 print output |
| 83 return output | 81 return output |
| 84 | 82 |
| 85 def time_cmd(cmd): | 83 def time_cmd(cmd): |
| 86 """Determine the amount of (real) time it takes to execute a given command.""" | 84 """Determine the amount of (real) time it takes to execute a given command.""" |
| 87 start = time.time() | 85 start = time.time() |
| 88 run_cmd(cmd) | 86 run_cmd(cmd) |
| 89 return time.time() - start | 87 return time.time() - start |
| 90 | 88 |
| 91 def sync_and_build(): | 89 def sync_and_build(): |
| 92 """Make sure we have the latest version of of the repo, and build it. We | 90 """Make sure we have the latest version of of the repo, and build it. We |
| 93 begin and end standing in DART_INSTALL_LOCATION. | 91 begin and end standing in DART_INSTALL_LOCATION. |
| 94 | 92 |
| 95 Returns: | 93 Returns: |
| 96 err_code = 1 if there was a problem building.""" | 94 err_code = 1 if there was a problem building.""" |
| 97 os.chdir(DART_INSTALL_LOCATION) | 95 os.chdir(DART_INSTALL_LOCATION) |
| 98 #Revert our newly built minfrog to prevent conflicts when we update | 96 #Revert our newly built minfrog to prevent conflicts when we update |
| 99 run_cmd(['svn', 'revert', os.path.join(os.getcwd(), 'frog', 'minfrog')]) | 97 run_cmd(['svn', 'revert', os.path.join(os.getcwd(), 'frog', 'minfrog')]) |
| 100 | 98 |
| 101 run_cmd(['gclient', 'sync']) | 99 run_cmd(['gclient', 'sync']) |
| 102 | 100 |
| 103 # On Windows, the output directory is marked as "Read Only," which causes an | 101 # On Windows, the output directory is marked as "Read Only," which causes an |
| 104 # error to be thrown when we use shutil.rmtree. This helper function changes | 102 # error to be thrown when we use shutil.rmtree. This helper function changes |
| 105 # the permissions so we can still delete the directory. | 103 # the permissions so we can still delete the directory. |
| 106 def on_rm_error(func, path, exc_info): | 104 def on_rm_error(func, path, exc_info): |
| 107 if os.path.exists(path): | 105 if os.path.exists(path): |
| 108 os.chmod(path, stat.S_IWRITE) | 106 os.chmod(path, stat.S_IWRITE) |
| 109 os.unlink(path) | 107 os.unlink(path) |
| 110 # TODO(efortuna): building the sdk locally is a band-aid until all build | 108 # TODO(efortuna): building the sdk locally is a band-aid until all build |
| 111 # platform SDKs are hosted in Google storage. Pull from https://sandbox. | 109 # platform SDKs are hosted in Google storage. Pull from https://sandbox. |
| 112 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk | 110 # google.com/storage/?arg=dart-dump-render-tree#dart-dump-render-tree%2Fsdk |
| 113 # eventually. | 111 # eventually. |
| 114 # TODO(efortuna): Currently always building ia32 architecture because we don't | 112 # TODO(efortuna): Currently always building ia32 architecture because we don't |
| 115 # have test statistics for what's passing on x64. Eliminate arch specification | 113 # have test statistics for what's passing on x64. Eliminate arch specification |
| 116 # when we have tests running on x64, too. | 114 # when we have tests running on x64, too. |
| 117 shutil.rmtree(os.path.join(os.getcwd(), | 115 shutil.rmtree(os.path.join(os.getcwd(), |
| 118 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), | 116 utils.GetBuildRoot(utils.GuessOS(), 'release', 'ia32')), |
| 119 onerror=on_rm_error) | 117 onerror=on_rm_error) |
| 120 lines = run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', 'release', | 118 lines = run_cmd([os.path.join('.', 'tools', 'build.py'), '-m', 'release', |
| 121 '--arch=ia32', 'create_sdk']) | 119 '--arch=ia32', 'create_sdk']) |
| 122 | 120 |
| 123 for line in lines: | 121 for line in lines: |
| 124 if 'BUILD FAILED' in lines: | 122 if 'BUILD FAILED' in lines: |
| 125 # Someone checked in a broken build! Just stop trying to make it work | 123 # Someone checked in a broken build! Just stop trying to make it work |
| 126 # and wait to try again. | 124 # and wait to try again. |
| 127 print 'Broken Build' | 125 print 'Broken Build' |
| 128 return 1 | 126 return 1 |
| 129 return 0 | 127 return 0 |
| 130 | 128 |
| 131 def ensure_output_directory(dir_name): | 129 def ensure_output_directory(dir_name): |
| 132 """Test that the listed directory name exists, and if not, create one for | 130 """Test that the listed directory name exists, and if not, create one for |
| 133 our output to be placed. | 131 our output to be placed. |
| 134 | 132 |
| 135 Args: | 133 Args: |
| 136 dir_name: the directory we will create if it does not exist.""" | 134 dir_name: the directory we will create if it does not exist.""" |
| 137 dir_path = os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 135 dir_path = os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', |
| 138 'perf_testing', dir_name) | 136 'perf_testing', dir_name) |
| 139 if not os.path.exists(dir_path): | 137 if not os.path.exists(dir_path): |
| 140 os.mkdir(dir_path) | 138 os.mkdir(dir_path) |
| 141 print 'Creating output directory ', dir_path | 139 print 'Creating output directory ', dir_path |
| 142 | 140 |
| 143 def has_new_code(): | 141 def has_new_code(): |
| 144 """Tests if there are any newer versions of files on the server.""" | 142 """Tests if there are any newer versions of files on the server.""" |
| 145 os.chdir(DART_INSTALL_LOCATION) | 143 os.chdir(DART_INSTALL_LOCATION) |
| 146 results = run_cmd(['svn', 'st', '-u']) | 144 results = run_cmd(['svn', 'st', '-u']) |
| 147 for line in results: | 145 for line in results: |
| 148 if '*' in line: | 146 if '*' in line: |
| 149 return True | 147 return True |
| 150 return False | 148 return False |
| 151 | 149 |
| 152 def get_browsers(): | 150 def get_browsers(): |
| 153 browsers = ['ff', 'chrome'] | 151 browsers = ['ff', 'chrome'] |
| 154 if platform.system() == 'Darwin': | 152 if platform.system() == 'Darwin': |
| 155 browsers += ['safari'] | 153 browsers += ['safari'] |
| (...skipping 19 matching lines...) Expand all Loading... |
| 175 return 'macos' | 173 return 'macos' |
| 176 else: | 174 else: |
| 177 return 'linux' | 175 return 'linux' |
| 178 | 176 |
| 179 def upload_to_app_engine(username, password): | 177 def upload_to_app_engine(username, password): |
| 180 """Upload our results to our appengine server. | 178 """Upload our results to our appengine server. |
| 181 Arguments: | 179 Arguments: |
| 182 username: App Engine username for uploading data to dartperf.googleplex.com | 180 username: App Engine username for uploading data to dartperf.googleplex.com |
| 183 password: App Engine password | 181 password: App Engine password |
| 184 """ | 182 """ |
| 185 # TODO(efortuna): This is the most basic way to get the data up | 183 # TODO(efortuna): This is the most basic way to get the data up |
| 186 # for others to view. Revisit this once we're serving nicer graphs (Google | 184 # for others to view. Revisit this once we're serving nicer graphs (Google |
| 187 # Chart Tools) and from multiple perfbots and once we're in a position to | 185 # Chart Tools) and from multiple perfbots and once we're in a position to |
| 188 # organize the data in a useful manner(!!). | 186 # organize the data in a useful manner(!!). |
| 189 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 187 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', |
| 190 'perf_testing')) | 188 'perf_testing')) |
| 191 for data in [BROWSER_PERF, TIME_SIZE, CL_PERF]: | 189 for data in [BROWSER_PERF, TIME_SIZE, CL_PERF]: |
| 192 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | 190 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) |
| 193 shutil.rmtree(path, ignore_errors=True) | 191 shutil.rmtree(path, ignore_errors=True) |
| 194 os.makedirs(path) | 192 os.makedirs(path) |
| 195 files = [] | 193 files = [] |
| 196 # Copy the 1000 most recent trace files to be uploaded. | 194 # Copy the 1000 most recent trace files to be uploaded. |
| 197 for f in os.listdir(data): | 195 for f in os.listdir(data): |
| 198 files += [(os.path.getmtime(os.path.join(data, f)), f)] | 196 files += [(os.path.getmtime(os.path.join(data, f)), f)] |
| 199 files.sort() | 197 files.sort() |
| 200 for f in files[-1000:]: | 198 for f in files[-1000:]: |
| 201 shutil.copyfile(os.path.join(data, f[1]), | 199 shutil.copyfile(os.path.join(data, f[1]), |
| 202 os.path.join(path, f[1]+'.txt')) | 200 os.path.join(path, f[1]+'.txt')) |
| 203 # Generate directory listing. | 201 # Generate directory listing. |
| 204 for data in [BROWSER_PERF, TIME_SIZE, CL_PERF]: | 202 for data in [BROWSER_PERF, TIME_SIZE, CL_PERF]: |
| 205 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) | 203 path = os.path.join('appengine', 'static', 'data', data, utils.GuessOS()) |
| 206 out = open(os.path.join('appengine', 'static', | 204 out = open(os.path.join('appengine', 'static', |
| 207 '%s-%s.html' % (data, utils.GuessOS())), 'w') | 205 '%s-%s.html' % (data, utils.GuessOS())), 'w') |
| 208 out.write('<html>\n <body>\n <ul>\n') | 206 out.write('<html>\n <body>\n <ul>\n') |
| 209 for f in os.listdir(path): | 207 for f in os.listdir(path): |
| 210 if not f.startswith('.'): | 208 if not f.startswith('.'): |
| 211 out.write(' <li><a href=data' + \ | 209 out.write(' <li><a href=data' + \ |
| 212 '''/%(data)s/%(os)s/%(file)s>%(file)s</a></li>\n''' % \ | 210 '''/%(data)s/%(os)s/%(file)s>%(file)s</a></li>\n''' % \ |
| 213 {'data': data, 'os': utils.GuessOS(), 'file': f}) | 211 {'data': data, 'os': utils.GuessOS(), 'file': f}) |
| 214 out.write(' </ul>\n </body>\n</html>') | 212 out.write(' </ul>\n </body>\n</html>') |
| 215 out.close() | 213 out.close() |
| 216 | 214 |
| 217 shutil.rmtree(os.path.join('appengine', 'static', 'graphs'), | 215 shutil.rmtree(os.path.join('appengine', 'static', 'graphs'), |
| 218 ignore_errors=True) | 216 ignore_errors=True) |
| 219 shutil.copytree('graphs', os.path.join('appengine', 'static', 'graphs')) | 217 shutil.copytree('graphs', os.path.join('appengine', 'static', 'graphs')) |
| 220 shutil.copyfile('index.html', os.path.join('appengine', 'static', | 218 shutil.copyfile('index.html', os.path.join('appengine', 'static', |
| 221 'index.html')) | 219 'index.html')) |
| 222 shutil.copyfile('data.html', os.path.join('appengine', 'static', | 220 shutil.copyfile('data.html', os.path.join('appengine', 'static', |
| 223 'data.html')) | 221 'data.html')) |
| 224 p = subprocess.Popen([os.path.join('..', '..', '..', 'third_party', | 222 p = subprocess.Popen([os.path.join('..', '..', '..', 'third_party', |
| 225 'appengine-python', 'appcfg.py'), 'update', | 223 'appengine-python', 'appcfg.py'), 'update', |
| 226 'appengine/'], shell=HAS_SHELL, stdin=subprocess.PIPE) | 224 'appengine/'], shell=HAS_SHELL, stdin=subprocess.PIPE) |
| 227 p.stdin.write(username + '\n') | 225 p.stdin.write(username + '\n') |
| 228 p.stdin.write(password + '\n') | 226 p.stdin.write(password + '\n') |
| 229 p.communicate() | 227 p.communicate() |
| 230 | 228 |
| 231 | 229 |
| 232 class TestRunner(object): | 230 class TestRunner(object): |
| 233 """The base class to provide shared code for different tests we will run and | 231 """The base class to provide shared code for different tests we will run and |
| 234 graph.""" | 232 graph.""" |
| 235 | 233 |
| 236 def __init__(self, result_folder_name, platform_list, v8_and_or_frog_list, | 234 def __init__(self, result_folder_name, platform_list, js_and_or_frog_list, |
| 237 values_list): | 235 values_list): |
| 238 """Args: | 236 """Args: |
| 239 result_folder_name the name of the folder where a tracefile of | 237 result_folder_name the name of the folder where a tracefile of |
| 240 performance results will be stored. | 238 performance results will be stored. |
| 241 platform_list a list containing the platform(s) that our data has been | 239 platform_list a list containing the platform(s) that our data has been |
| 242 run on. (command line, firefox, chrome, etc) | 240 run on. (command line, firefox, chrome, etc) |
| 243 v8_and_or_frog_list a list specifying whether we hold data about Frog | 241 js_and_or_frog_list a list specifying whether we hold data about Frog |
| 244 generated code, plain JS code (v8), or a combination of both. | 242 generated code, plain JS code (js), or a combination of both. |
| 245 values_list a list containing the type of data we will be graphing | 243 values_list a list containing the type of data we will be graphing |
| 246 (benchmarks, percentage passing, etc)""" | 244 (benchmarks, percentage passing, etc)""" |
| 247 self.result_folder_name = result_folder_name | 245 self.result_folder_name = result_folder_name |
| 248 # cur_time is used as a timestamp of when this performance test was run. | 246 # cur_time is used as a timestamp of when this performance test was run. |
| 249 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) | 247 self.cur_time = str(time.mktime(datetime.datetime.now().timetuple())) |
| 250 self.browser_color = {'chrome': 'green', 'ie': 'blue', 'ff': 'red', | 248 self.browser_color = {'chrome': 'green', 'ie': 'blue', 'ff': 'red', |
| 251 'safari':'black'} | 249 'safari':'black'} |
| 252 self.values_list = values_list | 250 self.values_list = values_list |
| 253 self.platform_list = platform_list | 251 self.platform_list = platform_list |
| 254 self.revision_dict = dict() | 252 self.revision_dict = dict() |
| 255 self.values_dict = dict() | 253 self.values_dict = dict() |
| 256 self.color_index = 0 | 254 self.color_index = 0 |
| 257 for platform in platform_list: | 255 for platform in platform_list: |
| 258 self.revision_dict[platform] = dict() | 256 self.revision_dict[platform] = dict() |
| 259 self.values_dict[platform] = dict() | 257 self.values_dict[platform] = dict() |
| 260 for f in v8_and_or_frog_list: | 258 for f in js_and_or_frog_list: |
| 261 self.revision_dict[platform][f] = dict() | 259 self.revision_dict[platform][f] = dict() |
| 262 self.values_dict[platform][f] = dict() | 260 self.values_dict[platform][f] = dict() |
| 263 for val in values_list: | 261 for val in values_list: |
| 264 self.revision_dict[platform][f][val] = [] | 262 self.revision_dict[platform][f][val] = [] |
| 265 self.values_dict[platform][f][val] = [] | 263 self.values_dict[platform][f][val] = [] |
| 266 if JS in v8_and_or_frog_list: | 264 if JS in js_and_or_frog_list: |
| 267 self.revision_dict[platform][JS][JS_MEAN] = [] | 265 self.revision_dict[platform][JS][JS_MEAN] = [] |
| 268 self.values_dict[platform][JS][JS_MEAN] = [] | 266 self.values_dict[platform][JS][JS_MEAN] = [] |
| 269 if FROG in v8_and_or_frog_list: | 267 if FROG in js_and_or_frog_list: |
| 270 self.revision_dict[platform][FROG][FROG_MEAN] = [] | 268 self.revision_dict[platform][FROG][FROG_MEAN] = [] |
| 271 self.values_dict[platform][FROG][FROG_MEAN] = [] | 269 self.values_dict[platform][FROG][FROG_MEAN] = [] |
| 272 | 270 |
| 273 def get_color(self): | 271 def get_color(self): |
| 274 color = COLORS[self.color_index] | 272 color = COLORS[self.color_index] |
| 275 self.color_index = (self.color_index + 1) % len(COLORS) | 273 self.color_index = (self.color_index + 1) % len(COLORS) |
| 276 return color | 274 return color |
| 277 | 275 |
| 278 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, | 276 def style_and_save_perf_plot(self, chart_title, y_axis_label, size_x, size_y, |
| 279 legend_loc, filename, platform_list, v8_and_or_frog_list, values_list, | 277 legend_loc, filename, platform_list, js_and_or_frog_list, values_list, |
| 280 should_clear_axes=True): | 278 should_clear_axes=True): |
| 281 """Sets style preferences for chart boilerplate that is consistent across | 279 """Sets style preferences for chart boilerplate that is consistent across |
| 282 all charts, and saves the chart as a png. | 280 all charts, and saves the chart as a png. |
| 283 | 281 |
| 284 Args: | 282 Args: |
| 285 size_x: the size of the printed chart, in inches, in the horizontal | 283 size_x: the size of the printed chart, in inches, in the horizontal |
| 286 direction | 284 direction |
| 287 size_y: the size of the printed chart, in inches in the vertical direction | 285 size_y: the size of the printed chart, in inches in the vertical direction |
| 288 legend_loc: the location of the legend in on the chart. See suitable | 286 legend_loc: the location of the legend in on the chart. See suitable |
| 289 arguments for the loc argument in matplotlib | 287 arguments for the loc argument in matplotlib |
| 290 filename: the filename that we want to save the resulting chart as | 288 filename: the filename that we want to save the resulting chart as |
| 291 platform_list: a list containing the platform(s) that our data has been | 289 platform_list: a list containing the platform(s) that our data has been |
| 292 run on. (command line, firefox, chrome, etc) | 290 run on. (command line, firefox, chrome, etc) |
| 293 values_list: a list containing the type of data we will be graphing | 291 values_list: a list containing the type of data we will be graphing |
| 294 (performance, percentage passing, etc) | 292 (performance, percentage passing, etc) |
| 295 should_clear_axes: True if we want to create a fresh graph, instead of | 293 should_clear_axes: True if we want to create a fresh graph, instead of |
| 296 plotting additional lines on the current graph.""" | 294 plotting additional lines on the current graph.""" |
| 297 if should_clear_axes: | 295 if should_clear_axes: |
| 298 plt.cla() # cla = clear current axes | 296 plt.cla() # cla = clear current axes |
| 299 for platform in platform_list: | 297 for platform in platform_list: |
| 300 for f in v8_and_or_frog_list: | 298 for f in js_and_or_frog_list: |
| 301 for val in values_list: | 299 for val in values_list: |
| 302 plt.plot(self.revision_dict[platform][f][val], | 300 plt.plot(self.revision_dict[platform][f][val], |
| 303 self.values_dict[platform][f][val], | 301 self.values_dict[platform][f][val], |
| 304 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) | 302 color=self.get_color(), label='%s-%s-%s' % (platform, f, val)) |
| 305 | 303 |
| 306 plt.xlabel('Revision Number') | 304 plt.xlabel('Revision Number') |
| 307 plt.ylabel(y_axis_label) | 305 plt.ylabel(y_axis_label) |
| 308 plt.title(chart_title) | 306 plt.title(chart_title) |
| 309 fontP = FontProperties() | 307 fontP = FontProperties() |
| 310 fontP.set_size('small') | 308 fontP.set_size('small') |
| 311 plt.legend(loc=legend_loc, prop = fontP) | 309 plt.legend(loc=legend_loc, prop = fontP) |
| 312 | 310 |
| 313 fig = plt.gcf() | 311 fig = plt.gcf() |
| 314 fig.set_size_inches(size_x, size_y) | 312 fig.set_size_inches(size_x, size_y) |
| 315 fig.savefig(os.path.join(GRAPH_OUT_DIR, filename)) | 313 fig.savefig(os.path.join(GRAPH_OUT_DIR, filename)) |
| 316 | 314 |
| 317 def add_svn_revision_to_trace(self, outfile): | 315 def add_svn_revision_to_trace(self, outfile): |
| 318 """Add the svn version number to the provided tracefile.""" | 316 """Add the svn version number to the provided tracefile.""" |
| 319 def search_for_revision(svn_info_command): | 317 def search_for_revision(svn_info_command): |
| 320 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, | 318 p = subprocess.Popen(svn_info_command, stdout = subprocess.PIPE, |
| 321 stderr = subprocess.STDOUT, shell = HAS_SHELL) | 319 stderr = subprocess.STDOUT, shell = HAS_SHELL) |
| 322 output, _ = p.communicate() | 320 output, _ = p.communicate() |
| 323 for line in output.split('\n'): | 321 for line in output.split('\n'): |
| 324 if 'Revision' in line: | 322 if 'Revision' in line: |
| 325 run_cmd(['echo', line.strip()], outfile) | 323 run_cmd(['echo', line.strip()], outfile) |
| 326 return True | 324 return True |
| 327 return False | 325 return False |
| 328 | 326 |
| 329 if not search_for_revision(['svn', 'info']): | 327 if not search_for_revision(['svn', 'info']): |
| 330 if not search_for_revision(['git', 'svn', 'info']): | 328 if not search_for_revision(['git', 'svn', 'info']): |
| 331 run_cmd(['echo', 'Revision: unknown'], outfile) | 329 run_cmd(['echo', 'Revision: unknown'], outfile) |
| 332 | 330 |
| 333 def calculate_geometric_mean(self, platform, frog_or_v8, svn_revision): | 331 def calculate_geometric_mean(self, platform, frog_or_js, svn_revision): |
| 334 """Calculate the aggregate geometric mean for JS and frog benchmark sets, | 332 """Calculate the aggregate geometric mean for JS and frog benchmark sets, |
| 335 given two benchmark dictionaries.""" | 333 given two benchmark dictionaries.""" |
| 336 geo_mean = 0 | 334 geo_mean = 0 |
| 337 for benchmark in get_benchmarks(): | 335 for benchmark in get_benchmarks(): |
| 338 geo_mean += math.log(self.values_dict[platform][frog_or_v8][benchmark][ | 336 geo_mean += math.log(self.values_dict[platform][frog_or_js][benchmark][ |
| 339 len(self.values_dict[platform][frog_or_v8][benchmark]) - 1]) | 337 len(self.values_dict[platform][frog_or_js][benchmark]) - 1]) |
| 340 | 338 |
| 341 mean = JS_MEAN | 339 mean = JS_MEAN |
| 342 if frog_or_v8 == FROG: | 340 if frog_or_js == FROG: |
| 343 mean = FROG_MEAN | 341 mean = FROG_MEAN |
| 344 self.values_dict[platform][frog_or_v8][mean] += \ | 342 self.values_dict[platform][frog_or_js][mean] += \ |
| 345 [math.pow(math.e, geo_mean / len(get_benchmarks()))] | 343 [math.pow(math.e, geo_mean / len(get_benchmarks()))] |
| 346 self.revision_dict[platform][frog_or_v8][mean] += [svn_revision] | 344 self.revision_dict[platform][frog_or_js][mean] += [svn_revision] |
| 347 | 345 |
| 348 def run(self): | 346 def run(self): |
| 349 """Run the benchmarks/tests from the command line and plot the | 347 """Run the benchmarks/tests from the command line and plot the |
| 350 results.""" | 348 results.""" |
| 351 plt.cla() # cla = clear current axes | 349 plt.cla() # cla = clear current axes |
| 352 os.chdir(DART_INSTALL_LOCATION) | 350 os.chdir(DART_INSTALL_LOCATION) |
| 353 ensure_output_directory(self.result_folder_name) | 351 ensure_output_directory(self.result_folder_name) |
| 354 ensure_output_directory(GRAPH_OUT_DIR) | 352 ensure_output_directory(GRAPH_OUT_DIR) |
| 355 self.run_tests() | 353 self.run_tests() |
| 356 | 354 |
| 357 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) | 355 os.chdir(os.path.join('tools', 'testing', 'perf_testing')) |
| 358 | 356 |
| 359 # TODO(efortuna): You will want to make this only use a subset of the files | 357 # TODO(efortuna): You will want to make this only use a subset of the files |
| 360 # eventually. | 358 # eventually. |
| 361 files = os.listdir(self.result_folder_name) | 359 files = os.listdir(self.result_folder_name) |
| 362 | 360 |
| 363 for afile in files: | 361 for afile in files: |
| 364 if not afile.startswith('.'): | 362 if not afile.startswith('.'): |
| 365 self.process_file(afile) | 363 self.process_file(afile) |
| 366 | 364 |
| 367 self.plot_results('%s.png' % self.result_folder_name) | 365 self.plot_results('%s.png' % self.result_folder_name) |
| 368 | 366 |
| 369 class PerformanceTest(TestRunner): | 367 class PerformanceTest(TestRunner): |
| 370 """Super class for all performance testing.""" | 368 """Super class for all performance testing.""" |
| 371 def __init__(self, result_folder_name, platform_list, platform_type): | 369 def __init__(self, result_folder_name, platform_list, platform_type): |
| 372 super(PerformanceTest, self).__init__(result_folder_name, | 370 super(PerformanceTest, self).__init__(result_folder_name, |
| 373 platform_list, get_versions(), get_benchmarks()) | 371 platform_list, get_versions(), get_benchmarks()) |
| 374 self.platform_list = platform_list | 372 self.platform_list = platform_list |
| 375 self.platform_type = platform_type | 373 self.platform_type = platform_type |
| 376 | 374 |
| 377 def plot_all_perf(self, png_filename): | 375 def plot_all_perf(self, png_filename): |
| 378 """Create a plot that shows the performance changes of individual benchmarks | 376 """Create a plot that shows the performance changes of individual benchmarks |
| 379 run by JS and generated by frog, over svn history.""" | 377 run by JS and generated by frog, over svn history.""" |
| 380 for benchmark in get_benchmarks(): | 378 for benchmark in get_benchmarks(): |
| 381 self.style_and_save_perf_plot( | 379 self.style_and_save_perf_plot( |
| 382 'Performance of %s over time on the %s on %s' % (benchmark, | 380 'Performance of %s over time on the %s on %s' % (benchmark, |
| 383 self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, | 381 self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, |
| 384 14, 'lower left', benchmark + png_filename, self.platform_list, | 382 14, 'lower left', benchmark + png_filename, self.platform_list, |
| 385 get_versions(), [benchmark]) | 383 get_versions(), [benchmark]) |
| 386 | 384 |
| 387 def plot_avg_perf(self, png_filename): | 385 def plot_avg_perf(self, png_filename): |
| 388 """Generate a plot that shows the performance changes of the geomentric mean | 386 """Generate a plot that shows the performance changes of the geomentric mean |
| 389 of JS and frog benchmark performance over svn history.""" | 387 of JS and frog benchmark performance over svn history.""" |
| 390 (title, y_axis, size_x, size_y, loc, filename) = \ | 388 (title, y_axis, size_x, size_y, loc, filename) = \ |
| 391 ('Geometric Mean of benchmark %s performance on %s ' % | 389 ('Geometric Mean of benchmark %s performance on %s ' % |
| 392 (self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, 5, | 390 (self.platform_type, utils.GuessOS()), 'Speed (bigger = better)', 16, 5, |
| 393 'lower left', 'avg'+png_filename) | 391 'lower left', 'avg'+png_filename) |
| 394 clear_axis = True | 392 clear_axis = True |
| 395 for platform in self.platform_list: | 393 for platform in self.platform_list: |
| 396 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, | 394 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, |
| 397 filename, [platform], [JS], [JS_MEAN], clear_axis) | 395 filename, [platform], [JS], [JS_MEAN], clear_axis) |
| 398 clear_axis = False | 396 clear_axis = False |
| 399 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, | 397 self.style_and_save_perf_plot(title, y_axis, size_x, size_y, loc, |
| 400 filename, [platform], [FROG], [FROG_MEAN], clear_axis) | 398 filename, [platform], [FROG], [FROG_MEAN], clear_axis) |
| 401 | 399 |
| 402 def plot_results(self, png_filename): | 400 def plot_results(self, png_filename): |
| 403 self.plot_all_perf(png_filename) | 401 self.plot_all_perf(png_filename) |
| 404 self.plot_avg_perf('2' + png_filename) | 402 self.plot_avg_perf('2' + png_filename) |
| 405 | 403 |
| 406 | 404 |
| 407 class CommandLinePerformanceTest(PerformanceTest): | 405 class CommandLinePerformanceTest(PerformanceTest): |
| 408 """Run performance tests from the command line.""" | 406 """Run performance tests from the command line.""" |
| 409 | 407 |
| 410 def __init__(self, result_folder_name): | 408 def __init__(self, result_folder_name): |
| 411 super(CommandLinePerformanceTest, self).__init__(result_folder_name, | 409 super(CommandLinePerformanceTest, self).__init__(result_folder_name, |
| 412 [COMMAND_LINE], 'command line') | 410 [COMMAND_LINE], 'command line') |
| 413 | 411 |
| 414 def process_file(self, afile): | 412 def process_file(self, afile): |
| 415 """Pull all the relevant information out of a given tracefile. | 413 """Pull all the relevant information out of a given tracefile. |
| 416 | 414 |
| 417 Args: | 415 Args: |
| 418 afile: The filename string we will be processing.""" | 416 afile: The filename string we will be processing.""" |
| 419 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 417 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', |
| 420 'perf_testing')) | 418 'perf_testing')) |
| 421 f = open(os.path.join(self.result_folder_name, afile)) | 419 f = open(os.path.join(self.result_folder_name, afile)) |
| 422 tabulate_data = False | 420 tabulate_data = False |
| 423 revision_num = 0 | 421 revision_num = 0 |
| 424 for line in f.readlines(): | 422 for line in f.readlines(): |
| 425 if 'Revision' in line: | 423 if 'Revision' in line: |
| 426 revision_num = int(line.split()[1]) | 424 revision_num = int(line.split()[1]) |
| 427 elif 'Benchmark' in line: | 425 elif 'Benchmark' in line: |
| 428 tabulate_data = True | 426 tabulate_data = True |
| 429 elif tabulate_data: | 427 elif tabulate_data: |
| 430 tokens = line.split() | 428 tokens = line.split() |
| 431 if len(tokens) < 4 or tokens[0] not in get_benchmarks(): | 429 if len(tokens) < 4 or tokens[0] not in get_benchmarks(): |
| 432 #Done tabulating data. | 430 #Done tabulating data. |
| 433 break | 431 break |
| 434 v8_value = float(tokens[1]) | 432 js_value = float(tokens[1]) |
| 435 frog_value = float(tokens[3]) | 433 frog_value = float(tokens[3]) |
| 436 if v8_value == 0 or frog_value == 0: | 434 if js_value == 0 or frog_value == 0: |
| 437 #Then there was an error when this performance test was run. Do not | 435 #Then there was an error when this performance test was run. Do not |
| 438 #count it in our numbers. | 436 #count it in our numbers. |
| 439 return | 437 return |
| 440 benchmark = tokens[0] | 438 benchmark = tokens[0] |
| 441 self.revision_dict[COMMAND_LINE][JS][benchmark] += [revision_num] | 439 self.revision_dict[COMMAND_LINE][JS][benchmark] += [revision_num] |
| 442 self.values_dict[COMMAND_LINE][JS][benchmark] += [v8_value] | 440 self.values_dict[COMMAND_LINE][JS][benchmark] += [js_value] |
| 443 self.revision_dict[COMMAND_LINE][FROG][benchmark] += [revision_num] | 441 self.revision_dict[COMMAND_LINE][FROG][benchmark] += [revision_num] |
| 444 self.values_dict[COMMAND_LINE][FROG][benchmark] += [frog_value] | 442 self.values_dict[COMMAND_LINE][FROG][benchmark] += [frog_value] |
| 445 f.close() | 443 f.close() |
| 446 | 444 |
| 447 self.calculate_geometric_mean(COMMAND_LINE, FROG, revision_num) | 445 self.calculate_geometric_mean(COMMAND_LINE, FROG, revision_num) |
| 448 self.calculate_geometric_mean(COMMAND_LINE, JS, revision_num) | 446 self.calculate_geometric_mean(COMMAND_LINE, JS, revision_num) |
| 449 | 447 |
| 450 def run_tests(self): | 448 def run_tests(self): |
| 451 """Run a performance test on our updated system.""" | 449 """Run a performance test on our updated system.""" |
| 452 os.chdir('frog') | 450 os.chdir('frog') |
| 453 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', | 451 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', |
| 454 self.result_folder_name, 'result' + self.cur_time) | 452 self.result_folder_name, 'result' + self.cur_time) |
| 455 run_cmd(['python', os.path.join('benchmarks', 'perf_tests.py')], | 453 run_cmd(['python', os.path.join('benchmarks', 'perf_tests.py')], |
| 456 self.trace_file) | 454 self.trace_file) |
| 457 os.chdir('..') | 455 os.chdir('..') |
| 458 | 456 |
| 459 | 457 |
| 460 class BrowserPerformanceTest(PerformanceTest): | 458 class BrowserPerformanceTest(PerformanceTest): |
| 461 """Runs performance tests, in the browser.""" | 459 """Runs performance tests, in the browser.""" |
| 462 | 460 |
| 463 def __init__(self, result_folder_name): | 461 def __init__(self, result_folder_name): |
| 464 super(BrowserPerformanceTest, self).__init__( | 462 super(BrowserPerformanceTest, self).__init__( |
| 465 result_folder_name, get_browsers(), 'browser') | 463 result_folder_name, get_browsers(), 'browser') |
| 466 | 464 |
| 467 def run_tests(self): | 465 def run_tests(self): |
| 468 """Run a performance test in the browser.""" | 466 """Run a performance test in the browser.""" |
| 469 | 467 |
| 470 os.chdir('frog') | 468 os.chdir('frog') |
| 471 run_cmd(['python', os.path.join('benchmarks', 'make_web_benchmarks.py')]) | 469 run_cmd(['python', os.path.join('benchmarks', 'make_web_benchmarks.py')]) |
| 472 os.chdir('..') | 470 os.chdir('..') |
| 473 | 471 |
| 474 for browser in get_browsers(): | 472 for browser in get_browsers(): |
| 475 for version in get_versions(): | 473 for version in get_versions(): |
| 476 self.trace_file = os.path.join('tools', 'testing', 'perf_testing', | 474 self.trace_file = os.path.join('tools', 'testing', 'perf_testing', |
| 477 self.result_folder_name, | 475 self.result_folder_name, |
| 478 'perf-%s-%s-%s' % (self.cur_time, browser, version)) | 476 'perf-%s-%s-%s' % (self.cur_time, browser, version)) |
| 479 self.add_svn_revision_to_trace(self.trace_file) | 477 self.add_svn_revision_to_trace(self.trace_file) |
| 480 file_path = os.path.join(os.getcwd(), 'internal', 'browserBenchmarks', | 478 file_path = os.path.join(os.getcwd(), 'internal', 'browserBenchmarks', |
| 481 'benchmark_page_%s.html' % version) | 479 'benchmark_page_%s.html' % version) |
| 482 run_cmd(['python', os.path.join('tools', 'testing', 'run_selenium.py'), | 480 run_cmd(['python', os.path.join('tools', 'testing', 'run_selenium.py'), |
| 483 '--out', file_path, '--browser', browser, | 481 '--out', file_path, '--browser', browser, |
| 484 '--timeout', '600', '--perf'], self.trace_file, append=True) | 482 '--timeout', '600', '--perf'], self.trace_file, append=True) |
| 485 | 483 |
| 486 def process_file(self, afile): | 484 def process_file(self, afile): |
| 487 """Comb through the html to find the performance results.""" | 485 """Comb through the html to find the performance results.""" |
| 488 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 486 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', |
| 489 'perf_testing')) | 487 'perf_testing')) |
| 490 parts = afile.split('-') | 488 parts = afile.split('-') |
| 491 browser = parts[2] | 489 browser = parts[2] |
| 492 version = parts[3] | 490 version = parts[3] |
| 493 f = open(os.path.join(self.result_folder_name, afile)) | 491 f = open(os.path.join(self.result_folder_name, afile)) |
| 494 lines = f.readlines() | 492 lines = f.readlines() |
| 495 line = '' | 493 line = '' |
| 496 i = 0 | 494 i = 0 |
| 497 revision_num = 0 | 495 revision_num = 0 |
| 498 while '<div id="results">' not in line and i < len(lines): | 496 while '<div id="results">' not in line and i < len(lines): |
| 499 if 'Revision' in line: | 497 if 'Revision' in line: |
| 500 revision_num = int(line.split()[1].strip('"')) | 498 revision_num = int(line.split()[1].strip('"')) |
| 501 line = lines[i] | 499 line = lines[i] |
| 502 i += 1 | 500 i += 1 |
| 503 | 501 |
| 504 if i >= len(lines) or revision_num == 0: | 502 if i >= len(lines) or revision_num == 0: |
| 505 # Then this run did not complete. Ignore this tracefile. | 503 # Then this run did not complete. Ignore this tracefile. |
| 506 return | 504 return |
| 507 | 505 |
| 508 line = lines[i] | 506 line = lines[i] |
| 509 i += 1 | 507 i += 1 |
| 510 results = [] | 508 results = [] |
| 511 if line.find('<br>') > -1: | 509 if line.find('<br>') > -1: |
| 512 results = line.split('<br>') | 510 results = line.split('<br>') |
| 513 else: | 511 else: |
| 514 results = line.split('<br />') | 512 results = line.split('<br />') |
| 515 for result in results: | 513 for result in results: |
| 516 name_and_score = result.split(':') | 514 name_and_score = result.split(':') |
| 517 if len(name_and_score) < 2: | 515 if len(name_and_score) < 2: |
| 518 break | 516 break |
| 519 name = name_and_score[0].strip() | 517 name = name_and_score[0].strip() |
| 520 score = name_and_score[1].strip() | 518 score = name_and_score[1].strip() |
| 521 if version == JS or version == 'v8': | 519 if version == JS or version == 'v8': |
| 522 version = JS | 520 version = JS |
| 523 bench_dict = self.values_dict[browser][JS] | 521 bench_dict = self.values_dict[browser][JS] |
| 524 else: | 522 else: |
| 525 bench_dict = self.values_dict[browser][FROG] | 523 bench_dict = self.values_dict[browser][FROG] |
| 526 bench_dict[name] += [float(score)] | 524 bench_dict[name] += [float(score)] |
| 527 self.revision_dict[browser][version][name] += [revision_num] | 525 self.revision_dict[browser][version][name] += [revision_num] |
| 528 | 526 |
| 529 f.close() | 527 f.close() |
| 530 self.calculate_geometric_mean(browser, version, revision_num) | 528 self.calculate_geometric_mean(browser, version, revision_num) |
| 531 | 529 |
| 532 | |
| 533 class BrowserCorrectnessTest(TestRunner): | |
| 534 def __init__(self, test_type, result_folder_name): | |
| 535 super(BrowserCorrectnessTest, self).__init__(result_folder_name, | |
| 536 get_browsers(), [FROG], [CORRECTNESS]) | |
| 537 self.test_type = test_type | |
| 538 | |
| 539 def run_tests(self): | |
| 540 """Run a test of the latest svn revision.""" | |
| 541 system = get_os_directory() | |
| 542 suffix = '' | |
| 543 if platform.system() == 'Windows': | |
| 544 suffix = '.exe' | |
| 545 for browser in get_browsers(): | |
| 546 current_file = 'correctness%s-%s' % (self.cur_time, browser) | |
| 547 self.trace_file = os.path.join('tools', 'testing', | |
| 548 'perf_testing', self.result_folder_name, current_file) | |
| 549 self.add_svn_revision_to_trace(self.trace_file) | |
| 550 dart_sdk = os.path.join(os.getcwd(), utils.GetBuildRoot(utils.GuessOS(), | |
| 551 'release', 'ia32'), 'dart-sdk') | |
| 552 run_cmd([os.path.join('.', 'tools', 'testing', 'bin', system, | |
| 553 'dart' + suffix), os.path.join('tools', 'test.dart'), | |
| 554 '--compiler=frog', '--runtime=%s' % browser, | |
| 555 '--frog=%s' % os.path.join(dart_sdk, 'bin', | |
| 556 'frogc'), '--froglib=%s' % os.path.join(dart_sdk, 'lib'), '--report', | |
| 557 '--timeout=20', '--progress=color', '--mode=release', | |
| 558 self.test_type], self.trace_file, append=True) | |
| 559 | |
| 560 def process_file(self, afile): | |
| 561 """Given a trace file, extract all the relevant information out of it to | |
| 562 determine the number of correctly passing tests. | |
| 563 | |
| 564 Arguments: | |
| 565 afile: the filename string""" | |
| 566 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | |
| 567 'perf_testing')) | |
| 568 browser = afile.rpartition('-')[2] | |
| 569 f = open(os.path.join(self.result_folder_name, afile)) | |
| 570 revision_num = 0 | |
| 571 lines = f.readlines() | |
| 572 total_tests = 0 | |
| 573 num_failed = 0 | |
| 574 expect_fail = 0 | |
| 575 for line in lines: | |
| 576 if 'Total:' in line: | |
| 577 total_tests = int(line.split('Total: ')[1].split()[0]) | |
| 578 if 'will be skipped' in line: | |
| 579 total_tests -= int(line.split()[1]) | |
| 580 if 'we should fix' in line: | |
| 581 expect_fail += int(line.split()[1]) | |
| 582 if 'Revision' in line: | |
| 583 revision_num = int(line.split()[1]) | |
| 584 if '--- TIMEOUT ---' in line or 'FAIL:' in line or 'PASS' in line: | |
| 585 # (A printed out 'PASS' indicates we incorrectly passed a negative | |
| 586 # test.) | |
| 587 num_failed += 1 | |
| 588 | |
| 589 self.revision_dict[browser][FROG][CORRECTNESS] += [revision_num] | |
| 590 self.values_dict[browser][FROG][CORRECTNESS] += [100.0 * | |
| 591 (((float)(total_tests - (expect_fail + num_failed))) /total_tests)] | |
| 592 f.close() | |
| 593 | |
| 594 def plot_results(self, png_filename): | |
| 595 first_time = True | |
| 596 for browser in get_browsers(): | |
| 597 self.style_and_save_perf_plot('Percentage of language tests passing in ' | |
| 598 'different browsers', '% of tests passed', 8, 8, 'lower left', | |
| 599 png_filename, [browser], [FROG], [CORRECTNESS], first_time) | |
| 600 first_time = False | |
| 601 | |
| 602 | 530 |
| 603 class CompileTimeAndSizeTest(TestRunner): | 531 class CompileTimeAndSizeTest(TestRunner): |
| 604 """Run tests to determine how long minfrog takes to compile, and the compiled | 532 """Run tests to determine how long minfrog takes to compile, and the compiled |
| 605 file output size of some benchmarking files.""" | 533 file output size of some benchmarking files.""" |
| 606 def __init__(self, result_folder_name): | 534 def __init__(self, result_folder_name): |
| 607 super(CompileTimeAndSizeTest, self).__init__(result_folder_name, | 535 super(CompileTimeAndSizeTest, self).__init__(result_folder_name, |
| 608 [COMMAND_LINE], [FROG], ['Compiling on Dart VM', 'Bootstrapping', | 536 [COMMAND_LINE], [FROG], ['Compiling on Dart VM', 'Bootstrapping', |
| 609 'minfrog', 'swarm', 'total']) | 537 'minfrog', 'swarm', 'total']) |
| 610 self.failure_threshold = {'Compiling on Dart VM' : 1, 'Bootstrapping' : .5, | 538 self.failure_threshold = {'Compiling on Dart VM' : 1, 'Bootstrapping' : .5, |
| 611 'minfrog' : 100, 'swarm' : 100, 'total' : 100} | 539 'minfrog' : 100, 'swarm' : 100, 'total' : 100} |
| 612 | 540 |
| 613 def run_tests(self): | 541 def run_tests(self): |
| 614 os.chdir('frog') | 542 os.chdir('frog') |
| 615 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', | 543 self.trace_file = os.path.join('..', 'tools', 'testing', 'perf_testing', |
| 616 self.result_folder_name, self.result_folder_name + self.cur_time) | 544 self.result_folder_name, self.result_folder_name + self.cur_time) |
| 617 | 545 |
| 618 self.add_svn_revision_to_trace(self.trace_file) | 546 self.add_svn_revision_to_trace(self.trace_file) |
| 619 | 547 |
| 620 suffix = '' | 548 suffix = '' |
| 621 if platform.system() == 'Windows': | 549 if platform.system() == 'Windows': |
| 622 suffix = '.exe' | 550 suffix = '.exe' |
| 623 elapsed = time_cmd([os.path.join('..', utils.GetBuildRoot(utils.GuessOS(), | 551 elapsed = time_cmd([os.path.join('..', utils.GetBuildRoot(utils.GuessOS(), |
| 624 'release', 'ia32'), 'dart' + suffix), os.path.join('.', 'minfrogc.dart'), | 552 'release', 'ia32'), 'dart' + suffix), os.path.join('.', 'minfrogc.dart'), |
| 625 '--out=minfrog', 'minfrog.dart']) | 553 '--out=minfrog', 'minfrog.dart']) |
| 626 run_cmd(['echo', '%f Compiling on Dart VM in production mode in seconds' | 554 run_cmd(['echo', '%f Compiling on Dart VM in production mode in seconds' |
| 627 % elapsed], self.trace_file, append=True) | 555 % elapsed], self.trace_file, append=True) |
| 628 elapsed = time_cmd([os.path.join('.', 'minfrog'), '--out=minfrog', | 556 elapsed = time_cmd([os.path.join('.', 'minfrog'), '--out=minfrog', |
| 629 'minfrog.dart', os.path.join('tests', 'hello.dart')]) | 557 'minfrog.dart', os.path.join('tests', 'hello.dart')]) |
| 630 if elapsed < self.failure_threshold['Bootstrapping']: | 558 if elapsed < self.failure_threshold['Bootstrapping']: |
| 631 #minfrog didn't compile correctly. Stop testing now, because subsequent | 559 #minfrog didn't compile correctly. Stop testing now, because subsequent |
| 632 #numbers will be meaningless. | 560 #numbers will be meaningless. |
| 633 return | 561 return |
| 634 size = os.path.getsize('minfrog') | 562 size = os.path.getsize('minfrog') |
| 635 run_cmd(['echo', '%f Bootstrapping time in seconds in production mode' % | 563 run_cmd(['echo', '%f Bootstrapping time in seconds in production mode' % |
| 636 elapsed], self.trace_file, append=True) | 564 elapsed], self.trace_file, append=True) |
| 637 run_cmd(['echo', '%d Generated checked minfrog size' % size], | 565 run_cmd(['echo', '%d Generated checked minfrog size' % size], |
| 638 self.trace_file, append=True) | 566 self.trace_file, append=True) |
| 639 | 567 |
| 640 run_cmd([os.path.join('.', 'minfrog'), '--out=swarm-result', | 568 run_cmd([os.path.join('.', 'minfrog'), '--out=swarm-result', |
| 641 '--compile-only', os.path.join('..', 'samples', 'swarm', | 569 '--compile-only', os.path.join('..', 'samples', 'swarm', |
| 642 'swarm.dart')]) | 570 'swarm.dart')]) |
| 643 swarm_size = 0 | 571 swarm_size = 0 |
| 644 try: | 572 try: |
| 645 swarm_size = os.path.getsize('swarm-result') | 573 swarm_size = os.path.getsize('swarm-result') |
| 646 except OSError: | 574 except OSError: |
| 647 pass #If compilation failed, continue on running other tests. | 575 pass #If compilation failed, continue on running other tests. |
| 648 | 576 |
| 649 run_cmd([os.path.join('.', 'minfrog'), '--out=total-result', | 577 run_cmd([os.path.join('.', 'minfrog'), '--out=total-result', |
| 650 '--compile-only', os.path.join('..', 'samples', 'total', | 578 '--compile-only', os.path.join('..', 'samples', 'total', |
| 651 'client', 'Total.dart')]) | 579 'client', 'Total.dart')]) |
| 652 total_size = 0 | 580 total_size = 0 |
| 653 try: | 581 try: |
| 654 total_size = os.path.getsize('total-result') | 582 total_size = os.path.getsize('total-result') |
| 655 except OSError: | 583 except OSError: |
| 656 pass #If compilation failed, continue on running other tests. | 584 pass #If compilation failed, continue on running other tests. |
| 657 | 585 |
| 658 run_cmd(['echo', '%d Generated checked swarm size' % swarm_size], | 586 run_cmd(['echo', '%d Generated checked swarm size' % swarm_size], |
| 659 self.trace_file, append=True) | 587 self.trace_file, append=True) |
| 660 | 588 |
| 661 run_cmd(['echo', '%d Generated checked total size' % total_size], | 589 run_cmd(['echo', '%d Generated checked total size' % total_size], |
| 662 self.trace_file, append=True) | 590 self.trace_file, append=True) |
| 663 os.chdir('..') | 591 os.chdir('..') |
| 664 | 592 |
| 665 def process_file(self, afile): | 593 def process_file(self, afile): |
| 666 """Pull all the relevant information out of a given tracefile. | 594 """Pull all the relevant information out of a given tracefile. |
| 667 | 595 |
| 668 Args: | 596 Args: |
| 669 afile: is the filename string we will be processing.""" | 597 afile: is the filename string we will be processing.""" |
| 670 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', | 598 os.chdir(os.path.join(DART_INSTALL_LOCATION, 'tools', 'testing', |
| 671 'perf_testing')) | 599 'perf_testing')) |
| 672 f = open(os.path.join(self.result_folder_name, afile)) | 600 f = open(os.path.join(self.result_folder_name, afile)) |
| 673 tabulate_data = False | 601 tabulate_data = False |
| 674 revision_num = 0 | 602 revision_num = 0 |
| 675 for line in f.readlines(): | 603 for line in f.readlines(): |
| 676 tokens = line.split() | 604 tokens = line.split() |
| 677 if 'Revision' in line: | 605 if 'Revision' in line: |
| 678 revision_num = int(line.split()[1]) | 606 revision_num = int(line.split()[1]) |
| 679 else: | 607 else: |
| 680 for metric in self.values_list: | 608 for metric in self.values_list: |
| 681 if metric in line: | 609 if metric in line: |
| 682 num = tokens[0] | 610 num = tokens[0] |
| 683 if num.find('.') == -1: | 611 if num.find('.') == -1: |
| 684 num = int(num) | 612 num = int(num) |
| 685 else: | 613 else: |
| 686 num = float(num) | 614 num = float(num) |
| 687 self.values_dict[COMMAND_LINE][FROG][metric] += [num] | 615 self.values_dict[COMMAND_LINE][FROG][metric] += [num] |
| 688 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | 616 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] |
| 689 | 617 |
| 690 if revision_num != 0: | 618 if revision_num != 0: |
| 691 for metric in self.values_list: | 619 for metric in self.values_list: |
| 692 self.revision_dict[COMMAND_LINE][FROG][metric].pop() | 620 self.revision_dict[COMMAND_LINE][FROG][metric].pop() |
| 693 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | 621 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] |
| 694 # Fill in 0 if compilation failed. | 622 # Fill in 0 if compilation failed. |
| 695 if self.values_dict[COMMAND_LINE][FROG][metric][-1] < \ | 623 if self.values_dict[COMMAND_LINE][FROG][metric][-1] < \ |
| 696 self.failure_threshold[metric]: | 624 self.failure_threshold[metric]: |
| 697 self.values_dict[COMMAND_LINE][FROG][metric] += [0] | 625 self.values_dict[COMMAND_LINE][FROG][metric] += [0] |
| 698 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] | 626 self.revision_dict[COMMAND_LINE][FROG][metric] += [revision_num] |
| 699 | 627 |
| 700 f.close() | 628 f.close() |
| 701 | 629 |
| 702 def plot_results(self, png_filename): | 630 def plot_results(self, png_filename): |
| 703 self.style_and_save_perf_plot('Compiled minfrog Sizes', | 631 self.style_and_save_perf_plot('Compiled minfrog Sizes', |
| 704 'Size (in bytes)', 10, 10, 'lower left', png_filename, [COMMAND_LINE], | 632 'Size (in bytes)', 10, 10, 'lower left', png_filename, [COMMAND_LINE], |
| 705 [FROG], ['swarm', 'total', 'minfrog']) | 633 [FROG], ['swarm', 'total', 'minfrog']) |
| 706 | 634 |
| 707 self.style_and_save_perf_plot('Time to compile and bootstrap', | 635 self.style_and_save_perf_plot('Time to compile and bootstrap', |
| 708 'Seconds', 10, 10, 'lower left', '2' + png_filename, [COMMAND_LINE], | 636 'Seconds', 10, 10, 'lower left', '2' + png_filename, [COMMAND_LINE], |
| 709 [FROG], ['Bootstrapping', 'Compiling on Dart VM']) | 637 [FROG], ['Bootstrapping', 'Compiling on Dart VM']) |
| 710 | 638 |
| 711 def parse_args(): | 639 def parse_args(): |
| 712 parser = optparse.OptionParser() | 640 parser = optparse.OptionParser() |
| 713 parser.add_option('--command-line', '-c', dest='cl', | 641 parser.add_option('--command-line', '-c', dest='cl', |
| 714 help='Run the command line tests', | 642 help='Run the command line tests', |
| 715 action='store_true', default=False) | |
| 716 parser.add_option('--size-time', '-s', dest='size', | |
| 717 help='Run the code size and timing tests', | |
| 718 action='store_true', default=False) | 643 action='store_true', default=False) |
| 719 parser.add_option('--language', '-l', dest='language', | 644 parser.add_option('--size-time', '-s', dest='size', |
| 720 help='Run the language correctness tests', | 645 help='Run the code size and timing tests', |
| 721 action='store_true', default=False) | 646 action='store_true', default=False) |
| 722 parser.add_option('--browser-perf', '-b', dest='perf', | 647 parser.add_option('--browser-perf', '-b', dest='perf', |
| 723 help='Run the browser performance tests', | 648 help='Run the browser performance tests', |
| 724 action='store_true', default=False) | 649 action='store_true', default=False) |
| 725 parser.add_option('--forever', '-f', dest='continuous', | 650 parser.add_option('--forever', '-f', dest='continuous', |
| 726 help = 'Run this script forever, always checking for the next svn ' | 651 help = 'Run this script forever, always checking for the next svn ' |
| 727 'checkin', action='store_true', default=False) | 652 'checkin', action='store_true', default=False) |
| 728 parser.add_option('--verbose', '-v', dest='verbose', | 653 parser.add_option('--verbose', '-v', dest='verbose', |
| 729 help = 'Print extra debug output', action='store_true', default=False) | 654 help = 'Print extra debug output', action='store_true', default=False) |
| 730 parser.add_option('--user', '-u', dest='username', | 655 parser.add_option('--user', '-u', dest='username', |
| 731 help='Username for submitting new data to App Engine', default='') | 656 help='Username for submitting new data to App Engine', default='') |
| 732 | 657 |
| 733 args, ignored = parser.parse_args() | 658 args, ignored = parser.parse_args() |
| 734 password = '' | 659 password = '' |
| 735 if args.username != '': | 660 if args.username != '': |
| 736 password = getpass.getpass("App Engine Password: ") | 661 password = getpass.getpass("App Engine Password: ") |
| 737 else: | 662 else: |
| 738 print 'Warning: performance data will not be uploaded to App Engine' + \ | 663 print 'Warning: performance data will not be uploaded to App Engine' + \ |
| 739 ' if you do not provide a username.' | 664 ' if you do not provide a username.' |
| 740 if not (args.cl or args.size or args.language or args.perf): | 665 if not (args.cl or args.size or args.perf): |
| 741 args.cl = args.size = args.language = args.perf = True | 666 args.cl = args.size = args.perf = True |
| 742 return (args.cl, args.size, args.language, args.perf, args.continuous, | 667 return (args.cl, args.size, args.perf, args.continuous, |
| 743 args.verbose, args.username, password) | 668 args.verbose, args.username, password) |
| 744 | 669 |
| 745 def run_test_sequence(cl, size, language, perf, username, password): | 670 def run_test_sequence(cl, size, perf, username, password): |
| 746 # The buildbot already builds and syncs to a specific revision. Don't fight | 671 # The buildbot already builds and syncs to a specific revision. Don't fight |
| 747 # with it or replicate work. | 672 # with it or replicate work. |
| 748 if sync_and_build() == 1: | 673 if sync_and_build() == 1: |
| 749 return # The build is broken. | 674 return # The build is broken. |
| 750 if size: | 675 if size: |
| 751 CompileTimeAndSizeTest(TIME_SIZE).run() | 676 CompileTimeAndSizeTest(TIME_SIZE).run() |
| 752 if cl: | 677 if cl: |
| 753 CommandLinePerformanceTest(CL_PERF).run() | 678 CommandLinePerformanceTest(CL_PERF).run() |
| 754 if language: | |
| 755 BrowserCorrectnessTest('language', BROWSER_CORRECTNESS).run() | |
| 756 if perf: | 679 if perf: |
| 757 BrowserPerformanceTest(BROWSER_PERF).run() | 680 BrowserPerformanceTest(BROWSER_PERF).run() |
| 758 | 681 |
| 759 if username != '': | 682 if username != '': |
| 760 upload_to_app_engine(username, password) | 683 upload_to_app_engine(username, password) |
| 761 | 684 |
| 762 def main(): | 685 def main(): |
| 763 global VERBOSE | 686 global VERBOSE |
| 764 (cl, size, language, perf, continuous, verbose, username, password) = parse_ar
gs() | 687 (cl, size, perf, continuous, verbose, username, password) = parse_args() |
| 765 VERBOSE = verbose | 688 VERBOSE = verbose |
| 766 if continuous: | 689 if continuous: |
| 767 while True: | 690 while True: |
| 768 if has_new_code(): | 691 if has_new_code(): |
| 769 run_test_sequence(cl, size, language, perf, username, password) | 692 run_test_sequence(cl, size, perf, username, password) |
| 770 else: | 693 else: |
| 771 time.sleep(SLEEP_TIME) | 694 time.sleep(SLEEP_TIME) |
| 772 else: | 695 else: |
| 773 run_test_sequence(cl, size, language, perf, username, password) | 696 run_test_sequence(cl, size, perf, username, password) |
| 774 | 697 |
| 775 if __name__ == '__main__': | 698 if __name__ == '__main__': |
| 776 main() | 699 main() |
| OLD | NEW |