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

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

Issue 2230103002: [Findit] Pipeline change to save suspected cls to data store. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@0808-resubmit-suspected_cl_model
Patch Set: Modified pipelines after design change. Created 4 years, 3 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 from collections import defaultdict 5 from collections import defaultdict
6 import logging 6 import logging
7 7
8 from google.appengine.ext import ndb 8 from google.appengine.ext import ndb
9 9
10 from common.git_repository import GitRepository 10 from common.git_repository import GitRepository
11 from common.http_client_appengine import HttpClientAppengine as HttpClient 11 from common.http_client_appengine import HttpClientAppengine as HttpClient
12 from common.pipeline_wrapper import BasePipeline 12 from common.pipeline_wrapper import BasePipeline
13 from common.waterfall import failure_type 13 from common.waterfall import failure_type
14 from model import analysis_approach_type
14 from model import analysis_status 15 from model import analysis_status
15 from model import result_status 16 from model import result_status
16 from model.wf_analysis import WfAnalysis 17 from model.wf_analysis import WfAnalysis
17 from model.wf_try_job import WfTryJob 18 from model.wf_try_job import WfTryJob
18 from model.wf_try_job_data import WfTryJobData 19 from model.wf_try_job_data import WfTryJobData
19 from waterfall.send_notification_for_culprit_pipeline import ( 20 from waterfall.send_notification_for_culprit_pipeline import (
20 SendNotificationForCulpritPipeline) 21 SendNotificationForCulpritPipeline)
22 from waterfall import suspected_cl_util
21 23
22 24
23 GIT_REPO = GitRepository( 25 GIT_REPO = GitRepository(
24 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) 26 'https://chromium.googlesource.com/chromium/src.git', HttpClient())
25 27
26 28
27 def _GetResultAnalysisStatus(analysis, result): 29 def _GetResultAnalysisStatus(analysis, result):
28 """Returns the analysis status based on existing status and try job result. 30 """Returns the analysis status based on existing status and try job result.
29 31
30 Args: 32 Args:
(...skipping 13 matching lines...) Expand all
44 if (try_job_found_culprit and 46 if (try_job_found_culprit and
45 (old_result_status is None or 47 (old_result_status is None or
46 old_result_status == result_status.NOT_FOUND_UNTRIAGED or 48 old_result_status == result_status.NOT_FOUND_UNTRIAGED or
47 old_result_status == result_status.NOT_FOUND_INCORRECT or 49 old_result_status == result_status.NOT_FOUND_INCORRECT or
48 old_result_status == result_status.NOT_FOUND_CORRECT)): 50 old_result_status == result_status.NOT_FOUND_CORRECT)):
49 return result_status.FOUND_UNTRIAGED 51 return result_status.FOUND_UNTRIAGED
50 52
51 return old_result_status 53 return old_result_status
52 54
53 55
54 def _GetSuspectedCLs(analysis, result): 56 # def _GetTestTryJobSuspectedCLs(culprit):
57 # # Suspected CLs are from test failures.
58 # suspected_cl_revisions = []
59 # suspected_cls = []
60 # for results in culprit.itervalues():
61 # for test_cl_info in results['tests'].itervalues():
62 # revision = test_cl_info['revision']
63 # if revision not in suspected_cl_revisions:
64 # suspected_cl_revisions.append(revision)
65 # suspected_cls.append(test_cl_info)
66 #
67 # return suspected_cls
68 #
69 #
70 # def _GetTryJobSuspectedCLs(result):
71 # """Returns all suspected CLs found by the try job.
72 #
73 # Args:
74 # result: A result dict containing the culprit from the results of
75 # this try job.
76 #
77 # Returns:
78 # A list of suspected CLs found by this try job.
79 # """
80 # if not result or not result.get('culprit'):
81 # return []
82 #
83 # culprit = result.get('culprit')
84 # compile_cl_info = culprit.get('compile')
85 #
86 # if compile_cl_info:
87 # # Suspected CL is from compile failure.
88 # return [compile_cl_info]
89 #
90 # return _GetTestTryJobSuspectedCLs(culprit)
chanli 2016/09/12 20:56:15 Have deleted this part.
91
92
93 def _GetSuspectedCLs(analysis, culprits):
55 """Returns a list of suspected CLs. 94 """Returns a list of suspected CLs.
56 95
57 Args: 96 Args:
58 analysis: The WfAnalysis entity corresponding to this try job. 97 analysis: The WfAnalysis entity corresponding to this try job.
59 result: A result dict containing the culprit from the results of 98 is_compile_try_job: True if the try job is for compile failure,
60 this try job. 99 otherwise False.
100 culprits: A list of suspected CLs found by the try job.
61 101
62 Returns: 102 Returns:
63 A combined list of suspected CLs from those already in analysis and those 103 A combined list of suspected CLs from those already in analysis and those
64 found by this try job. 104 found by this try job.
65 """ 105 """
66 suspected_cls = analysis.suspected_cls[:] if analysis.suspected_cls else [] 106 suspected_cls = analysis.suspected_cls[:] if analysis.suspected_cls else []
67 suspected_cl_revisions = [cl['revision'] for cl in suspected_cls] 107 suspected_cl_revisions = [cl['revision'] for cl in suspected_cls]
68 culprit = result.get('culprit')
69 compile_cl_info = culprit.get('compile')
70 108
71 if compile_cl_info: 109 for revision, try_job_suspected_cl in culprits.iteritems():
72 # Suspected CL is from compile failure.
73 revision = compile_cl_info.get('revision')
74 if revision not in suspected_cl_revisions: 110 if revision not in suspected_cl_revisions:
75 suspected_cl_revisions.append(revision) 111 suspected_cl_revisions.append(revision)
76 suspected_cls.append(compile_cl_info) 112 suspected_cls.append(try_job_suspected_cl)
77 return suspected_cls
78
79 # Suspected CLs are from test failures.
80 for results in culprit.itervalues():
81 if results.get('revision'):
82 # Non swarming test failures, only have step level failure info.
83 revision = results.get('revision')
84 cl_info = {
85 'url': results.get('url'),
86 'repo_name': results.get('repo_name'),
87 'revision': results.get('revision'),
88 'commit_position': results.get('commit_position')
89 }
90 if revision not in suspected_cl_revisions:
91 suspected_cl_revisions.append(revision)
92 suspected_cls.append(cl_info)
93 else:
94 for test_cl_info in results['tests'].values():
95 revision = test_cl_info.get('revision')
96 if revision not in suspected_cl_revisions:
97 suspected_cl_revisions.append(revision)
98 suspected_cls.append(test_cl_info)
99 113
100 return suspected_cls 114 return suspected_cls
101 115
102 116
103 def _GetFailedRevisionFromResultsDict(results_dict): 117 def _GetFailedRevisionFromResultsDict(results_dict):
104 """Finds the failed revision from the given dict of revisions. 118 """Finds the failed revision from the given dict of revisions.
105 119
106 Args: 120 Args:
107 results_dict: (dict) A dict that maps revisions to their results. For 121 results_dict: (dict) A dict that maps revisions to their results. For
108 example: 122 example:
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 if (not test_result['valid'] or 175 if (not test_result['valid'] or
162 test_result['status'] != 'failed'): # pragma: no cover 176 test_result['status'] != 'failed'): # pragma: no cover
163 continue 177 continue
164 178
165 failed_revisions.add(revision) 179 failed_revisions.add(revision)
166 180
167 if step not in culprit_map: 181 if step not in culprit_map:
168 culprit_map[step] = { 182 culprit_map[step] = {
169 'tests': {} 183 'tests': {}
170 } 184 }
171 if (not test_result['failures'] and
172 not culprit_map[step].get('revision')):
173 # Non swarming test failures, only have step level failure info.
174 culprit_map[step]['revision'] = revision
175 for failed_test in test_result['failures']: 185 for failed_test in test_result['failures']:
176 # Swarming tests, gets first failed revision for each test. 186 # Swarming tests, gets first failed revision for each test.
177 if failed_test not in culprit_map[step]['tests']: 187 if failed_test not in culprit_map[step]['tests']:
178 culprit_map[step]['tests'][failed_test] = { 188 culprit_map[step]['tests'][failed_test] = {
179 'revision': revision 189 'revision': revision
180 } 190 }
181 191
182 return culprit_map, list(failed_revisions) 192 return culprit_map, list(failed_revisions)
183 193
184 194
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 send_notification_right_now) 244 send_notification_right_now)
235 elif compile_suspected_cl: 245 elif compile_suspected_cl:
236 # A special case where try job didn't find any suspected cls, but 246 # A special case where try job didn't find any suspected cls, but
237 # heuristic found a suspected_cl. 247 # heuristic found a suspected_cl.
238 _StartSendNotificationPipeline( 248 _StartSendNotificationPipeline(
239 master_name, builder_name, build_number, 249 master_name, builder_name, build_number,
240 compile_suspected_cl['repo_name'], compile_suspected_cl['revision'], 250 compile_suspected_cl['repo_name'], compile_suspected_cl['revision'],
241 send_notification_right_now=True) 251 send_notification_right_now=True)
242 252
243 253
254 def _GetTestFailureCausedByCL(result):
255 if not result:
256 return None
257
258 failures = {}
259 for step_name, step_result in result.iteritems():
260 if step_result['status'] == 'failed':
261 failures[step_name] = step_result['failures']
262
263 return failures
264
265
244 class IdentifyTryJobCulpritPipeline(BasePipeline): 266 class IdentifyTryJobCulpritPipeline(BasePipeline):
245 """A pipeline to identify culprit CL info based on try job compile results.""" 267 """A pipeline to identify culprit CL info based on try job compile results."""
246 268
247 def _GetCulpritInfo(self, failed_revisions): 269 def _GetCulpritInfo(self, failed_revisions):
248 """Gets commit_positions and review urls for revisions.""" 270 """Gets commit_positions and review urls for revisions."""
249 culprits = {} 271 culprits = {}
250 # TODO(lijeffrey): remove hard-coded 'chromium' when DEPS file parsing is 272 # TODO(lijeffrey): remove hard-coded 'chromium' when DEPS file parsing is
251 # supported. 273 # supported.
252 for failed_revision in failed_revisions: 274 for failed_revision in failed_revisions:
253 culprits[failed_revision] = { 275 culprits[failed_revision] = {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 } 310 }
289 failed_revisions.add(revision) 311 failed_revisions.add(revision)
290 return culprit_map, list(failed_revisions) 312 return culprit_map, list(failed_revisions)
291 313
292 return _GetCulpritsForTestsFromResultsDict( 314 return _GetCulpritsForTestsFromResultsDict(
293 blame_list, result['report'].get('result')) 315 blame_list, result['report'].get('result'))
294 316
295 def _UpdateCulpritMapWithCulpritInfo(self, culprit_map, culprits): 317 def _UpdateCulpritMapWithCulpritInfo(self, culprit_map, culprits):
296 """Fills in commit_position and review url for each failed rev in map.""" 318 """Fills in commit_position and review url for each failed rev in map."""
297 for step_culprit in culprit_map.values(): 319 for step_culprit in culprit_map.values():
298 if step_culprit.get('revision'):
299 culprit = culprits[step_culprit['revision']]
300 step_culprit['commit_position'] = culprit['commit_position']
301 step_culprit['url'] = culprit['url']
302 step_culprit['repo_name'] = culprit['repo_name']
303 for test_culprit in step_culprit.get('tests', {}).values(): 320 for test_culprit in step_culprit.get('tests', {}).values():
304 test_revision = test_culprit['revision'] 321 test_revision = test_culprit['revision']
305 test_culprit.update(culprits[test_revision]) 322 test_culprit.update(culprits[test_revision])
306 323
307 def _GetCulpritDataForTest(self, culprit_map): 324 def _GetCulpritDataForTest(self, culprit_map):
308 """Gets culprit revision for each failure for try job metadata.""" 325 """Gets culprit revision for each failure for try job metadata."""
309 culprit_data = {} 326 culprit_data = {}
310 for step, step_culprit in culprit_map.iteritems(): 327 for step, step_culprit in culprit_map.iteritems():
311 if step_culprit['tests']: 328 culprit_data[step] = {}
312 culprit_data[step] = {} 329 for test, test_culprit in step_culprit['tests'].iteritems():
313 for test, test_culprit in step_culprit['tests'].iteritems(): 330 culprit_data[step][test] = test_culprit['revision']
314 culprit_data[step][test] = test_culprit['revision']
315 else:
316 culprit_data[step] = step_culprit['revision']
317 return culprit_data 331 return culprit_data
318 332
319 # Arguments number differs from overridden method - pylint: disable=W0221 333 # Arguments number differs from overridden method - pylint: disable=W0221
320 def run( 334 def run(
321 self, master_name, builder_name, build_number, blame_list, try_job_type, 335 self, master_name, builder_name, build_number, blame_list, try_job_type,
322 try_job_id, result): 336 try_job_id, result):
323 """Identifies the information for failed revisions. 337 """Identifies the information for failed revisions.
324 338
325 Please refer to try_job_result_format.md for format check. 339 Please refer to try_job_result_format.md for format check.
326 """ 340 """
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 else: # pragma: no cover 376 else: # pragma: no cover
363 result_to_update.append(result) 377 result_to_update.append(result)
364 try_job_result.status = analysis_status.COMPLETED 378 try_job_result.status = analysis_status.COMPLETED
365 try_job_result.put() 379 try_job_result.put()
366 380
367 @ndb.transactional 381 @ndb.transactional
368 def UpdateWfAnalysisWithTryJobResult(): 382 def UpdateWfAnalysisWithTryJobResult():
369 if not culprits: 383 if not culprits:
370 return 384 return
371 385
386
387 print culprits
chanli 2016/09/12 20:56:15 Have removed unnecessary lines.
388
372 analysis = WfAnalysis.Get(master_name, builder_name, build_number) 389 analysis = WfAnalysis.Get(master_name, builder_name, build_number)
373 # Update analysis result and suspected CLs with results of this try job if 390 # Update analysis result and suspected CLs with results of this try job if
374 # culprits were found. 391 # culprits were found.
375 updated_result_status = _GetResultAnalysisStatus(analysis, result) 392 updated_result_status = _GetResultAnalysisStatus(analysis, result)
376 updated_suspected_cls = _GetSuspectedCLs(analysis, result) 393 updated_suspected_cls = _GetSuspectedCLs(analysis, culprits)
377
378 if (analysis.result_status != updated_result_status or 394 if (analysis.result_status != updated_result_status or
379 analysis.suspected_cls != updated_suspected_cls): 395 analysis.suspected_cls != updated_suspected_cls):
380 analysis.result_status = updated_result_status 396 analysis.result_status = updated_result_status
381 analysis.suspected_cls = updated_suspected_cls 397 analysis.suspected_cls = updated_suspected_cls
382 analysis.put() 398 analysis.put()
383 399
400 def UpdateSuspectedCLs():
401 if not culprits:
402 return
403
404 # Creates or updates each suspected_cl.
405 for culprit in culprits.values():
406 revision = culprit['revision']
407 if try_job_type == failure_type.COMPILE:
408 failures = {'compile': []}
409 else:
410 failures = _GetTestFailureCausedByCL(
411 result.get('report', {}).get('result', {}).get(revision))
412
413 suspected_cl_util.UpdateSuspectedCL(
414 culprit['repo_name'], revision, culprit.get('commit_position'),
415 analysis_approach_type.TRY_JOB, master_name, builder_name,
416 build_number, try_job_type, failures, None)
417
418 # try_job_suspected_cls = _GetTryJobSuspectedCLs(result)
384 # Store try-job results. 419 # Store try-job results.
385 UpdateTryJobResult() 420 UpdateTryJobResult()
386 421
387 # Saves cls found by heuristic approach for later use. 422 # Saves cls found by heuristic approach for later use.
388 # This part must be before UpdateWfAnalysisWithTryJobResult(). 423 # This part must be before UpdateWfAnalysisWithTryJobResult().
389 analysis = WfAnalysis.Get(master_name, builder_name, build_number) 424 analysis = WfAnalysis.Get(master_name, builder_name, build_number)
390 heuristic_cls = _GetHeuristicSuspectedCLs(analysis) 425 heuristic_cls = _GetHeuristicSuspectedCLs(analysis)
391 compile_suspected_cl = ( 426 compile_suspected_cl = (
392 _GetSuspectedCLFoundByHeuristicForCompile(analysis) 427 _GetSuspectedCLFoundByHeuristicForCompile(analysis)
393 if try_job_type == failure_type.COMPILE else None) 428 if try_job_type == failure_type.COMPILE else None)
394 429
395 # Add try-job results to WfAnalysis. 430 # Add try-job results to WfAnalysis.
396 UpdateWfAnalysisWithTryJobResult() 431 UpdateWfAnalysisWithTryJobResult()
432 # Updates suspected_cl
433 UpdateSuspectedCLs()
397 434
398 _NotifyCulprits(master_name, builder_name, build_number, culprits, 435 _NotifyCulprits(master_name, builder_name, build_number, culprits,
399 heuristic_cls, compile_suspected_cl) 436 heuristic_cls, compile_suspected_cl)
400 return result.get('culprit') if result else None 437 return result.get('culprit') if result else None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698