Chromium Code Reviews| 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 import copy |
| 7 from datetime import datetime | 7 from datetime import datetime |
| 8 import logging | 8 import logging |
| 9 import os | 9 import os |
| 10 | 10 |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 'last_pass': test_result.get('last_pass'), | 140 'last_pass': test_result.get('last_pass'), |
| 141 'supported': supported, | 141 'supported': supported, |
| 142 'tests': group['tests'], | 142 'tests': group['tests'], |
| 143 'suspected_cls': group['suspected_cls'] | 143 'suspected_cls': group['suspected_cls'] |
| 144 } | 144 } |
| 145 organized_suspected_cls.append(shared_result) | 145 organized_suspected_cls.append(shared_result) |
| 146 | 146 |
| 147 return organized_results | 147 return organized_results |
| 148 | 148 |
| 149 | 149 |
| 150 def _GetAnalysisResultWithTryJobInfo( | 150 def _GetAnalysisResultWithTryJobInfo(show_debug_info, organized_results, |
| 151 organized_results, master_name, builder_name, build_number): | 151 master_name, builder_name, build_number): |
| 152 """Reorganizes analysis result and try job result by step_name and culprit. | 152 """Reorganizes analysis result and try job result by step_name and culprit. |
| 153 | 153 |
| 154 Returns: | 154 Returns: |
| 155 update_result (dict): A dict of classified results. | 155 update_result (dict): A dict of classified results. |
| 156 | 156 |
| 157 The format for those dicts are as below: | 157 The format for those dicts are as below: |
| 158 { | 158 { |
| 159 # A dict of results that contains both | 159 # A dict of results that contains both |
| 160 # heuristic analysis results and try job results. | 160 # heuristic analysis results and try job results. |
| 161 'reliable_failures': { | 161 'reliable_failures': { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 # such as non-swarming tests or swarming rerun failed. | 198 # such as non-swarming tests or swarming rerun failed. |
| 199 'unclassified_failures': {...} | 199 'unclassified_failures': {...} |
| 200 } | 200 } |
| 201 """ | 201 """ |
| 202 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) | 202 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) |
| 203 | 203 |
| 204 if not organized_results: | 204 if not organized_results: |
| 205 return updated_results | 205 return updated_results |
| 206 | 206 |
| 207 try_job_info = handlers_util.GetAllTryJobResults( | 207 try_job_info = handlers_util.GetAllTryJobResults( |
| 208 master_name, builder_name, build_number) | 208 master_name, builder_name, build_number, show_debug_info) |
| 209 | |
| 209 if not try_job_info: | 210 if not try_job_info: |
| 210 return updated_results | 211 return updated_results |
| 211 | 212 |
| 212 for step_name, try_jobs in try_job_info.iteritems(): | 213 for step_name, try_jobs in try_job_info.iteritems(): |
| 213 try_jobs = try_jobs['try_jobs'] | 214 try_jobs = try_jobs['try_jobs'] |
| 214 step_heuristic_results = organized_results[step_name] | 215 step_heuristic_results = organized_results[step_name] |
| 215 step_updated_results = updated_results[step_name]['results'] | 216 step_updated_results = updated_results[step_name]['results'] |
| 216 | 217 |
| 217 # Finds out try job result index and heuristic result index for each test. | 218 # Finds out try job result index and heuristic result index for each test. |
| 218 test_result_map = defaultdict(lambda: defaultdict(int)) | 219 test_result_map = defaultdict(lambda: defaultdict(int)) |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 246 'try_job': try_job_result, | 247 'try_job': try_job_result, |
| 247 'heuristic_analysis': { | 248 'heuristic_analysis': { |
| 248 'suspected_cls': heuristic_result['suspected_cls'] | 249 'suspected_cls': heuristic_result['suspected_cls'] |
| 249 }, | 250 }, |
| 250 'tests': tests if tests != [NON_SWARMING] else [], | 251 'tests': tests if tests != [NON_SWARMING] else [], |
| 251 'first_failure': heuristic_result['first_failure'], | 252 'first_failure': heuristic_result['first_failure'], |
| 252 'last_pass': heuristic_result['last_pass'], | 253 'last_pass': heuristic_result['last_pass'], |
| 253 'supported': heuristic_result['supported'] | 254 'supported': heuristic_result['supported'] |
| 254 } | 255 } |
| 255 | 256 |
| 256 if ('status' not in try_job_result or | 257 if (('status' not in try_job_result or |
| 257 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()): | 258 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()) or |
| 259 (tests == [NON_SWARMING] and show_debug_info)): | |
| 258 # There is no try job info but only heuristic result. | 260 # There is no try job info but only heuristic result. |
| 259 try_job_result['status'] = try_job_result.get( | 261 try_job_result['status'] = try_job_result.get( |
| 260 'status', result_status.UNKNOWN) | 262 'status', result_status.UNKNOWN) |
| 261 step_updated_results['unclassified_failures'].append(final_result) | 263 step_updated_results['unclassified_failures'].append(final_result) |
| 262 elif try_job_result['status'] == result_status.FLAKY: | 264 elif try_job_result['status'] == result_status.FLAKY: |
| 263 step_updated_results['flaky_failures'].append(final_result) | 265 step_updated_results['flaky_failures'].append(final_result) |
| 264 else: | 266 else: |
| 265 step_updated_results['reliable_failures'].append(final_result) | 267 step_updated_results['reliable_failures'].append(final_result) |
| 266 | 268 |
| 267 return updated_results | 269 return updated_results |
| 268 | 270 |
| 269 | 271 |
| 270 class BuildFailure(BaseHandler): | 272 class BuildFailure(BaseHandler): |
| 271 PERMISSION_LEVEL = Permission.ANYONE | 273 PERMISSION_LEVEL = Permission.ANYONE |
| 272 | 274 |
| 273 def _ShowDebugInfo(self): | 275 def _ShowDebugInfo(self): |
| 274 # Show debug info only if the app is run locally during development, if the | 276 # Show debug info only if the app is run locally during development, if the |
| 275 # currently logged-in user is an admin, or if it is explicitly requested | 277 # currently logged-in user is an admin, or if it is explicitly requested |
| 276 # with parameter 'debug=1'. | 278 # with parameter 'debug=1'. |
| 277 return ( | 279 return users.is_current_user_admin() or self.request.get('debug') == '1' |
| 278 users.is_current_user_admin() or self.request.get('debug') == '1') | |
| 279 | 280 |
| 280 def _ShowTriageHelpButton(self): | 281 def _ShowTriageHelpButton(self): |
| 281 return users.is_current_user_admin() | 282 return users.is_current_user_admin() |
| 282 | 283 |
| 283 def _PrepareCommonDataForFailure(self, analysis): | 284 def _PrepareCommonDataForFailure(self, analysis): |
| 284 return { | 285 return { |
| 285 'master_name': analysis.master_name, | 286 'master_name': analysis.master_name, |
| 286 'builder_name': analysis.builder_name, | 287 'builder_name': analysis.builder_name, |
| 287 'build_number': analysis.build_number, | 288 'build_number': analysis.build_number, |
| 288 'pipeline_status_path': analysis.pipeline_status_path, | 289 'pipeline_status_path': analysis.pipeline_status_path, |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 307 return try_job_data # pragma: no cover. | 308 return try_job_data # pragma: no cover. |
| 308 | 309 |
| 309 referred_build_keys = analysis.failure_result_map[ | 310 referred_build_keys = analysis.failure_result_map[ |
| 310 constants.COMPILE_STEP_NAME].split('/') | 311 constants.COMPILE_STEP_NAME].split('/') |
| 311 try_job = WfTryJob.Get(*referred_build_keys) | 312 try_job = WfTryJob.Get(*referred_build_keys) |
| 312 if not try_job or not try_job.compile_results: | 313 if not try_job or not try_job.compile_results: |
| 313 return try_job_data # pragma: no cover. | 314 return try_job_data # pragma: no cover. |
| 314 result = try_job.compile_results[-1] | 315 result = try_job.compile_results[-1] |
| 315 | 316 |
| 316 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get( | 317 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get( |
| 317 try_job.status, 'unknown').lower() | 318 try_job.status, 'unknown').lower() |
| 318 try_job_data['url'] = result.get('url') | 319 try_job_data['url'] = result.get('url') |
| 319 try_job_data['completed'] = try_job.completed | 320 try_job_data['completed'] = try_job.completed |
| 320 try_job_data['failed'] = try_job.failed | 321 try_job_data['failed'] = try_job.failed |
| 321 try_job_data['culprit'] = result.get( | 322 try_job_data['culprit'] = result.get( |
| 322 'culprit', {}).get(constants.COMPILE_STEP_NAME) | 323 'culprit', {}).get(constants.COMPILE_STEP_NAME) |
| 323 | 324 |
| 324 return try_job_data | 325 return try_job_data |
| 325 | 326 |
| 326 @staticmethod | 327 @staticmethod |
| 327 def _PopulateHeuristicDataForCompileFailure(analysis, data): | 328 def _PopulateHeuristicDataForCompileFailure(analysis, data): |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 338 def _PrepareDataForCompileFailure(self, analysis): | 339 def _PrepareDataForCompileFailure(self, analysis): |
| 339 data = self._PrepareCommonDataForFailure(analysis) | 340 data = self._PrepareCommonDataForFailure(analysis) |
| 340 | 341 |
| 341 # Check result from heuristic analysis. | 342 # Check result from heuristic analysis. |
| 342 self._PopulateHeuristicDataForCompileFailure(analysis, data) | 343 self._PopulateHeuristicDataForCompileFailure(analysis, data) |
| 343 # Check result from try job. | 344 # Check result from try job. |
| 344 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis) | 345 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis) |
| 345 | 346 |
| 346 return data | 347 return data |
| 347 | 348 |
| 348 def _PrepareDataForTestFailures(self, analysis, build_info): | 349 def _PrepareDataForTestFailures(self, analysis, build_info, |
| 350 show_debug_info=False): | |
| 349 data = self._PrepareCommonDataForFailure(analysis) | 351 data = self._PrepareCommonDataForFailure(analysis) |
| 350 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP | 352 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP |
| 351 | 353 |
| 352 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( | 354 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( |
| 353 analysis.result) | 355 analysis.result) |
| 354 analysis_result = _GetAnalysisResultWithTryJobInfo( | 356 analysis_result = _GetAnalysisResultWithTryJobInfo( |
| 355 organized_results, *build_info) | 357 show_debug_info, organized_results, *build_info) |
| 356 data['analysis_result'] = analysis_result | 358 data['analysis_result'] = analysis_result |
| 357 | 359 |
| 358 return data | 360 return data |
| 359 | 361 |
| 360 def HandleGet(self): | 362 def HandleGet(self): |
| 361 """Triggers analysis of a build failure on demand and return current result. | 363 """Triggers analysis of a build failure on demand and return current result. |
| 362 | 364 |
| 363 If the final analysis result is available, set cache-control to 1 day to | 365 If the final analysis result is available, set cache-control to 1 day to |
| 364 avoid overload by unnecessary and frequent query from clients; otherwise | 366 avoid overload by unnecessary and frequent query from clients; otherwise |
| 365 set cache-control to 5 seconds to allow repeated query. | 367 set cache-control to 5 seconds to allow repeated query. |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 381 if not analysis: | 383 if not analysis: |
| 382 return BaseHandler.CreateError( | 384 return BaseHandler.CreateError( |
| 383 'Master "%s" is not supported yet.' % master_name, 501) | 385 'Master "%s" is not supported yet.' % master_name, 501) |
| 384 | 386 |
| 385 if not analysis: | 387 if not analysis: |
| 386 # Only allow admin to force a re-run and set the build_completed. | 388 # Only allow admin to force a re-run and set the build_completed. |
| 387 force = (users.is_current_user_admin() and | 389 force = (users.is_current_user_admin() and |
| 388 self.request.get('force') == '1') | 390 self.request.get('force') == '1') |
| 389 build_completed = (users.is_current_user_admin() and | 391 build_completed = (users.is_current_user_admin() and |
| 390 self.request.get('build_completed') == '1') | 392 self.request.get('build_completed') == '1') |
| 393 force_try_job_rerun = (users.is_current_user_admin() and | |
| 394 self.request.get('force_try_job_rerun') == '1') | |
|
stgao
2016/06/02 01:25:13
nit: just "force_try_job"?
lijeffrey
2016/06/02 05:36:06
Done.
| |
| 391 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( | 395 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( |
| 392 master_name, builder_name, build_number, | 396 master_name, builder_name, build_number, |
| 393 build_completed=build_completed, force=force, | 397 build_completed=build_completed, force=force, |
| 398 force_try_job_rerun=force_try_job_rerun, | |
| 394 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) | 399 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) |
| 395 | 400 |
| 396 if analysis.failure_type == failure_type.COMPILE: | 401 if analysis.failure_type == failure_type.COMPILE: |
| 397 return { | 402 return { |
| 398 'template': 'waterfall/compile_failure.html', | 403 'template': 'waterfall/compile_failure.html', |
| 399 'data': self._PrepareDataForCompileFailure(analysis), | 404 'data': self._PrepareDataForCompileFailure(analysis), |
| 400 } | 405 } |
| 401 else: | 406 else: |
| 402 return { | 407 return { |
| 403 'template': 'build_failure.html', | 408 'template': 'build_failure.html', |
| 404 'data': self._PrepareDataForTestFailures(analysis, build_info), | 409 'data': self._PrepareDataForTestFailures(analysis, build_info, |
| 410 self._ShowDebugInfo()), | |
| 405 } | 411 } |
| 406 | 412 |
| 407 def HandlePost(self): # pragma: no cover | 413 def HandlePost(self): # pragma: no cover |
| 408 return self.HandleGet() | 414 return self.HandleGet() |
| OLD | NEW |