OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """Snapshot Build Linear Benchmark Tool |
| 7 |
| 8 This script executes and gathers stats from a series of snapshot archives. It |
| 9 starts at a start revision (it will try to guess HEAD) and asks for an end |
| 10 revision. It will then linearly run across this revision range by downloading, |
| 11 unzipping, and gathers stats for each revision. |
| 12 """ |
| 13 |
| 14 import optparse |
| 15 import os |
| 16 import sys |
| 17 from distutils.version import LooseVersion |
| 18 import inspect |
| 19 |
| 20 cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( |
| 21 inspect.currentframe() ))[0])) |
| 22 |
| 23 bisect_builds_dir = os.path.join(cmd_folder, "..") |
| 24 |
| 25 if bisect_builds_dir not in sys.path: |
| 26 sys.path.insert(0, bisect_builds_dir) |
| 27 |
| 28 bisect_builds = __import__('bisect-builds') |
| 29 |
| 30 def GatherStats(platform, |
| 31 official_builds, |
| 32 execute, |
| 33 start_rev=0, |
| 34 end_rev=0, |
| 35 num_runs=1, |
| 36 stdoutdir=None): |
| 37 """Given start and known end revisions, run sequentially |
| 38 on archived revisions, gathering performance stats at each one. |
| 39 |
| 40 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). |
| 41 @param official_builds Specify build type (Chromium or Official build). |
| 42 @param execute String specifying what command to run at each revision. |
| 43 @param start_rev Number/tag of the revision to start on. |
| 44 @param end_rev Number/tag of the revision to end on. |
| 45 @param num_runs Number of times to run each build. |
| 46 @param stdoutdir The (optional) path to an output directory.""" |
| 47 |
| 48 context = bisect_builds.PathContext(platform, start_rev, end_rev, |
| 49 official_builds) |
| 50 cwd = os.getcwd() |
| 51 |
| 52 |
| 53 print "Downloading list of known revisions..." |
| 54 _GetDownloadPath = lambda rev: os.path.join(cwd, |
| 55 '%s-%s' % (str(rev), context.archive_name)) |
| 56 if official_builds: |
| 57 revlist = context.GetOfficialBuildsList() |
| 58 else: |
| 59 revlist = context.GetRevList() |
| 60 |
| 61 print 'Running on range [%s, %s].' % (revlist[0], revlist[-1]) |
| 62 |
| 63 # Initialize our revision index and fetch the first revision. |
| 64 rev = revlist[0] |
| 65 next_rev = 0 |
| 66 zipfile = _GetDownloadPath(rev) |
| 67 initial_fetch = bisect_builds.DownloadJob(context, 'initial_fetch', rev, |
| 68 zipfile) |
| 69 initial_fetch.Start() |
| 70 initial_fetch.WaitFor() |
| 71 index = 1 |
| 72 |
| 73 while zipfile and index <= len(revlist): |
| 74 cur_fetch = None |
| 75 if index < len(revlist): |
| 76 # Pre-fetch next revision |
| 77 next_rev = revlist[index] |
| 78 cur_fetch = bisect_builds.DownloadJob(context, 'cur_fetch', next_rev, |
| 79 _GetDownloadPath(next_rev)) |
| 80 cur_fetch.Start() |
| 81 |
| 82 # Run test on the index revision. |
| 83 def execute_args(chrome_path): |
| 84 return (execute % chrome_path).split(' ') |
| 85 |
| 86 # Run test on the index revision. |
| 87 (status, stdout, stderr) = bisect_builds.RunRevision(context, |
| 88 rev, |
| 89 zipfile, |
| 90 num_runs, |
| 91 execute_args, |
| 92 stdoutdir) |
| 93 |
| 94 rev = next_rev |
| 95 |
| 96 os.unlink(zipfile) |
| 97 zipfile = None |
| 98 |
| 99 # Wait for fetch to complete. |
| 100 try: |
| 101 if cur_fetch: |
| 102 cur_fetch.WaitFor() |
| 103 index = index + 1 |
| 104 zipfile = cur_fetch.zipfile |
| 105 except SystemExit: |
| 106 print "Cleaning up..." |
| 107 for f in [_GetDownloadPath(revlist[index])]: |
| 108 try: |
| 109 os.unlink(f) |
| 110 except OSError: |
| 111 pass |
| 112 sys.exit(0) |
| 113 |
| 114 return 0 |
| 115 |
| 116 def main(): |
| 117 usage = ('%prog [options]\n' |
| 118 'Collects benchmark statistics on snapshot builds.\n') |
| 119 parser = optparse.OptionParser(usage=usage) |
| 120 # Strangely, the default help output doesn't include the choice list. |
| 121 choices = ['mac', 'win', 'linux', 'linux64'] |
| 122 # linux-chromiumos lacks a continuous archive http://crbug.com/78158 |
| 123 parser.add_option('-a', '--archive', |
| 124 choices = choices, |
| 125 help = 'The buildbot archive to run [%s].' % |
| 126 '|'.join(choices)) |
| 127 parser.add_option('-o', action="store_true", dest='official_builds', |
| 128 help = 'Run across official ' + |
| 129 'Chrome builds (internal only) instead of ' + |
| 130 'Chromium archives.') |
| 131 parser.add_option('-e', '--end', type = 'str', |
| 132 help = 'The revision to end on. Default is HEAD.') |
| 133 parser.add_option('-s', '--start', type = 'str', |
| 134 help = 'The revision to start from. Default is 0.') |
| 135 parser.add_option('-t', '--times', type = 'int', |
| 136 help = 'Number of times to run each build. Temporary' + |
| 137 ' profiles are reused. Default is 1.', |
| 138 default = 1) |
| 139 parser.add_option('-x', '--execute', type='str', |
| 140 help = 'Command to execute for each revision. Include a ' + |
| 141 'single %s to specify the location of the downloaded ' + |
| 142 'chrome executable. Default is \'python ' + |
| 143 'run_scrolling_benchmark page_sets/Q32012.json ' + |
| 144 '--browser-executable=%s\'.', |
| 145 default = 'python ./run_scrolling_benchmark ' + |
| 146 'page_sets/Q32012.json --browser-executable=%s') |
| 147 parser.add_option('-d', '--output-directory', '--stdoutdir', type='str', |
| 148 help = 'Save stdout to files for each revision.', |
| 149 default = None) |
| 150 parser.add_option('-r', '--revisions', type = 'int', |
| 151 help = 'Number of revisions to run if a start revision ' + |
| 152 'isn\'t specified. Default is 500.', |
| 153 default = 500) |
| 154 (opts, args) = parser.parse_args() |
| 155 |
| 156 if opts.archive is None: |
| 157 print 'Error: missing required parameter: --archive' |
| 158 print |
| 159 parser.print_help() |
| 160 return 1 |
| 161 |
| 162 # Create the context. Initialize 0 for the revisions as they are set below. |
| 163 context = bisect_builds.PathContext(opts.archive, 0, 0, opts.official_builds) |
| 164 # Pick a starting point, try to get HEAD for this. |
| 165 if opts.end: |
| 166 end_rev = opts.end |
| 167 else: |
| 168 end_rev = '999.0.0.0' |
| 169 if not opts.official_builds: |
| 170 end_rev = GetChromiumRevision(context.GetLastChangeURL()) |
| 171 |
| 172 # Find out where to start. |
| 173 if opts.start: |
| 174 start_rev = opts.start |
| 175 else: |
| 176 if opts.official_builds: |
| 177 r = int(end_rev[:end_rev.find('.')]) - opts.revisions + 1 |
| 178 start_rev = str(r) + '.0.0.0' |
| 179 else: |
| 180 start_rev = int(end_rev) - opts.revisions + 1 |
| 181 |
| 182 if opts.official_builds: |
| 183 start_rev = LooseVersion(start_rev) |
| 184 end_rev = LooseVersion(end_rev) |
| 185 else: |
| 186 start_rev = int(start_rev) |
| 187 end_rev = int(end_rev) |
| 188 |
| 189 if opts.official_builds and start_rev < LooseVersion('0.0.0.0') or \ |
| 190 not opts.official_builds and start_rev < 0: |
| 191 print ('The start revision (%s) must be greater than 0.\n' % start_rev) |
| 192 parser.print_help() |
| 193 return 1 |
| 194 |
| 195 if start_rev > end_rev: |
| 196 print ('The start revision (%s) must precede the end revision (%s).\n' % |
| 197 (start_rev, end_rev)) |
| 198 parser.print_help() |
| 199 return 1 |
| 200 |
| 201 if opts.times < 1: |
| 202 print('Number of times to run (%d) must be greater than or equal to 1.' % |
| 203 opts.times) |
| 204 parser.print_help() |
| 205 return 1 |
| 206 |
| 207 return GatherStats(opts.archive, opts.official_builds, opts.execute, |
| 208 start_rev, end_rev, opts.times, opts.output_directory) |
| 209 |
| 210 if __name__ == '__main__': |
| 211 sys.exit(main()) |
OLD | NEW |