Ejemplo n.º 1
0
def beat(previous_state, log_filename):
    """Run a cycle of heartbeat checks to ensure bot is running."""
    # Handle case when run_bot.py script is stuck. If yes, kill its process.
    task_end_time = tasks.get_task_end_time()
    if psutil and task_end_time and dates.time_has_expired(
            task_end_time, seconds=tasks.TASK_COMPLETION_BUFFER):

        # Get absolute path to |run_bot| script. We use this to identify unique
        # instances of bot running on a particular host.
        startup_scripts_directory = environment.get_startup_scripts_directory()
        bot_file_path = os.path.join(startup_scripts_directory, 'run_bot')

        for process in psutil.process_iter():
            try:
                command_line = ' '.join(process.cmdline())
            except (psutil.AccessDenied, psutil.NoSuchProcess, OSError):
                continue

            # Find the process running the main bot script.
            if bot_file_path not in command_line:
                continue

            process_id = process.pid
            logs.log('Killing stale bot (pid %d) which seems to have stuck.' %
                     process_id)
            try:
                process_handler.terminate_root_and_child_processes(process_id)
            except Exception:
                logs.log_error('Failed to terminate stale bot processes.')

        # Minor cleanup to avoid disk space issues on bot restart.
        process_handler.terminate_stale_application_instances()
        shell.clear_temp_directory()
        shell.clear_testcase_directories()

        # Concerned stale processes should be killed. Now, delete the stale task.
        tasks.track_task_end()

    # Figure out when the log file was last modified.
    try:
        current_state = str(os.path.getmtime(log_filename))
    except Exception:
        current_state = None

    # Only update the heartbeat if the log file was modified.
    if current_state and current_state != previous_state:
        # Try updating the heartbeat. If an error occurs, just
        # wait and return None.
        if not data_handler.update_heartbeat():
            return None
        # Heartbeat is successfully updated.

    return current_state
Ejemplo n.º 2
0
def cleanup_task_state():
  """Cleans state before and after a task is executed."""
  # Cleanup stale processes.
  process_handler.cleanup_stale_processes()

  # Clear build urls, temp and testcase directories.
  shell.clear_build_urls_directory()
  shell.clear_crash_stacktraces_directory()
  shell.clear_testcase_directories()
  shell.clear_temp_directory()
  shell.clear_system_temp_directory()
  shell.clear_device_temp_directories()

  # Reset memory tool environment variables.
  environment.reset_current_memory_tool_options()

  # Call python's garbage collector.
  utils.python_gc()
    def test_clear_testcase_directories(self):
        """Test clearing test directories."""
        fuzz_inputs = os.environ['FUZZ_INPUTS']
        worker_fuzz_inputs = file_host.rebase_to_worker_root(fuzz_inputs)

        fuzz_inputs_disk = os.environ['FUZZ_INPUTS_DISK']
        worker_fuzz_inputs_disk = file_host.rebase_to_worker_root(
            fuzz_inputs_disk)

        with open(os.path.join(worker_fuzz_inputs, 'file'), 'w') as f:
            f.write('blah')

        with open(os.path.join(worker_fuzz_inputs_disk, 'file'), 'w') as f:
            f.write('blah2')

        shell.clear_testcase_directories()
        self.assertEqual(len(os.listdir(worker_fuzz_inputs)), 0)
        self.assertEqual(len(os.listdir(worker_fuzz_inputs_disk)), 0)
Ejemplo n.º 4
0
def setup_testcase(testcase, job_type, fuzzer_override=None):
    """Sets up the testcase and needed dependencies like fuzzer,
  data bundle, etc."""
    fuzzer_name = fuzzer_override or testcase.fuzzer_name
    task_name = environment.get_value('TASK_NAME')
    testcase_fail_wait = environment.get_value('FAIL_WAIT')
    testcase_id = testcase.key.id()

    # Clear testcase directories.
    shell.clear_testcase_directories()

    # Adjust the test timeout value if this is coming from an user uploaded
    # testcase.
    if testcase.uploader_email:
        _set_timeout_value_from_user_upload(testcase_id)

    # Update the fuzzer if necessary in order to get the updated data bundle.
    if fuzzer_name:
        try:
            update_successful = update_fuzzer_and_data_bundles(fuzzer_name)
        except errors.InvalidFuzzerError:
            # Close testcase and don't recreate tasks if this fuzzer is invalid.
            testcase.open = False
            testcase.fixed = 'NA'
            testcase.set_metadata('fuzzer_was_deleted', True)
            logs.log_error('Closed testcase %d with invalid fuzzer %s.' %
                           (testcase_id, fuzzer_name))

            error_message = 'Fuzzer %s no longer exists' % fuzzer_name
            data_handler.update_testcase_comment(testcase,
                                                 data_types.TaskState.ERROR,
                                                 error_message)
            return None, None, None

        if not update_successful:
            error_message = 'Unable to setup fuzzer %s' % fuzzer_name
            data_handler.update_testcase_comment(testcase,
                                                 data_types.TaskState.ERROR,
                                                 error_message)
            tasks.add_task(task_name,
                           testcase_id,
                           job_type,
                           wait_time=testcase_fail_wait)
            return None, None, None

    # Extract the testcase and any of its resources to the input directory.
    file_list, input_directory, testcase_file_path = unpack_testcase(testcase)
    if not file_list:
        error_message = 'Unable to setup testcase %s' % testcase_file_path
        data_handler.update_testcase_comment(testcase,
                                             data_types.TaskState.ERROR,
                                             error_message)
        tasks.add_task(task_name,
                       testcase_id,
                       job_type,
                       wait_time=testcase_fail_wait)
        return None, None, None

    # For Android/Fuchsia, we need to sync our local testcases directory with the
    # one on the device.
    if environment.is_android():
        _copy_testcase_to_device_and_setup_environment(testcase,
                                                       testcase_file_path)

    # Push testcases to worker.
    if environment.is_trusted_host():
        from clusterfuzz._internal.bot.untrusted_runner import file_host
        file_host.push_testcases_to_worker()

    # Copy global blacklist into local blacklist.
    is_lsan_enabled = environment.get_value('LSAN')
    if is_lsan_enabled:
        # Get local blacklist without this testcase's entry.
        leak_blacklist.copy_global_to_local_blacklist(
            excluded_testcase=testcase)

    prepare_environment_for_testcase(testcase, job_type, task_name)

    return file_list, input_directory, testcase_file_path
Ejemplo n.º 5
0
def execute_task(metadata_id, job_type):
    """Unpack a bundled testcase archive and create analyze jobs for each item."""
    metadata = ndb.Key(data_types.BundledArchiveMetadata,
                       int(metadata_id)).get()
    if not metadata:
        logs.log_error('Invalid bundle metadata id %s.' % metadata_id)
        return

    bot_name = environment.get_value('BOT_NAME')
    upload_metadata = data_types.TestcaseUploadMetadata.query(
        data_types.TestcaseUploadMetadata.blobstore_key ==
        metadata.blobstore_key).get()
    if not upload_metadata:
        logs.log_error('Invalid upload metadata key %s.' %
                       metadata.blobstore_key)
        return

    job = data_types.Job.query(data_types.Job.name == metadata.job_type).get()
    if not job:
        logs.log_error('Invalid job_type %s.' % metadata.job_type)
        return

    # Update the upload metadata with this bot name.
    upload_metadata.bot_name = bot_name
    upload_metadata.put()

    # We can't use FUZZ_INPUTS directory since it is constrained
    # by tmpfs limits.
    testcases_directory = environment.get_value('FUZZ_INPUTS_DISK')

    # Retrieve multi-testcase archive.
    archive_path = os.path.join(testcases_directory, metadata.archive_filename)
    if not blobs.read_blob_to_disk(metadata.blobstore_key, archive_path):
        logs.log_error('Could not retrieve archive for bundle %d.' %
                       metadata_id)
        tasks.add_task('unpack', metadata_id, job_type)
        return

    try:
        archive.unpack(archive_path, testcases_directory)
    except:
        logs.log_error('Could not unpack archive for bundle %d.' % metadata_id)
        tasks.add_task('unpack', metadata_id, job_type)
        return

    # Get additional testcase metadata (if any).
    additional_metadata = None
    if upload_metadata.additional_metadata_string:
        additional_metadata = json.loads(
            upload_metadata.additional_metadata_string)

    archive_state = data_types.ArchiveStatus.NONE
    bundled = True
    file_list = archive.get_file_list(archive_path)

    for file_path in file_list:
        absolute_file_path = os.path.join(testcases_directory, file_path)
        filename = os.path.basename(absolute_file_path)

        # Only files are actual testcases. Skip directories.
        if not os.path.isfile(absolute_file_path):
            continue

        try:
            file_handle = open(absolute_file_path, 'rb')
            blob_key = blobs.write_blob(file_handle)
            file_handle.close()
        except:
            blob_key = None

        if not blob_key:
            logs.log_error('Could not write testcase %s to blobstore.' %
                           absolute_file_path)
            continue

        data_handler.create_user_uploaded_testcase(
            blob_key, metadata.blobstore_key, archive_state,
            metadata.archive_filename, filename, metadata.timeout, job,
            metadata.job_queue, metadata.http_flag, metadata.gestures,
            metadata.additional_arguments, metadata.bug_information,
            metadata.crash_revision, metadata.uploader_email,
            metadata.platform_id, metadata.app_launch_command,
            metadata.fuzzer_name, metadata.overridden_fuzzer_name,
            metadata.fuzzer_binary_name, bundled, upload_metadata.retries,
            upload_metadata.bug_summary_update_flag,
            upload_metadata.quiet_flag, additional_metadata)

    # The upload metadata for the archive is not needed anymore since we created
    # one for each testcase.
    upload_metadata.key.delete()

    shell.clear_testcase_directories()