| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 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 copy |
| 6 from datetime import datetime | 7 from datetime import datetime |
| 7 | 8 |
| 8 from google.appengine.api import users | 9 from google.appengine.api import users |
| 9 | 10 |
| 10 from common import constants | 11 from common import constants |
| 11 from common.base_handler import BaseHandler | 12 from common.base_handler import BaseHandler |
| 12 from common.base_handler import Permission | 13 from common.base_handler import Permission |
| 13 from common.waterfall import failure_type | 14 from common.waterfall import failure_type |
| 14 from handlers import handlers_util | 15 from handlers import handlers_util |
| 15 from handlers import result_status | 16 from handlers import result_status |
| 16 from handlers.result_status import NO_TRY_JOB_REASON_MAP | 17 from handlers.result_status import NO_TRY_JOB_REASON_MAP |
| 17 from model import analysis_status | 18 from model import analysis_status |
| 19 from model import result_status as analysis_result_status |
| 20 from model import suspected_cl_status |
| 21 from model.base_build_model import BaseBuildModel |
| 22 from model.result_status import RESULT_STATUS_TO_DESCRIPTION |
| 23 from model.suspected_cl_status import CL_STATUS_TO_DESCRIPTION |
| 18 from model.wf_analysis import WfAnalysis | 24 from model.wf_analysis import WfAnalysis |
| 25 from model.wf_suspected_cl import WfSuspectedCL |
| 19 from model.wf_try_job import WfTryJob | 26 from model.wf_try_job import WfTryJob |
| 20 from model.result_status import FOUND_INCORRECT | |
| 21 from model.result_status import RESULT_STATUS_TO_DESCRIPTION | |
| 22 from waterfall import build_failure_analysis_pipelines | 27 from waterfall import build_failure_analysis_pipelines |
| 23 from waterfall import buildbot | 28 from waterfall import buildbot |
| 24 from waterfall import waterfall_config | 29 from waterfall import waterfall_config |
| 25 | 30 |
| 26 | 31 |
| 27 NON_SWARMING = object() | 32 NON_SWARMING = object() |
| 28 | 33 |
| 29 | 34 |
| 35 _ANALYSIS_CL_STATUS_MAP = { |
| 36 analysis_result_status.FOUND_CORRECT: suspected_cl_status.CORRECT, |
| 37 analysis_result_status.FOUND_INCORRECT: suspected_cl_status.INCORRECT |
| 38 } |
| 39 |
| 40 |
| 30 def _FormatDatetime(dt): | 41 def _FormatDatetime(dt): |
| 31 if not dt: | 42 if not dt: |
| 32 return None | 43 return None |
| 33 else: | 44 else: |
| 34 return dt.strftime('%Y-%m-%d %H:%M:%S UTC') | 45 return dt.strftime('%Y-%m-%d %H:%M:%S UTC') |
| 35 | 46 |
| 36 | 47 |
| 48 def _GetCLDict(analysis, cl_info): |
| 49 if not cl_info: |
| 50 return {} |
| 51 |
| 52 cl_keys = cl_info.split('/') |
| 53 repo_name = cl_keys[0] |
| 54 revision = cl_keys[1] |
| 55 for cl in analysis.suspected_cls: |
| 56 if cl['repo_name'] == repo_name and cl['revision'] == revision: |
| 57 return cl |
| 58 return {} |
| 59 |
| 60 |
| 37 def _GetTriageHistory(analysis): | 61 def _GetTriageHistory(analysis): |
| 38 if (not users.is_current_user_admin() or | 62 if (not users.is_current_user_admin() or |
| 39 not analysis.completed or | 63 not analysis.completed or |
| 40 not analysis.triage_history): | 64 not analysis.triage_history): |
| 41 return None | 65 return None |
| 42 | 66 |
| 43 triage_history = [] | 67 triage_history = [] |
| 44 for triage_record in analysis.triage_history: | 68 for triage_record in analysis.triage_history: |
| 45 cl_status = (FOUND_INCORRECT if triage_record.get('cl_status') == 1 | |
| 46 else triage_record.get('cl_status')) | |
| 47 status = triage_record.get('result_status', cl_status) | |
| 48 triage_history.append({ | 69 triage_history.append({ |
| 49 'triage_time': _FormatDatetime( | 70 'triage_time': _FormatDatetime( |
| 50 datetime.utcfromtimestamp(triage_record['triage_timestamp'])), | 71 datetime.utcfromtimestamp(triage_record['triage_timestamp'])), |
| 51 'user_name': triage_record['user_name'], | 72 'user_name': triage_record['user_name'], |
| 52 'result_status': RESULT_STATUS_TO_DESCRIPTION.get(status), | 73 'triaged_cl': _GetCLDict(analysis, triage_record.get('triaged_cl')), |
| 74 'result_status': ( |
| 75 RESULT_STATUS_TO_DESCRIPTION.get(triage_record.get('result_status')) |
| 76 or CL_STATUS_TO_DESCRIPTION.get(triage_record.get('cl_status'))), |
| 53 'version': triage_record.get('version'), | 77 'version': triage_record.get('version'), |
| 54 }) | 78 }) |
| 55 | 79 |
| 56 return triage_history | 80 return triage_history |
| 57 | 81 |
| 58 | 82 |
| 59 def _GetOrganizedAnalysisResultBySuspectedCL(analysis_result): | 83 def _GetOrganizedAnalysisResultBySuspectedCL(analysis_result): |
| 60 """Group tests it they have the same suspected CLs.""" | 84 """Group tests it they have the same suspected CLs.""" |
| 61 organized_results = defaultdict(list) | 85 organized_results = defaultdict(list) |
| 62 | 86 |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 compile_failure = None | 322 compile_failure = None |
| 299 for failure in analysis.result.get('failures', []): | 323 for failure in analysis.result.get('failures', []): |
| 300 if failure['step_name'] == constants.COMPILE_STEP_NAME: | 324 if failure['step_name'] == constants.COMPILE_STEP_NAME: |
| 301 compile_failure = failure | 325 compile_failure = failure |
| 302 if compile_failure: # pragma: no branch. | 326 if compile_failure: # pragma: no branch. |
| 303 data['first_failure'] = compile_failure['first_failure'] | 327 data['first_failure'] = compile_failure['first_failure'] |
| 304 data['last_pass'] = compile_failure['last_pass'] | 328 data['last_pass'] = compile_failure['last_pass'] |
| 305 data['suspected_cls_by_heuristic'] = compile_failure['suspected_cls'] | 329 data['suspected_cls_by_heuristic'] = compile_failure['suspected_cls'] |
| 306 | 330 |
| 307 | 331 |
| 332 def _GetAllSuspectedCLsAndCheckStatus( |
| 333 master_name, builder_name, build_number, analysis): |
| 334 build_key = BaseBuildModel.CreateBuildId( |
| 335 master_name, builder_name, build_number) |
| 336 suspected_cls = copy.deepcopy(analysis.suspected_cls) |
| 337 if not suspected_cls: |
| 338 return [] |
| 339 |
| 340 for cl in suspected_cls: |
| 341 cl['status'] = _ANALYSIS_CL_STATUS_MAP.get(analysis.result_status, None) |
| 342 suspected_cl = WfSuspectedCL.Get(cl['repo_name'], cl['revision']) |
| 343 if not suspected_cl: |
| 344 continue |
| 345 |
| 346 build = suspected_cl.builds.get(build_key) |
| 347 if build: |
| 348 cl['status'] = build['status'] |
| 349 |
| 350 return suspected_cls |
| 351 |
| 352 |
| 308 class BuildFailure(BaseHandler): | 353 class BuildFailure(BaseHandler): |
| 309 PERMISSION_LEVEL = Permission.ANYONE | 354 PERMISSION_LEVEL = Permission.ANYONE |
| 310 | 355 |
| 311 def _ShowDebugInfo(self): | 356 def _ShowDebugInfo(self): |
| 312 # Show debug info only if the app is run locally during development, if the | 357 # Show debug info only if the app is run locally during development, if the |
| 313 # currently logged-in user is an admin, or if it is explicitly requested | 358 # currently logged-in user is an admin, or if it is explicitly requested |
| 314 # with parameter 'debug=1'. | 359 # with parameter 'debug=1'. |
| 315 return users.is_current_user_admin() or self.request.get('debug') == '1' | 360 return users.is_current_user_admin() or self.request.get('debug') == '1' |
| 316 | 361 |
| 317 def _ShowTriageHelpButton(self): | 362 def _ShowTriageHelpButton(self): |
| (...skipping 18 matching lines...) Expand all Loading... |
| 336 'triage_history': _GetTriageHistory(analysis), | 381 'triage_history': _GetTriageHistory(analysis), |
| 337 'show_triage_help_button': self._ShowTriageHelpButton(), | 382 'show_triage_help_button': self._ShowTriageHelpButton(), |
| 338 'triage_reference_analysis_master_name': | 383 'triage_reference_analysis_master_name': |
| 339 analysis.triage_reference_analysis_master_name, | 384 analysis.triage_reference_analysis_master_name, |
| 340 'triage_reference_analysis_builder_name': | 385 'triage_reference_analysis_builder_name': |
| 341 analysis.triage_reference_analysis_builder_name, | 386 analysis.triage_reference_analysis_builder_name, |
| 342 'triage_reference_analysis_build_number': | 387 'triage_reference_analysis_build_number': |
| 343 analysis.triage_reference_analysis_build_number | 388 analysis.triage_reference_analysis_build_number |
| 344 } | 389 } |
| 345 | 390 |
| 346 def _PrepareDataForCompileFailure(self, analysis): | 391 def _PrepareDataForCompileFailure(self, analysis, data): |
| 347 data = self._PrepareCommonDataForFailure(analysis) | |
| 348 | 392 |
| 349 # Check result from heuristic analysis. | 393 # Check result from heuristic analysis. |
| 350 _PopulateHeuristicDataForCompileFailure(analysis, data) | 394 _PopulateHeuristicDataForCompileFailure(analysis, data) |
| 351 # Check result from try job. | 395 # Check result from try job. |
| 352 data['try_job'] = _PrepareTryJobDataForCompileFailure(analysis) | 396 data['try_job'] = _PrepareTryJobDataForCompileFailure(analysis) |
| 353 | 397 |
| 354 return data | 398 def _PrepareDataForTestFailures(self, analysis, build_info, data, |
| 399 show_debug_info=False): |
| 355 | 400 |
| 356 def _PrepareDataForTestFailures(self, analysis, build_info, | |
| 357 show_debug_info=False): | |
| 358 data = self._PrepareCommonDataForFailure(analysis) | |
| 359 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP | 401 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP |
| 360 | 402 |
| 361 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( | 403 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( |
| 362 analysis.result) | 404 analysis.result) |
| 363 analysis_result = _GetAnalysisResultWithTryJobInfo( | 405 analysis_result = _GetAnalysisResultWithTryJobInfo( |
| 364 show_debug_info, organized_results, *build_info) | 406 show_debug_info, organized_results, *build_info) |
| 365 | 407 |
| 366 data['analysis_result'] = analysis_result | 408 data['analysis_result'] = analysis_result |
| 367 return data | 409 return data |
| 368 | 410 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 398 build_completed = (users.is_current_user_admin() and | 440 build_completed = (users.is_current_user_admin() and |
| 399 self.request.get('build_completed') == '1') | 441 self.request.get('build_completed') == '1') |
| 400 force_try_job = (users.is_current_user_admin() and | 442 force_try_job = (users.is_current_user_admin() and |
| 401 self.request.get('force_try_job') == '1') | 443 self.request.get('force_try_job') == '1') |
| 402 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( | 444 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( |
| 403 master_name, builder_name, build_number, | 445 master_name, builder_name, build_number, |
| 404 build_completed=build_completed, force=force, | 446 build_completed=build_completed, force=force, |
| 405 force_try_job=force_try_job, | 447 force_try_job=force_try_job, |
| 406 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) | 448 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) |
| 407 | 449 |
| 450 data = self._PrepareCommonDataForFailure(analysis) |
| 451 data['suspected_cls'] = _GetAllSuspectedCLsAndCheckStatus( |
| 452 master_name, builder_name, build_number, analysis) |
| 453 |
| 408 if analysis.failure_type == failure_type.COMPILE: | 454 if analysis.failure_type == failure_type.COMPILE: |
| 455 self._PrepareDataForCompileFailure(analysis, data) |
| 409 return { | 456 return { |
| 410 'template': 'waterfall/compile_failure.html', | 457 'template': 'waterfall/compile_failure.html', |
| 411 'data': self._PrepareDataForCompileFailure(analysis), | 458 'data': data |
| 412 } | 459 } |
| 413 else: | 460 else: |
| 461 self._PrepareDataForTestFailures( |
| 462 analysis, build_info, data, self._ShowDebugInfo()) |
| 414 return { | 463 return { |
| 415 'template': 'waterfall/test_failure.html', | 464 'template': 'waterfall/test_failure.html', |
| 416 'data': self._PrepareDataForTestFailures(analysis, build_info, | 465 'data': data |
| 417 self._ShowDebugInfo()), | |
| 418 } | 466 } |
| 419 | 467 |
| 420 def HandlePost(self): # pragma: no cover | 468 def HandlePost(self): # pragma: no cover |
| 421 return self.HandleGet() | 469 return self.HandleGet() |
| OLD | NEW |