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

Side by Side Diff: appengine/findit/waterfall/build_failure_analysis.py

Issue 1154593005: [Findit] Add a sub-pipeline to analyze failures caused by DEPS rolls. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Improve readability. Created 5 years, 6 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
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import collections 5 import collections
6 import os 6 import os
7 import re 7 import re
8 8
9 from common.diff import ChangeType 9 from common.diff import ChangeType
10 from waterfall.failure_signal import FailureSignal 10 from waterfall.failure_signal import FailureSignal
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 if changed_src_file_path != file_path_in_log: 152 if changed_src_file_path != file_path_in_log:
153 hint = '%s %s (%s was in log)' % ( 153 hint = '%s %s (%s was in log)' % (
154 change_action, changed_src_file_path, file_path_in_log) 154 change_action, changed_src_file_path, file_path_in_log)
155 else: 155 else:
156 hint = '%s %s (and it was in log)' % ( 156 hint = '%s %s (and it was in log)' % (
157 change_action, changed_src_file_path) 157 change_action, changed_src_file_path)
158 158
159 self._hints[hint] += score 159 self._hints[hint] += score
160 self._score += score 160 self._score += score
161 161
162 def AddDEPSRoll(self, dep_path, dep_repo_url, dep_new_revision,
163 dep_old_revision, file_path_in_log, score):
164 url_to_changes_in_roll = '%s/+log/%s..%s?pretty=fuller' % (
165 dep_repo_url, dep_old_revision[:12], dep_new_revision[:12])
166 hint = ('Rolled %s with changes %s (and %s was in log)' % (
167 dep_path, url_to_changes_in_roll, file_path_in_log))
168 self._hints[hint] = score
169 self._score += score
170
162 def ToDict(self): 171 def ToDict(self):
163 return { 172 return {
164 'score': self._score, 173 'score': self._score,
165 'hints': self._hints, 174 'hints': self._hints,
166 } 175 }
167 176
168 177
169 def _CheckFile(touched_file, 178 def _CheckFile(touched_file,
170 file_path_in_log, 179 file_path_in_log,
171 justification, 180 justification,
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 elif _IsRelated(changed_src_file_path, file_path_in_log): 236 elif _IsRelated(changed_src_file_path, file_path_in_log):
228 score = 1 237 score = 1
229 238
230 if score: 239 if score:
231 justification.AddFileChange('deleted', 240 justification.AddFileChange('deleted',
232 changed_src_file_path, 241 changed_src_file_path,
233 file_path_in_log, 242 file_path_in_log,
234 score, 243 score,
235 file_name_occurrences.get(file_name)) 244 file_name_occurrences.get(file_name))
236 245
237 def _CheckFiles(failure_signal, change_log): 246
247 def _CheckFiles(failure_signal, change_log, deps_info):
238 """Check files in the given change log of a CL against the failure signal. 248 """Check files in the given change log of a CL against the failure signal.
239 249
240 Args: 250 Args:
241 failure_signal (FailureSignal): The failure signal of a failed step or test. 251 failure_signal (FailureSignal): The failure signal of a failed step or test.
242 change_log (dict): The change log of a CL as returned by 252 change_log (dict): The change log of a CL as returned by
243 common.change_log.ChangeLog.ToDict(). 253 common.change_log.ChangeLog.ToDict().
254 deps_info (dict): Output of pipeline ExtractDEPSInfoPipeline.
244 255
245 Returns: 256 Returns:
246 A dict as returned by _Justification.ToDict() if the CL is suspected for the 257 A dict as returned by _Justification.ToDict() if the CL is suspected for the
247 failure; otherwise None. 258 failure; otherwise None.
248 """ 259 """
249 # Use a dict to map each file name of the touched files to their occurrences. 260 # Use a dict to map each file name of the touched files to their occurrences.
250 file_name_occurrences = collections.defaultdict(int) 261 file_name_occurrences = collections.defaultdict(int)
251 for touched_file in change_log['touched_files']: 262 for touched_file in change_log['touched_files']:
252 change_type = touched_file['change_type'] 263 change_type = touched_file['change_type']
253 if (change_type in (ChangeType.ADD, ChangeType.COPY, 264 if (change_type in (ChangeType.ADD, ChangeType.COPY,
254 ChangeType.RENAME, ChangeType.MODIFY)): 265 ChangeType.RENAME, ChangeType.MODIFY)):
255 file_name = os.path.basename(touched_file['new_path']) 266 file_name = os.path.basename(touched_file['new_path'])
256 file_name_occurrences[file_name] += 1 267 file_name_occurrences[file_name] += 1
257 268
258 if change_type in (ChangeType.DELETE, ChangeType.RENAME): 269 if change_type in (ChangeType.DELETE, ChangeType.RENAME):
259 file_name = os.path.basename(touched_file['old_path']) 270 file_name = os.path.basename(touched_file['old_path'])
260 file_name_occurrences[file_name] += 1 271 file_name_occurrences[file_name] += 1
261 272
262 justification = _Justification() 273 justification = _Justification()
263 274
264 for file_path_in_log, _ in failure_signal.files.iteritems(): 275 for file_path_in_log, _ in failure_signal.files.iteritems():
265 # TODO(stgao): remove this hack when DEPS parsing is supported. 276 # Strip src/ from file path to make all files relative to the chromium root
266 if file_path_in_log.startswith('src/'): 277 # directory.
267 file_path_in_log = file_path_in_log[4:] 278 file_path_in_log = file_path_in_log.lstrip('src/')
268 279
269 for touched_file in change_log['touched_files']: 280 for touched_file in change_log['touched_files']:
270 _CheckFile( 281 _CheckFile(
271 touched_file, file_path_in_log, justification, file_name_occurrences) 282 touched_file, file_path_in_log, justification, file_name_occurrences)
272 283
284 for roll in deps_info.get('deps_rolls', {}).get(change_log['revision'], []):
285 dep_path = roll['path'].lstrip('src/')
286 if file_path_in_log.startswith(dep_path):
287 justification.AddDEPSRoll(
288 dep_path, roll['repo_url'], roll['new_revision'],
289 roll['old_revision'], file_path_in_log[len(dep_path):], 2)
290
273 if not justification.score: 291 if not justification.score:
274 return None 292 return None
275 else: 293 else:
276 return justification.ToDict() 294 return justification.ToDict()
277 295
278 296
279 def AnalyzeBuildFailure(failure_info, change_logs, failure_signals): 297 def AnalyzeBuildFailure(
298 failure_info, change_logs, deps_info, failure_signals):
280 """Analyze the given failure signals, and figure out culprit CLs. 299 """Analyze the given failure signals, and figure out culprit CLs.
281 300
282 Args: 301 Args:
283 failure_info (dict): Output of pipeline DetectFirstFailurePipeline. 302 failure_info (dict): Output of pipeline DetectFirstFailurePipeline.
284 change_logs (dict): Output of pipeline PullChangelogPipeline. 303 change_logs (dict): Output of pipeline PullChangelogPipeline.
304 deps_info (dict): Output of pipeline ExtractDEPSInfoPipeline.
285 failure_signals (dict): Output of pipeline ExtractSignalPipeline. 305 failure_signals (dict): Output of pipeline ExtractSignalPipeline.
286 306
287 Returns: 307 Returns:
288 A dict with the following form: 308 A dict with the following form:
289 { 309 {
290 'failures': [ 310 'failures': [
291 { 311 {
292 'step_name': 'compile', 312 'step_name': 'compile',
293 'first_failure': 230, 313 'first_failure': 230,
294 'last_pass': 229, 314 'last_pass': 229,
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 368
349 step_analysis_result = { 369 step_analysis_result = {
350 'step_name': step_name, 370 'step_name': step_name,
351 'first_failure': step_failure_info['first_failure'], 371 'first_failure': step_failure_info['first_failure'],
352 'last_pass': step_failure_info.get('last_pass'), 372 'last_pass': step_failure_info.get('last_pass'),
353 'suspected_cls': [], 373 'suspected_cls': [],
354 } 374 }
355 375
356 while build_number <= failed_build_number: 376 while build_number <= failed_build_number:
357 for revision in builds[str(build_number)]['blame_list']: 377 for revision in builds[str(build_number)]['blame_list']:
358 justification_dict = _CheckFiles(failure_signal, change_logs[revision]) 378 justification_dict = _CheckFiles(
379 failure_signal, change_logs[revision], deps_info)
359 380
360 if not justification_dict: 381 if not justification_dict:
361 continue 382 continue
362 383
363 step_analysis_result['suspected_cls'].append( 384 step_analysis_result['suspected_cls'].append(
364 CreateCLInfoDict(justification_dict, build_number, 385 CreateCLInfoDict(justification_dict, build_number,
365 change_logs[revision])) 386 change_logs[revision]))
366 387
367 build_number += 1 388 build_number += 1
368 389
369 # TODO(stgao): sort CLs by score. 390 # TODO(stgao): sort CLs by score.
370 analysis_result['failures'].append(step_analysis_result) 391 analysis_result['failures'].append(step_analysis_result)
371 392
372 return analysis_result 393 return analysis_result
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698