def testExtractSignal(self): class DummyGeneralExtractor(Extractor): def Extract(self, *_): return '0' class DummyExtractor1(Extractor): def Extract(self, *_): return '1' class DummyExtractor2(Extractor): def Extract(self, *_): return '2' DUMMY_EXTRACTORS = {'1': DummyExtractor1, '2': DummyExtractor2} self.mock(extractors, 'GeneralExtractor', DummyGeneralExtractor) self.mock(extractors, 'EXTRACTORS', DUMMY_EXTRACTORS) cases = { # step_name: result '1': '1', '2': '2', '32434': '0' } for step_name, expected_result in cases.iteritems(): result = extractors.ExtractSignal('master', 'bot', step_name, 'test', '') self.assertEqual(expected_result, result)
def ExtractSignalsForCompileFailure(failure_info, http_client): signals = {} master_name = failure_info.master_name builder_name = failure_info.builder_name build_number = failure_info.build_number step_name = 'compile' if step_name not in (failure_info.failed_steps or {}): logging.debug( 'No compile failure found when extracting signals for failed ' 'build %s/%s/%d', master_name, builder_name, build_number) return signals if not failure_info.failed_steps[step_name].supported: # Bail out if the step is not supported. logging.info('Findit could not analyze compile failure for master %s.', master_name) return signals failure_log = None # 1. Tries to get stored failure log from step. step = (WfStep.Get(master_name, builder_name, build_number, step_name) or WfStep.Create(master_name, builder_name, build_number, step_name)) if step.log_data: failure_log = step.log_data else: # 2. Tries to get ninja_output as failure log. from_ninja_output = False use_ninja_output_log = (waterfall_config.GetDownloadBuildDataSettings( ).get('use_ninja_output_log')) if use_ninja_output_log: failure_log = step_util.GetWaterfallBuildStepLog( master_name, builder_name, build_number, step_name, http_client, 'json.output[ninja_info]') from_ninja_output = True if not failure_log: # 3. Tries to get stdout log for compile step. from_ninja_output = False failure_log = extract_signal.GetStdoutLog(master_name, builder_name, build_number, step_name, http_client) try: if not failure_log: raise extract_signal.FailedToGetFailureLogError( 'Failed to pull failure log (stdio or ninja output) of step %s of' ' %s/%s/%d' % (step_name, master_name, builder_name, build_number)) except extract_signal.FailedToGetFailureLogError: return {} # Save step log in datastore and avoid downloading again during retry. step.log_data = extract_signal.ExtractStorablePortionOfLog( failure_log, from_ninja_output) try: step.put() except Exception as e: # pragma: no cover # Sometimes, the step log is too large to save in datastore. logging.exception(e) signals[step_name] = extractors.ExtractSignal( master_name, builder_name, step_name, test_name=None, failure_log=failure_log).ToDict() extract_signal.SaveSignalInAnalysis(master_name, builder_name, build_number, signals) return signals
def run(self, failure_info): """Extracts failure signals from failed steps. Args: failure_info (dict): Output of pipeline DetectFirstFailurePipeline.run(). Returns: A dict like below: { 'step_name1': waterfall.failure_signal.FailureSignal.ToDict(), ... } """ signals = {} if not failure_info['failed'] or not failure_info['chromium_revision']: # Bail out if no failed step or no chromium revision. return signals # Bail out on infra failure if failure_info.get('failure_type') == failure_type.INFRA: return signals master_name = failure_info['master_name'] builder_name = failure_info['builder_name'] build_number = failure_info['build_number'] for step_name in failure_info.get('failed_steps', []): if not waterfall_config.StepIsSupportedForMaster( step_name, master_name): # Bail out if the step is not supported. continue step = WfStep.Get(master_name, builder_name, build_number, step_name) if step and step.log_data: failure_log = step.log_data else: # TODO: do test-level analysis instead of step-level. # TODO: Use swarming test result instead of archived gtest results gtest_result = buildbot.GetGtestResultLog( master_name, builder_name, build_number, step_name) if gtest_result: failure_log = _GetReliableTestFailureLog(gtest_result) if gtest_result is None or failure_log == 'invalid': if not lock_util.WaitUntilDownloadAllowed( master_name): # pragma: no cover raise pipeline.Retry( 'Failed to pull log of step %s of master %s' % (step_name, master_name)) try: failure_log = buildbot.GetStepLog( master_name, builder_name, build_number, step_name, self.HTTP_CLIENT) except ResponseTooLargeError: # pragma: no cover. logging.exception( 'Log of step "%s" is too large for urlfetch.', step_name) # If the stdio log of a step is too large, we don't want to pull it # again in next run, because that might lead to DDoS to the master. # TODO: Use archived stdio logs in Google Storage instead. failure_log = 'Stdio log is too large for urlfetch.' if not failure_log: # pragma: no cover raise pipeline.Retry( 'Failed to pull stdio of step %s of master %s' % (step_name, master_name)) # Save step log in datastore and avoid downloading again during retry. if not step: # pragma: no cover step = WfStep.Create(master_name, builder_name, build_number, step_name) step.log_data = _ExtractStorablePortionOfLog(failure_log) try: step.put() except Exception as e: # pragma: no cover # Sometimes, the step log is too large to save in datastore. logging.exception(e) # TODO: save result in datastore? if step.isolated: try: json_failure_log = (json.loads(failure_log) if failure_log != 'flaky' else {}) except ValueError: # pragma: no cover json_failure_log = {} logging.warning('failure_log %s is not valid JSON.' % failure_log) signals[step_name] = {'tests': {}} step_signal = FailureSignal() for test_name, test_failure_log in json_failure_log.iteritems( ): signals[step_name]['tests'][ test_name] = extractors.ExtractSignal( master_name, builder_name, step_name, test_name, base64.b64decode(test_failure_log)).ToDict() # Save signals in test failure log to step level. step_signal.MergeFrom( signals[step_name]['tests'][test_name]) signals[step_name]['files'] = step_signal.files signals[step_name]['keywords'] = step_signal.keywords else: signals[step_name] = extractors.ExtractSignal( master_name, builder_name, step_name, None, failure_log).ToDict() return signals
def run(self, failure_info): """ Args: failure_info (dict): Output of pipeline DetectFirstFailurePipeline.run(). Returns: A dict like below: { 'step_name1': waterfall.failure_signal.FailureSignal.ToDict(), ... } """ signals = {} if not failure_info['failed'] or not failure_info['chromium_revision']: # Bail out if no failed step or no chromium revision. return signals master_name = failure_info['master_name'] builder_name = failure_info['builder_name'] build_number = failure_info['build_number'] for step_name in failure_info.get('failed_steps', []): step = WfStep.Get(master_name, builder_name, build_number, step_name) if step and step.log_data: failure_log = step.log_data else: # TODO: do test-level analysis instead of step-level. gtest_result = buildbot.GetGtestResultLog( master_name, builder_name, build_number, step_name) if gtest_result: failure_log = self._GetReliableTestFailureLog(gtest_result) if gtest_result is None or failure_log == 'invalid': if not lock_util.WaitUntilDownloadAllowed( master_name): # pragma: no cover raise pipeline.Retry( 'Failed to pull log of step %s of master %s' % (step_name, master_name)) try: failure_log = buildbot.GetStepStdio( master_name, builder_name, build_number, step_name, self.HTTP_CLIENT) except ResponseTooLargeError: # pragma: no cover. logging.exception( 'Log of step "%s" is too large for urlfetch.', step_name) # If the stdio log of a step is too large, we don't want to pull it # again in next run, because that might lead to DDoS to the master. # TODO: Use archived stdio logs in Google Storage instead. failure_log = 'Stdio log is too large for urlfetch.' if not failure_log: # pragma: no cover raise pipeline.Retry( 'Failed to pull stdio of step %s of master %s' % (step_name, master_name)) # Save step log in datastore and avoid downloading again during retry. if not step: # pragma: no cover step = WfStep.Create(master_name, builder_name, build_number, step_name) step.log_data = self._ExtractStorablePortionOfLog(failure_log) try: step.put() except Exception as e: # pragma: no cover # Sometimes, the step log is too large to save in datastore. logging.exception(e) # TODO: save result in datastore? signals[step_name] = extractors.ExtractSignal( master_name, builder_name, step_name, None, failure_log).ToDict() return signals
def ExtractSignalsForTestFailure(failure_info, http_client): signals = {} master_name = failure_info.master_name builder_name = failure_info.builder_name build_number = failure_info.build_number failed_steps = failure_info.failed_steps or {} for step_name in failed_steps: failure_log = None if not failed_steps[step_name].supported: # Bail out if the step is not supported. continue # 1. Tries to get stored failure log from step. step = (WfStep.Get(master_name, builder_name, build_number, step_name) or WfStep.Create(master_name, builder_name, build_number, step_name)) if step.log_data and step.log_data != constants.TOO_LARGE_LOG: failure_log = step.log_data else: json_formatted_log = True # 2. Gets test results. list_isolated_data = failed_steps[step_name].list_isolated_data list_isolated_data = (list_isolated_data.ToSerializable() if list_isolated_data else []) merged_test_results = ( swarmed_test_util.RetrieveShardedTestResultsFromIsolatedServer( list_isolated_data, http_client)) if merged_test_results: test_results = test_results_util.GetTestResultObject( merged_test_results) if test_results: failure_log, _ = ( test_results_service. GetFailedTestsInformationFromTestResult(test_results)) failure_log = json.dumps( failure_log ) if failure_log else constants.FLAKY_FAILURE_LOG else: failure_log = constants.WRONG_FORMAT_LOG if not merged_test_results or failure_log in [ constants.INVALID_FAILURE_LOG, constants.WRONG_FORMAT_LOG ]: # 3. Gets stdout log. json_formatted_log = False failure_log = extract_signal.GetStdoutLog( master_name, builder_name, build_number, step_name, http_client) try: if not failure_log: raise extract_signal.FailedToGetFailureLogError( 'Failed to pull failure log (stdio or ninja output) of step %s of' ' %s/%s/%d' % (step_name, master_name, builder_name, build_number)) except extract_signal.FailedToGetFailureLogError: return {} # Save step log in datastore and avoid downloading again during retry. step.log_data = extract_signal.ExtractStorablePortionOfLog( failure_log, json_formatted_log ) if step.log_data != constants.TOO_LARGE_LOG else step.log_data step.isolated = step.isolated or json_formatted_log try: step.put() except Exception as e: # pragma: no cover # Sometimes, the step log is too large to save in datastore. logging.exception(e) if step.isolated: try: json_failure_log = (json.loads(failure_log) if failure_log != constants.FLAKY_FAILURE_LOG else {}) except ValueError: json_failure_log = {} logging.warning('failure_log %s is not valid JSON.' % failure_log) signals[step_name] = {'tests': {}} step_signal = FailureSignal() for test_name, test_failure_log in json_failure_log.iteritems(): signals[step_name]['tests'][ test_name] = extractors.ExtractSignal( master_name, builder_name, step_name, test_name, base64.b64decode(test_failure_log)).ToDict() # Save signals in test failure log to step level. step_signal.MergeFrom(signals[step_name]['tests'][test_name]) signals[step_name]['files'] = step_signal.files else: signals[step_name] = extractors.ExtractSignal( master_name, builder_name, step_name, None, failure_log).ToDict() return signals