| OLD | NEW |
| 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 datetime import timedelta | 5 from datetime import timedelta |
| 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 import appengine_util | |
| 11 from common import constants | |
| 12 from common import time_util | 10 from common import time_util |
| 13 from common.waterfall import failure_type | 11 from common.waterfall import failure_type |
| 14 from model import analysis_status | 12 from model import analysis_status |
| 13 from model.base_build_model import BaseBuildModel |
| 15 from model.wf_analysis import WfAnalysis | 14 from model.wf_analysis import WfAnalysis |
| 16 from model.wf_build import WfBuild | 15 from model.wf_build import WfBuild |
| 17 from model.wf_failure_group import WfFailureGroup | 16 from model.wf_failure_group import WfFailureGroup |
| 18 from model.wf_try_job import WfTryJob | 17 from model.wf_try_job import WfTryJob |
| 19 from waterfall import waterfall_config | 18 from waterfall import waterfall_config |
| 20 | 19 |
| 21 | 20 |
| 22 def _ShouldBailOutForOutdatedBuild(build): | 21 def _ShouldBailOutForOutdatedBuild(build): |
| 23 return (time_util.GetUTCNow() - build.start_time).days > 0 | 22 return (time_util.GetUTCNow() - build.start_time).days > 0 |
| 24 | 23 |
| 25 | 24 |
| 26 def _CurrentBuildKey(master_name, builder_name, build_number): | |
| 27 return '%s/%s/%d' % (master_name, builder_name, build_number) | |
| 28 | |
| 29 | |
| 30 def _BlameListsIntersection(blame_list_1, blame_list_2): | 25 def _BlameListsIntersection(blame_list_1, blame_list_2): |
| 31 return set(blame_list_1) & set(blame_list_2) | 26 return set(blame_list_1) & set(blame_list_2) |
| 32 | 27 |
| 33 | 28 |
| 34 def _GetStepsAndTests(failed_steps): | 29 def _GetStepsAndTests(failed_steps): |
| 35 """Extracts failed steps and tests from failed_steps data structure. | 30 """Extracts failed steps and tests from failed_steps data structure. |
| 36 | 31 |
| 37 Args: | 32 Args: |
| 38 failed_steps: Failed steps and test, plus extra information. Example: | 33 failed_steps: Failed steps and test, plus extra information. Example: |
| 39 { | 34 { |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 | 258 |
| 264 return try_job_entity_revived_or_created | 259 return try_job_entity_revived_or_created |
| 265 | 260 |
| 266 | 261 |
| 267 def _NeedANewCompileTryJob( | 262 def _NeedANewCompileTryJob( |
| 268 master_name, builder_name, build_number, failure_info): | 263 master_name, builder_name, build_number, failure_info): |
| 269 | 264 |
| 270 compile_failure = failure_info['failed_steps'].get('compile', {}) | 265 compile_failure = failure_info['failed_steps'].get('compile', {}) |
| 271 if compile_failure: | 266 if compile_failure: |
| 272 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 267 analysis = WfAnalysis.Get(master_name, builder_name, build_number) |
| 273 analysis.failure_result_map['compile'] = '%s/%s/%d' % ( | 268 analysis.failure_result_map['compile'] = BaseBuildModel.CreateBuildId( |
| 274 master_name, builder_name, compile_failure['first_failure']) | 269 master_name, builder_name, compile_failure['first_failure']) |
| 275 analysis.put() | 270 analysis.put() |
| 276 | 271 |
| 277 if compile_failure['first_failure'] == compile_failure['current_failure']: | 272 if compile_failure['first_failure'] == compile_failure['current_failure']: |
| 278 return True | 273 return True |
| 279 | 274 |
| 280 return False | 275 return False |
| 281 | 276 |
| 282 | 277 |
| 283 def _CurrentBuildKeyInFailureResultMap(master_name, builder_name, build_number): | 278 def GetBuildKeyForBuildInfoInFailureResultMap( |
| 279 master_name, builder_name, build_number): |
| 284 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 280 analysis = WfAnalysis.Get(master_name, builder_name, build_number) |
| 285 failure_result_map = analysis.failure_result_map | 281 failure_result_map = analysis.failure_result_map |
| 286 current_build_key = _CurrentBuildKey(master_name, builder_name, build_number) | 282 current_build_key = BaseBuildModel.CreateBuildId( |
| 283 master_name, builder_name, build_number) |
| 287 for step_keys in failure_result_map.itervalues(): | 284 for step_keys in failure_result_map.itervalues(): |
| 288 for test_key in step_keys.itervalues(): | 285 for test_key in step_keys.itervalues(): |
| 289 if test_key == current_build_key: | 286 if test_key == current_build_key: |
| 290 return True | 287 return True |
| 291 return False | 288 return False |
| 292 | 289 |
| 293 | 290 |
| 294 def _NeedANewTestTryJob( | 291 def _NeedANewTestTryJob( |
| 295 master_name, builder_name, build_number, failure_info, force_try_job): | 292 master_name, builder_name, build_number, failure_info, force_try_job): |
| 296 if failure_info['failure_type'] != failure_type.TEST: | 293 if failure_info['failure_type'] != failure_type.TEST: |
| 297 return False | 294 return False |
| 298 | 295 |
| 299 if (not force_try_job and | 296 if (not force_try_job and |
| 300 waterfall_config.ShouldSkipTestTryJobs(master_name, builder_name)): | 297 waterfall_config.ShouldSkipTestTryJobs(master_name, builder_name)): |
| 301 logging.info('Test try jobs on %s, %s are not supported yet.', | 298 logging.info('Test try jobs on %s, %s are not supported yet.', |
| 302 master_name, builder_name) | 299 master_name, builder_name) |
| 303 return False | 300 return False |
| 304 | 301 |
| 305 return _CurrentBuildKeyInFailureResultMap( | 302 return GetBuildKeyForBuildInfoInFailureResultMap( |
| 306 master_name, builder_name, build_number) | 303 master_name, builder_name, build_number) |
| 307 | 304 |
| 308 | 305 |
| 309 def NeedANewTryJob( | 306 def NeedANewTryJob( |
| 310 master_name, builder_name, build_number, failure_info, signals, | 307 master_name, builder_name, build_number, failure_info, signals, |
| 311 heuristic_result, force_try_job=False): | 308 heuristic_result, force_try_job=False): |
| 312 | 309 |
| 313 tryserver_mastername, tryserver_buildername = ( | 310 tryserver_mastername, tryserver_buildername = ( |
| 314 waterfall_config.GetTrybotForWaterfallBuilder(master_name, builder_name)) | 311 waterfall_config.GetTrybotForWaterfallBuilder(master_name, builder_name)) |
| 315 | 312 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 330 need_new_try_job = _NeedANewCompileTryJob( | 327 need_new_try_job = _NeedANewCompileTryJob( |
| 331 master_name, builder_name, build_number, failure_info) | 328 master_name, builder_name, build_number, failure_info) |
| 332 else: | 329 else: |
| 333 need_new_try_job = _NeedANewTestTryJob( | 330 need_new_try_job = _NeedANewTestTryJob( |
| 334 master_name, builder_name, build_number, failure_info, force_try_job) | 331 master_name, builder_name, build_number, failure_info, force_try_job) |
| 335 | 332 |
| 336 # TODO(chanli): enable the feature to trigger single try job for a group | 333 # TODO(chanli): enable the feature to trigger single try job for a group |
| 337 # when notification is ready. | 334 # when notification is ready. |
| 338 # We still call _IsBuildFailureUniqueAcrossPlatforms just so we have data for | 335 # We still call _IsBuildFailureUniqueAcrossPlatforms just so we have data for |
| 339 # failure groups. | 336 # failure groups. |
| 337 |
| 338 # TODO(chanli): Add checking for culprits of the group when enabling |
| 339 # single try job: add current build to suspected_cl.builds if the try job for |
| 340 # this group has already completed. |
| 340 if need_new_try_job: | 341 if need_new_try_job: |
| 341 _IsBuildFailureUniqueAcrossPlatforms( | 342 _IsBuildFailureUniqueAcrossPlatforms( |
| 342 master_name, builder_name, build_number, try_job_type, | 343 master_name, builder_name, build_number, try_job_type, |
| 343 failure_info['builds'][str(build_number)]['blame_list'], | 344 failure_info['builds'][str(build_number)]['blame_list'], |
| 344 failure_info['failed_steps'], signals, heuristic_result) | 345 failure_info['failed_steps'], signals, heuristic_result) |
| 345 | 346 |
| 346 need_new_try_job = need_new_try_job and ReviveOrCreateTryJobEntity( | 347 need_new_try_job = need_new_try_job and ReviveOrCreateTryJobEntity( |
| 347 master_name, builder_name, build_number, force_try_job) | 348 master_name, builder_name, build_number, force_try_job) |
| 348 return need_new_try_job | 349 return need_new_try_job |
| 349 | 350 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 361 master_name, builder_name) | 362 master_name, builder_name) |
| 362 for source_target in signals['compile'].get('failed_targets', []): | 363 for source_target in signals['compile'].get('failed_targets', []): |
| 363 # For link failures, we pass the executable targets directly to try-job, and | 364 # For link failures, we pass the executable targets directly to try-job, and |
| 364 # there is no 'source' for link failures. | 365 # there is no 'source' for link failures. |
| 365 # For compile failures, only pass the object files as the compile targets | 366 # For compile failures, only pass the object files as the compile targets |
| 366 # for the bots that we use strict regex to extract such information. | 367 # for the bots that we use strict regex to extract such information. |
| 367 if not source_target.get('source') or strict_regex: | 368 if not source_target.get('source') or strict_regex: |
| 368 compile_targets.append(source_target.get('target')) | 369 compile_targets.append(source_target.get('target')) |
| 369 | 370 |
| 370 return compile_targets | 371 return compile_targets |
| OLD | NEW |