diff --git a/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py b/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py index c7fd67cd70..eabadd43a3 100644 --- a/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py +++ b/src/clusterfuzz/_internal/bot/tasks/utasks/fuzz_task.py @@ -2187,8 +2187,11 @@ def run(self): self.testcase_directory = environment.get_value('FUZZ_INPUTS') fuzz_target = self.fuzz_target.binary if self.fuzz_target else None - build_setup_result = build_manager.setup_build( - environment.get_value('APP_REVISION'), fuzz_target=fuzz_target) + with self.steps_time_tracker.track('build_setup'): + build_setup_result = build_manager.setup_build( + environment.get_value('APP_REVISION'), fuzz_target=fuzz_target) + if not build_setup_result: + raise monitor.TimeStatTrackerFailureException _add_build_metadata_to_output(self.fuzz_task_output) engine_impl = engine.get(self.fuzzer.name) diff --git a/src/clusterfuzz/_internal/metrics/fuzzer_stats.py b/src/clusterfuzz/_internal/metrics/fuzzer_stats.py index 0e7e4883c7..0c274c15e9 100644 --- a/src/clusterfuzz/_internal/metrics/fuzzer_stats.py +++ b/src/clusterfuzz/_internal/metrics/fuzzer_stats.py @@ -41,6 +41,11 @@ 'type': 'INTERVAL', 'mode': 'NULLABLE', }, + { + 'name': 'build_setup', + 'type': 'INTERVAL', + 'mode': 'NULLABLE', + }, ] JOB_RUN_SCHEMA = { 'fields': [ diff --git a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py index 715c247e5e..06690f8db2 100644 --- a/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py +++ b/src/clusterfuzz/_internal/tests/core/bot/tasks/utasks/fuzz_task_test.py @@ -1620,6 +1620,20 @@ def test_utask_main_blackbox_fuzzer(self, mock_generate_blackbox_testcases): self.assertEqual(1, dist.count) self.assertGreater(dist.sum, 0) + self.assertGreater( + actual.fuzz_task_output.steps_duration['build_setup'] + .ToTimedelta().total_seconds(), 0) + dist = monitoring_metrics.FUZZ_TASK_STEPS_DURATION.get({ + 'fuzzer': fuzzer.name, + 'job': 'asan_test', + 'platform': environment.platform(), + 'runtime': environment.get_runtime().value, + 'step': 'build_setup', + 'success': True, + }) + self.assertEqual(1, dist.count) + self.assertGreater(dist.sum, 0) + @mock.patch( 'clusterfuzz._internal.bot.tasks.utasks.fuzz_task.FuzzingSession.generate_blackbox_testcases' ) @@ -1667,6 +1681,46 @@ def test_utask_main_process_crashes_exception( **labels, 'success': True }).count) + def test_utask_main_build_setup_failure(self): + """Test that a failed build setup is tracked as a failed step.""" + self._setup_utask_env() + self.mock.setup_build.return_value = None + fuzzer = self._create_test_fuzzer() + + fuzz_task_input = uworker_msg_pb2.FuzzTaskInput() + setup_input = uworker_msg_pb2.SetupInput( + fuzzer_name=fuzzer.name, fuzzer=uworker_io.entity_to_protobuf(fuzzer)) + + uworker_input = uworker_msg_pb2.Input( + fuzzer_name=fuzzer.name, + job_type='asan_test', + fuzz_task_input=fuzz_task_input, + setup_input=setup_input, + uworker_env={'FUZZ_TEST_TIMEOUT': '10'}) + + actual = fuzz_task.utask_main(uworker_input) + + self.assertEqual(uworker_msg_pb2.ErrorType.FUZZ_BUILD_SETUP_FAILURE, + actual.error_type) + self.mock.check_for_bad_build.assert_not_called() + labels = { + 'fuzzer': fuzzer.name, + 'job': 'asan_test', + 'platform': environment.platform(), + 'runtime': environment.get_runtime().value, + 'step': 'build_setup', + } + failed_dist = monitoring_metrics.FUZZ_TASK_STEPS_DURATION.get({ + **labels, 'success': False + }) + self.assertEqual(1, failed_dist.count) + self.assertGreater(failed_dist.sum, 0) + self.assertEqual( + 0, + monitoring_metrics.FUZZ_TASK_STEPS_DURATION.get({ + **labels, 'success': True + }).count) + class DoBlackboxFuzzingDeferredLoggingTest(fake_filesystem_unittest.TestCase): """do_blackbox_fuzzing deferred log upload unit tests without datastore dependency."""