Beispiel #1
0
def _SetupOutputDir():
    """Setup output directory."""
    if os.path.exists(OUTPUT_DIR):
        shutil.rmtree(OUTPUT_DIR)

    # Creates |OUTPUT_DIR| and its platform sub-directory.
    os.makedirs(coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR))
Beispiel #2
0
def _ExecuteCommand(target, command, output_file_path):
    """Runs a single command and generates a profraw data file."""
    # Per Clang "Source-based Code Coverage" doc:
    #
    # "%p" expands out to the process ID. It's not used by this scripts due to:
    # 1) If a target program spawns too many processess, it may exhaust all disk
    #    space available. For example, unit_tests writes thousands of .profraw
    #    files each of size 1GB+.
    # 2) If a target binary uses shared libraries, coverage profile data for them
    #    will be missing, resulting in incomplete coverage reports.
    #
    # "%Nm" expands out to the instrumented binary's signature. When this pattern
    # is specified, the runtime creates a pool of N raw profiles which are used
    # for on-line profile merging. The runtime takes care of selecting a raw
    # profile from the pool, locking it, and updating it before the program exits.
    # N must be between 1 and 9. The merge pool specifier can only occur once per
    # filename pattern.
    #
    # "%1m" is used when tests run in single process, such as fuzz targets.
    #
    # For other cases, "%4m" is chosen as it creates some level of parallelism,
    # but it's not too big to consume too much computing resource or disk space.
    profile_pattern_string = '%1m' if _IsFuzzerTarget(target) else '%4m'
    expected_profraw_file_name = os.extsep.join(
        [target, profile_pattern_string, PROFRAW_FILE_EXTENSION])
    expected_profraw_file_path = os.path.join(
        coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR),
        expected_profraw_file_name)
    command = command.replace(LLVM_PROFILE_FILE_PATH_SUBSTITUTION,
                              expected_profraw_file_path)

    try:
        # Some fuzz targets or tests may write into stderr, redirect it as well.
        with open(output_file_path, 'wb') as output_file_handle:
            subprocess.check_call(
                shlex.split(command),
                stdout=output_file_handle,
                stderr=subprocess.STDOUT,
                env=_GetEnvironmentVars(expected_profraw_file_path))
    except subprocess.CalledProcessError as e:
        logging.warning('Command: "%s" exited with non-zero return code.',
                        command)

    return open(output_file_path, 'rb').read()
Beispiel #3
0
def _GetTargetProfDataPathsByExecutingCommands(targets, commands):
    """Runs commands and returns the relative paths to the profraw data files.

  Args:
    targets: A list of targets built with coverage instrumentation.
    commands: A list of commands used to run the targets.

  Returns:
    A list of relative paths to the generated profraw data files.
  """
    logging.debug('Executing the test commands.')

    # Remove existing profraw data files.
    report_root_dir = coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR)
    for file_or_dir in os.listdir(report_root_dir):
        if file_or_dir.endswith(PROFRAW_FILE_EXTENSION):
            os.remove(os.path.join(report_root_dir, file_or_dir))

    # Ensure that logs directory exists.
    if not os.path.exists(_GetLogsDirectoryPath()):
        os.makedirs(_GetLogsDirectoryPath())

    profdata_file_paths = []

    # Run all test targets to generate profraw data files.
    for target, command in zip(targets, commands):
        output_file_name = os.extsep.join([target + '_output', 'log'])
        output_file_path = os.path.join(_GetLogsDirectoryPath(),
                                        output_file_name)

        profdata_file_path = None
        for _ in xrange(MERGE_RETRIES):
            logging.info(
                'Running command: "%s", the output is redirected to "%s".',
                command, output_file_path)

            if _IsIOSCommand(command):
                # On iOS platform, due to lack of write permissions, profraw files are
                # generated outside of the OUTPUT_DIR, and the exact paths are contained
                # in the output of the command execution.
                output = _ExecuteIOSCommand(command, output_file_path)
            else:
                # On other platforms, profraw files are generated inside the OUTPUT_DIR.
                output = _ExecuteCommand(target, command, output_file_path)

            profraw_file_paths = []
            if _IsIOS():
                profraw_file_paths = [
                    _GetProfrawDataFileByParsingOutput(output)
                ]
            else:
                for file_or_dir in os.listdir(report_root_dir):
                    if file_or_dir.endswith(PROFRAW_FILE_EXTENSION):
                        profraw_file_paths.append(
                            os.path.join(report_root_dir, file_or_dir))

            assert profraw_file_paths, (
                'Running target "%s" failed to generate any profraw data file, '
                'please make sure the binary exists, is properly instrumented and '
                'does not crash. %s' % (target, FILE_BUG_MESSAGE))

            assert isinstance(profraw_file_paths, list), (
                'Variable \'profraw_file_paths\' is expected to be of type \'list\', '
                'but it is a %s. %s' %
                (type(profraw_file_paths), FILE_BUG_MESSAGE))

            try:
                profdata_file_path = _CreateTargetProfDataFileFromProfRawFiles(
                    target, profraw_file_paths)
                break
            except Exception:
                logging.info('Retrying...')
            finally:
                # Remove profraw files now so that they are not used in next iteration.
                for profraw_file_path in profraw_file_paths:
                    os.remove(profraw_file_path)

        assert profdata_file_path, (
            'Failed to merge target "%s" profraw files after %d retries. %s' %
            (target, MERGE_RETRIES, FILE_BUG_MESSAGE))
        profdata_file_paths.append(profdata_file_path)

    logging.debug('Finished executing the test commands.')

    return profdata_file_paths
Beispiel #4
0
def _GetSummaryFilePath():
    """The JSON file that contains coverage summary written by llvm-cov export."""
    return os.path.join(
        coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR),
        SUMMARY_FILE_NAME)
Beispiel #5
0
def _GetProfdataFilePath():
    """Path to the resulting .profdata file."""
    return os.path.join(
        coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR),
        PROFDATA_FILE_NAME)
Beispiel #6
0
def _GetLogsDirectoryPath():
    """Path to the logs directory."""
    return os.path.join(
        coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR), LOGS_DIR_NAME)
Beispiel #7
0
def _GetLcovFilePath():
    """The LCOV file that contains coverage data written by llvm-cov export."""
    return os.path.join(
        coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR),
        LCOV_FILE_NAME)