Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Script which gathers and merges the JSON results from multiple | 6 """Script which gathers and merges the JSON results from multiple |
| 7 swarming shards of a step on the waterfall. | 7 swarming shards of a step on the waterfall. |
| 8 | 8 |
| 9 This is used to feed in the per-test times of previous runs of tests | 9 This is used to feed in the per-test times of previous runs of tests |
| 10 to the browser_test_runner's sharding algorithm, to improve shard | 10 to the browser_test_runner's sharding algorithm, to improve shard |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 138 for k in src.iterkeys(): | 138 for k in src.iterkeys(): |
| 139 if k not in dest: | 139 if k not in dest: |
| 140 dest[k] = src[k] | 140 dest[k] = src[k] |
| 141 else: | 141 else: |
| 142 dest[k] = Merge(dest[k], src[k]) | 142 dest[k] = Merge(dest[k], src[k]) |
| 143 return dest | 143 return dest |
| 144 | 144 |
| 145 return src | 145 return src |
| 146 | 146 |
| 147 | 147 |
| 148 def ExtractTestTimes(node, node_name, dest): | |
| 149 if 'times' in node: | |
| 150 dest[node_name] = node['times'][0] | |
|
nednguyen
2017/02/21 13:39:39
hmhh, should we use dest[node_name] = sum(node['ti
Ken Russell (switch to Gerrit)
2017/02/22 01:19:13
Thanks, good idea. Done.
| |
| 151 else: | |
| 152 # Currently the prefix names in the trie are dropped. Could | |
| 153 # concatenate them if the naming convention is changed. | |
| 154 for k in node.iterkeys(): | |
| 155 if isinstance(node[k], dict): | |
| 156 ExtractTestTimes(node[k], k, dest) | |
| 157 | |
| 148 def main(): | 158 def main(): |
| 149 rest_args = sys.argv[1:] | 159 rest_args = sys.argv[1:] |
| 150 parser = argparse.ArgumentParser( | 160 parser = argparse.ArgumentParser( |
| 151 description='Gather JSON results from a run of a Swarming test.', | 161 description='Gather JSON results from a run of a Swarming test.', |
| 152 formatter_class=argparse.ArgumentDefaultsHelpFormatter) | 162 formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| 153 parser.add_argument('-v', '--verbose', action='count', default=0, | 163 parser.add_argument('-v', '--verbose', action='count', default=0, |
| 154 help='Enable verbose output (specify multiple times ' | 164 help='Enable verbose output (specify multiple times ' |
| 155 'for more output)') | 165 'for more output)') |
| 156 parser.add_argument('--waterfall', type=str, default='chromium.gpu.fyi', | 166 parser.add_argument('--waterfall', type=str, default='chromium.gpu.fyi', |
| 157 help='Which waterfall to examine') | 167 help='Which waterfall to examine') |
| 158 parser.add_argument('--bot', type=str, default='Linux Release (NVIDIA)', | 168 parser.add_argument('--bot', type=str, default='Linux Release (NVIDIA)', |
| 159 help='Which bot on the waterfall to examine') | 169 help='Which bot on the waterfall to examine') |
| 160 parser.add_argument('--build', default=-1, type=int, | 170 parser.add_argument('--build', default=-1, type=int, |
| 161 help='Which build to fetch (-1 means most recent)') | 171 help='Which build to fetch (-1 means most recent)') |
| 162 parser.add_argument('--step', type=str, default='webgl2_conformance_tests', | 172 parser.add_argument('--step', type=str, default='webgl2_conformance_tests', |
| 163 help='Which step to fetch (treated as a prefix)') | 173 help='Which step to fetch (treated as a prefix)') |
| 164 parser.add_argument('--output', type=str, default='output.json', | 174 parser.add_argument('--output', type=str, default='output.json', |
| 165 help='Name of output file') | 175 help='Name of output file; contains only test run times') |
| 176 parser.add_argument('--full-output', type=str, default='', | |
| 177 help='Name of complete output file if desired') | |
| 166 parser.add_argument('--leak-temp-dir', action='store_true', default=False, | 178 parser.add_argument('--leak-temp-dir', action='store_true', default=False, |
| 167 help='Deliberately leak temporary directory') | 179 help='Deliberately leak temporary directory') |
| 180 parser.add_argument('--start-from-temp-dir', type=str, default='', | |
| 181 help='Start from temporary directory (for debugging)') | |
| 168 | 182 |
| 169 options = parser.parse_args(rest_args) | 183 options = parser.parse_args(rest_args) |
| 170 | 184 |
| 171 Swarming.CheckAuth() | 185 if options.start_from_temp_dir: |
| 186 tmpdir = options.start_from_temp_dir | |
| 187 shard_dirs = [f for f in os.listdir(tmpdir) | |
| 188 if os.path.isdir(os.path.join(tmpdir, f))] | |
| 189 numTaskIDs = len(shard_dirs) | |
| 190 else: | |
| 191 Swarming.CheckAuth() | |
| 172 | 192 |
| 173 waterfall = Waterfall(options.waterfall) | 193 waterfall = Waterfall(options.waterfall) |
| 174 build = options.build | 194 build = options.build |
| 175 if build < 0: | 195 if build < 0: |
| 176 build = waterfall.GetMostRecentlyCompletedBuildNumberForBot(options.bot) | 196 build = waterfall.GetMostRecentlyCompletedBuildNumberForBot(options.bot) |
| 177 | 197 |
| 178 build_json = waterfall.GetJsonForBuild(options.bot, build) | 198 build_json = waterfall.GetJsonForBuild(options.bot, build) |
| 179 | 199 |
| 180 if options.verbose: | 200 if options.verbose: |
| 181 print 'Fetching information from %s, bot %s, build %s' % ( | 201 print 'Fetching information from %s, bot %s, build %s' % ( |
| 182 options.waterfall, options.bot, build) | 202 options.waterfall, options.bot, build) |
| 183 | 203 |
| 184 taskIDs = [] | 204 taskIDs = [] |
| 185 for s in build_json['steps']: | 205 for s in build_json['steps']: |
| 186 if s['name'].startswith(options.step): | 206 if s['name'].startswith(options.step): |
| 187 # Found the step. | 207 # Found the step. |
| 188 # | 208 # |
| 189 # The Swarming shards happen to be listed in the 'urls' property | 209 # The Swarming shards happen to be listed in the 'urls' property |
| 190 # of the step. Iterate down them. | 210 # of the step. Iterate down them. |
| 191 if 'urls' not in s or not s['urls']: | 211 if 'urls' not in s or not s['urls']: |
| 192 # Note: we could also just download json.output if it exists. | 212 # Note: we could also just download json.output if it exists. |
| 193 print ('%s on waterfall %s, bot %s, build %s doesn\'t ' | 213 print ('%s on waterfall %s, bot %s, build %s doesn\'t ' |
| 194 'look like a Swarmed task') % ( | 214 'look like a Swarmed task') % ( |
| 195 s['name'], options.waterfall, options.bot, build) | 215 s['name'], options.waterfall, options.bot, build) |
| 196 return 1 | 216 return 1 |
| 197 taskIDs = Swarming.ExtractShardTaskIDs(s['urls']) | 217 taskIDs = Swarming.ExtractShardTaskIDs(s['urls']) |
| 198 if options.verbose: | 218 if options.verbose: |
| 199 print 'Found Swarming task IDs for step %s' % s['name'] | 219 print 'Found Swarming task IDs for step %s' % s['name'] |
| 200 | 220 |
| 201 break | 221 break |
| 202 if not taskIDs: | 222 if not taskIDs: |
| 203 print 'Problem gathering the Swarming task IDs for %s' % options.step | 223 print 'Problem gathering the Swarming task IDs for %s' % options.step |
| 204 return 1 | 224 return 1 |
| 205 | 225 |
| 206 # Collect the results. | 226 # Collect the results. |
| 207 tmpdir = tempfile.mkdtemp() | 227 tmpdir = tempfile.mkdtemp() |
| 208 Swarming.Collect(taskIDs, tmpdir, options.verbose) | 228 Swarming.Collect(taskIDs, tmpdir, options.verbose) |
| 229 numTaskIDs = len(taskIDs) | |
| 209 | 230 |
| 210 # Shards' JSON outputs are in sequentially-numbered subdirectories | 231 # Shards' JSON outputs are in sequentially-numbered subdirectories |
| 211 # of the output directory. | 232 # of the output directory. |
| 212 merged_json = None | 233 merged_json = None |
| 213 for i in xrange(len(taskIDs)): | 234 for i in xrange(numTaskIDs): |
| 214 with open(os.path.join(tmpdir, str(i), 'output.json')) as f: | 235 with open(os.path.join(tmpdir, str(i), 'output.json')) as f: |
| 215 cur_json = JsonLoadStrippingUnicode(f) | 236 cur_json = JsonLoadStrippingUnicode(f) |
| 216 if not merged_json: | 237 if not merged_json: |
| 217 merged_json = cur_json | 238 merged_json = cur_json |
| 218 else: | 239 else: |
| 219 merged_json = Merge(merged_json, cur_json) | 240 merged_json = Merge(merged_json, cur_json) |
| 241 extracted_times = {'times':{}} | |
| 242 ExtractTestTimes(merged_json, '', extracted_times['times']) | |
| 220 | 243 |
| 221 with open(options.output, 'w') as f: | 244 with open(options.output, 'w') as f: |
| 245 json.dump(extracted_times, f, sort_keys=True, indent=2, | |
| 246 separators=(',', ': ')) | |
| 247 | |
| 248 if options.full_output: | |
| 222 json.dump(merged_json, f, sort_keys=True, indent=2, | 249 json.dump(merged_json, f, sort_keys=True, indent=2, |
| 223 separators=(',', ': ')) | 250 separators=(',', ': ')) |
| 224 | 251 |
| 225 if options.leak_temp_dir: | 252 if options.leak_temp_dir: |
| 226 print 'Temporary directory: %s' % tmpdir | 253 print 'Temporary directory: %s' % tmpdir |
| 227 else: | 254 else: |
| 228 shutil.rmtree(tmpdir) | 255 shutil.rmtree(tmpdir) |
| 229 | 256 |
| 230 return 0 | 257 return 0 |
| 231 | 258 |
| 232 | 259 |
| 233 if __name__ == "__main__": | 260 if __name__ == "__main__": |
| 234 sys.exit(main()) | 261 sys.exit(main()) |
| OLD | NEW |