Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Side by Side Diff: tools/gen_bench_expectations_from_codereview.py

Issue 363833004: Fix RecreateSkps (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Add TODO Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2014 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 7
8 """Generate new bench expectations from results of trybots on a code review.""" 8 """Generate new bench expectations from results of trybots on a code review."""
9 9
10 10
11 import collections 11 import collections
12 import compare_codereview 12 import compare_codereview
13 import json
13 import os 14 import os
14 import re 15 import re
15 import shutil 16 import shutil
16 import subprocess
17 import sys 17 import sys
18 import urllib2
19
20 import fix_pythonpath # pylint: disable=W0611
21 from common.py.utils import shell_utils
18 22
19 23
20 BENCH_DATA_URL = 'gs://chromium-skia-gm/perfdata/%s/%s/*' 24 BENCH_DATA_URL = 'gs://chromium-skia-gm/perfdata/%s/%s/bench_*_data_*'
25 BUILD_STATUS_SUCCESS = 0
26 BUILD_STATUS_WARNINGS = 1
21 CHECKOUT_PATH = os.path.realpath(os.path.join( 27 CHECKOUT_PATH = os.path.realpath(os.path.join(
22 os.path.dirname(os.path.abspath(__file__)), os.pardir)) 28 os.path.dirname(os.path.abspath(__file__)), os.pardir))
23 TMP_BENCH_DATA_DIR = os.path.join(CHECKOUT_PATH, '.bench_data') 29 TMP_BENCH_DATA_DIR = os.path.join(CHECKOUT_PATH, '.bench_data')
24 30
25 31
26 TryBuild = collections.namedtuple( 32 TryBuild = collections.namedtuple(
27 'TryBuild', ['builder_name', 'build_number', 'is_finished']) 33 'TryBuild', ['builder_name', 'build_number', 'is_finished', 'json_url'])
28 34
29 35
30 def find_all_builds(codereview_url): 36 def find_all_builds(codereview_url):
31 """Finds and returns information about trybot runs for a code review. 37 """Finds and returns information about trybot runs for a code review.
32 38
33 Args: 39 Args:
34 codereview_url: URL of the codereview in question. 40 codereview_url: URL of the codereview in question.
35 41
36 Returns: 42 Returns:
37 List of NamedTuples: (builder_name, build_number, is_finished) 43 List of NamedTuples: (builder_name, build_number, is_finished)
38 """ 44 """
39 results = compare_codereview.CodeReviewHTMLParser().parse(codereview_url) 45 results = compare_codereview.CodeReviewHTMLParser().parse(codereview_url)
40 try_builds = [] 46 try_builds = []
41 for builder, data in results.iteritems(): 47 for builder, data in results.iteritems():
42 if builder.startswith('Perf'): 48 if builder.startswith('Perf'):
43 build_num = data.url.split('/')[-1] if data.url else None 49 build_num = None
50 json_url = None
51 if data.url:
52 split_url = data.url.split('/')
53 build_num = split_url[-1]
54 split_url.insert(split_url.index('builders'), 'json')
55 json_url = '/'.join(split_url)
44 is_finished = (data.status not in ('pending', 'try-pending') and 56 is_finished = (data.status not in ('pending', 'try-pending') and
45 build_num is not None) 57 build_num is not None)
46 try_builds.append(TryBuild(builder_name=builder, 58 try_builds.append(TryBuild(builder_name=builder,
47 build_number=build_num, 59 build_number=build_num,
48 is_finished=is_finished)) 60 is_finished=is_finished,
61 json_url=json_url))
49 return try_builds 62 return try_builds
50 63
51 64
52 def _all_trybots_finished(try_builds): 65 def _all_trybots_finished(try_builds):
53 """Return True iff all of the given try jobs have finished. 66 """Return True iff all of the given try jobs have finished.
54 67
55 Args: 68 Args:
56 try_builds: list of TryBuild instances. 69 try_builds: list of TryBuild instances.
57 70
58 Returns: 71 Returns:
(...skipping 19 matching lines...) Expand all
78 91
79 def get_bench_data(builder, build_num, dest_dir): 92 def get_bench_data(builder, build_num, dest_dir):
80 """Download the bench data for the given builder at the given build_num. 93 """Download the bench data for the given builder at the given build_num.
81 94
82 Args: 95 Args:
83 builder: string; name of the builder. 96 builder: string; name of the builder.
84 build_num: string; build number. 97 build_num: string; build number.
85 dest_dir: string; destination directory for the bench data. 98 dest_dir: string; destination directory for the bench data.
86 """ 99 """
87 url = BENCH_DATA_URL % (builder, build_num) 100 url = BENCH_DATA_URL % (builder, build_num)
88 subprocess.check_call(['gsutil', 'cp', '-R', url, dest_dir], 101 shell_utils.run(['gsutil', 'cp', '-R', url, dest_dir])
89 stdout=subprocess.PIPE,
90 stderr=subprocess.PIPE)
91 102
92 103
93 def find_revision_from_downloaded_data(dest_dir): 104 def find_revision_from_downloaded_data(dest_dir):
94 """Finds the revision at which the downloaded data was generated. 105 """Finds the revision at which the downloaded data was generated.
95 106
96 Args: 107 Args:
97 dest_dir: string; directory holding the downloaded data. 108 dest_dir: string; directory holding the downloaded data.
98 109
99 Returns: 110 Returns:
100 The revision (git commit hash) at which the downloaded data was 111 The revision (git commit hash) at which the downloaded data was
101 generated, or None if no revision can be found. 112 generated, or None if no revision can be found.
102 """ 113 """
103 for data_file in os.listdir(dest_dir): 114 for data_file in os.listdir(dest_dir):
104 match = re.match('bench_(?P<revision>[0-9a-fA-F]{2,40})_data.*', data_file) 115 match = re.match('bench_(?P<revision>[0-9a-fA-F]{2,40})_data.*', data_file)
105 if match: 116 if match:
106 return match.group('revision') 117 return match.group('revision')
107 return None 118 return None
108 119
109 120
110 class TrybotNotFinishedError(Exception): 121 class TrybotNotFinishedError(Exception):
111 pass 122 pass
112 123
113 124
125 def _step_succeeded(try_build, step_name):
126 """Return True if the given step succeeded and False otherwise.
127
128 This function talks to the build master's JSON interface, which is slow.
129
130 TODO(borenet): There are now a few places which talk to the master's JSON
131 interface. Maybe it'd be worthwhile to create a module which does this.
132
133 Args:
134 try_build: TryBuild instance; the build we're concerned about.
135 step_name: string; name of the step we're concerned about.
136 """
137 step_url = '/'.join((try_build.json_url, 'steps', step_name))
138 step_data = json.load(urllib2.urlopen(step_url))
139 # step_data['results'] may not be present if the step succeeded. If present,
140 # it is a list whose first element is a result code, per the documentation:
141 # http://docs.buildbot.net/latest/developer/results.html
142 result = step_data.get('results', [BUILD_STATUS_SUCCESS])[0]
143 if result in (BUILD_STATUS_SUCCESS, BUILD_STATUS_WARNINGS):
144 return True
145 return False
146
147
114 def gen_bench_expectations_from_codereview(codereview_url, 148 def gen_bench_expectations_from_codereview(codereview_url,
115 error_on_unfinished=True): 149 error_on_unfinished=True,
150 error_on_try_failure=True):
116 """Generate bench expectations from a code review. 151 """Generate bench expectations from a code review.
117 152
118 Scans the given code review for Perf trybot runs. Downloads the results of 153 Scans the given code review for Perf trybot runs. Downloads the results of
119 finished trybots and uses them to generate new expectations for their 154 finished trybots and uses them to generate new expectations for their
120 waterfall counterparts. 155 waterfall counterparts.
121 156
122 Args: 157 Args:
123 url: string; URL of the code review. 158 url: string; URL of the code review.
124 error_on_unfinished: bool; throw an error if any trybot has not finished. 159 error_on_unfinished: bool; throw an error if any trybot has not finished.
160 error_on_try_failure: bool; throw an error if any trybot failed an
161 important step.
125 """ 162 """
126 try_builds = find_all_builds(codereview_url) 163 try_builds = find_all_builds(codereview_url)
127 164
128 # Verify that all trybots have finished running. 165 # Verify that all trybots have finished running.
129 if error_on_unfinished and not _all_trybots_finished(try_builds): 166 if error_on_unfinished and not _all_trybots_finished(try_builds):
130 raise TrybotNotFinishedError('Not all trybots have finished.') 167 raise TrybotNotFinishedError('Not all trybots have finished.')
131 168
169 failed_run = []
132 failed_data_pull = [] 170 failed_data_pull = []
133 failed_gen_expectations = [] 171 failed_gen_expectations = []
134 172
173 # Don't even try to do anything if BenchPictures, PostBench, or
174 # UploadBenchResults failed.
175 for try_build in try_builds:
176 for step in ('BenchPictures', 'PostBench', 'UploadBenchResults'):
177 if not _step_succeeded(try_build, step):
178 msg = '%s failed on %s!' % (step, try_build.builder_name)
179 if error_on_try_failure:
180 raise Exception(msg)
181 print 'WARNING: %s Skipping.' % msg
182 failed_run.append(try_build.builder_name)
183
135 if os.path.isdir(TMP_BENCH_DATA_DIR): 184 if os.path.isdir(TMP_BENCH_DATA_DIR):
136 shutil.rmtree(TMP_BENCH_DATA_DIR) 185 shutil.rmtree(TMP_BENCH_DATA_DIR)
137 186
138 for try_build in try_builds: 187 for try_build in try_builds:
139 try_builder = try_build.builder_name 188 try_builder = try_build.builder_name
189
190 # Even if we're not erroring out on try failures, we can't generate new
191 # expectations for failed bots.
192 if try_builder in failed_run:
193 continue
194
140 builder = try_builder.replace('-Trybot', '') 195 builder = try_builder.replace('-Trybot', '')
141 196
142 # Download the data. 197 # Download the data.
143 dest_dir = os.path.join(TMP_BENCH_DATA_DIR, builder) 198 dest_dir = os.path.join(TMP_BENCH_DATA_DIR, builder)
144 os.makedirs(dest_dir) 199 os.makedirs(dest_dir)
145 try: 200 try:
146 get_bench_data(try_builder, try_build.build_number, dest_dir) 201 get_bench_data(try_builder, try_build.build_number, dest_dir)
147 except subprocess.CalledProcessError: 202 except shell_utils.CommandFailedException:
148 failed_data_pull.append(try_builder) 203 failed_data_pull.append(try_builder)
149 continue 204 continue
150 205
151 # Find the revision at which the data was generated. 206 # Find the revision at which the data was generated.
152 revision = find_revision_from_downloaded_data(dest_dir) 207 revision = find_revision_from_downloaded_data(dest_dir)
153 if not revision: 208 if not revision:
154 # If we can't find a revision, then something is wrong with the data we 209 # If we can't find a revision, then something is wrong with the data we
155 # downloaded. Skip this builder. 210 # downloaded. Skip this builder.
156 failed_data_pull.append(try_builder) 211 failed_data_pull.append(try_builder)
157 continue 212 continue
158 213
159 # Generate new expectations. 214 # Generate new expectations.
160 output_file = os.path.join(CHECKOUT_PATH, 'expectations', 'bench', 215 output_file = os.path.join(CHECKOUT_PATH, 'expectations', 'bench',
161 'bench_expectations_%s.txt' % builder) 216 'bench_expectations_%s.txt' % builder)
162 try: 217 try:
163 subprocess.check_call(['python', 218 shell_utils.run(['python',
164 os.path.join(CHECKOUT_PATH, 'bench', 219 os.path.join(CHECKOUT_PATH, 'bench',
165 'gen_bench_expectations.py'), 220 'gen_bench_expectations.py'),
166 '-b', builder, '-o', output_file, 221 '-b', builder, '-o', output_file,
167 '-d', dest_dir, '-r', revision]) 222 '-d', dest_dir, '-r', revision])
168 except subprocess.CalledProcessError: 223 except shell_utils.CommandFailedException:
169 failed_gen_expectations.append(builder) 224 failed_gen_expectations.append(builder)
170 225
171 failure = '' 226 failure = ''
172 if failed_data_pull: 227 if failed_data_pull:
173 failure += 'Failed to load data for: %s\n\n' % ','.join(failed_data_pull) 228 failure += 'Failed to load data for: %s\n\n' % ','.join(failed_data_pull)
174 if failed_gen_expectations: 229 if failed_gen_expectations:
175 failure += 'Failed to generate expectations for: %s\n\n' % ','.join( 230 failure += 'Failed to generate expectations for: %s\n\n' % ','.join(
176 failed_gen_expectations) 231 failed_gen_expectations)
177 if failure: 232 if failure:
178 raise Exception(failure) 233 raise Exception(failure)
179 234
180 235
181 if __name__ == '__main__': 236 if __name__ == '__main__':
182 gen_bench_expectations_from_codereview(sys.argv[1]) 237 gen_bench_expectations_from_codereview(sys.argv[1])
183 238
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698