Ejemplo n.º 1
0
def _ValidateCurrentPlatformIsSupported():
    """Asserts that this script suports running on the current platform"""
    target_os = _GetTargetOS()
    if target_os:
        current_platform = target_os
    else:
        current_platform = coverage_utils.GetHostPlatform()

    assert current_platform in [
        'linux', 'mac', 'chromeos', 'ios', 'win'
    ], ('Coverage is only supported on linux, mac, chromeos, ios and win.')
Ejemplo n.º 2
0
def _ConfigureLLVMCoverageTools(args):
  """Configures llvm coverage tools."""
  if args.coverage_tools_dir:
    llvm_bin_dir = coverage_utils.GetFullPath(args.coverage_tools_dir)
    global LLVM_COV_PATH
    global LLVM_PROFDATA_PATH
    LLVM_COV_PATH = os.path.join(llvm_bin_dir, 'llvm-cov')
    LLVM_PROFDATA_PATH = os.path.join(llvm_bin_dir, 'llvm-profdata')
  else:
    update.UpdatePackage('coverage_tools', coverage_utils.GetHostPlatform())

  if coverage_utils.GetHostPlatform() == 'win':
    LLVM_COV_PATH += '.exe'
    LLVM_PROFDATA_PATH += '.exe'

  coverage_tools_exist = (
      os.path.exists(LLVM_COV_PATH) and os.path.exists(LLVM_PROFDATA_PATH))
  assert coverage_tools_exist, ('Cannot find coverage tools, please make sure '
                                'both \'%s\' and \'%s\' exist.') % (
                                    LLVM_COV_PATH, LLVM_PROFDATA_PATH)
Ejemplo n.º 3
0
def _GetBinaryPathForWebTests():
    """Return binary path used to run blink web tests."""
    host_platform = coverage_utils.GetHostPlatform()
    if host_platform == 'win':
        return os.path.join(BUILD_DIR, 'content_shell.exe')
    elif host_platform == 'linux':
        return os.path.join(BUILD_DIR, 'content_shell')
    elif host_platform == 'mac':
        return os.path.join(BUILD_DIR, 'Content Shell.app', 'Contents',
                            'MacOS', 'Content Shell')
    else:
        assert False, 'This platform is not supported for web tests.'
Ejemplo n.º 4
0
def _ValidateCurrentPlatformIsSupported():
    """Asserts that this script suports running on the current platform"""
    target_os = _GetTargetOS()
    if target_os:
        current_platform = target_os
    else:
        current_platform = coverage_utils.GetHostPlatform()

    supported_platforms = ['android', 'chromeos', 'ios', 'linux', 'mac', 'win']
    assert current_platform in supported_platforms, ('Coverage is only'
                                                     'supported on %s' %
                                                     supported_platforms)
Ejemplo n.º 5
0
    def setUp(self):
        self.maxDiff = 1000
        self.COVERAGE_TOOLS_DIR = os.path.abspath(os.path.dirname(__file__))
        self.COVERAGE_SCRIPT = os.path.join(self.COVERAGE_TOOLS_DIR,
                                            'coverage.py')
        self.COVERAGE_UTILS = os.path.join(self.COVERAGE_TOOLS_DIR,
                                           'coverage_utils.py')

        self.CHROMIUM_SRC_DIR = os.path.dirname(
            os.path.dirname(self.COVERAGE_TOOLS_DIR))
        self.BUILD_DIR = os.path.join(self.CHROMIUM_SRC_DIR, 'out',
                                      'code_coverage_tools_test')

        self.REPORT_DIR_1 = os.path.join(self.BUILD_DIR, 'report1')
        self.REPORT_DIR_1_NO_COMPONENTS = os.path.join(
            self.BUILD_DIR, 'report1_no_components')
        self.REPORT_DIR_2 = os.path.join(self.BUILD_DIR, 'report2')
        self.REPORT_DIR_3 = os.path.join(self.BUILD_DIR, 'report3')
        self.REPORT_DIR_4 = os.path.join(self.BUILD_DIR, 'report4')

        self.LLVM_COV = os.path.join(self.CHROMIUM_SRC_DIR, 'third_party',
                                     'llvm-build', 'Release+Asserts', 'bin',
                                     'llvm-cov')

        self.PYTHON = 'python'
        self.PLATFORM = coverage_utils.GetHostPlatform()
        if self.PLATFORM == 'win32':
            self.LLVM_COV += '.exe'
            self.PYTHON += '.exe'

        # Even though 'is_component_build=false' is recommended, we intentionally
        # use 'is_component_build=true' to test handling of shared libraries.
        self.GN_ARGS = """use_clang_coverage=true
                      dcheck_always_on=true
                      ffmpeg_branding=\"ChromeOS\"
                      is_component_build=true
                      is_debug=false
                      proprietary_codecs=true
                      use_libfuzzer=true"""

        shutil.rmtree(self.BUILD_DIR, ignore_errors=True)

        gn_gen_cmd = ['gn', 'gen', self.BUILD_DIR, '--args=%s' % self.GN_ARGS]
        self.run_cmd(gn_gen_cmd)

        build_cmd = [
            'autoninja', '-C', self.BUILD_DIR, 'crypto_unittests',
            'libpng_read_fuzzer'
        ]
        self.run_cmd(build_cmd)
Ejemplo n.º 6
0
def _GeneratePerFileLineByLineCoverageInFormat(binary_paths,
                                               profdata_file_path, filters,
                                               ignore_filename_regex,
                                               output_format):
    """Generates per file line-by-line coverage in html or text using
  'llvm-cov show'.

  For a file with absolute path /a/b/x.cc, a html/txt report is generated as:
  OUTPUT_DIR/coverage/a/b/x.cc.[html|txt]. For html format, an index html file
  is also generated as: OUTPUT_DIR/index.html.

  Args:
    binary_paths: A list of paths to the instrumented binaries.
    profdata_file_path: A path to the profdata file.
    filters: A list of directories and files to get coverage for.
    ignore_filename_regex: A regular expression for skipping source code files
                           with certain file paths.
    output_format: The output format of generated report files.
  """
    # llvm-cov show [options] -instr-profile PROFILE BIN [-object BIN,...]
    # [[-object BIN]] [SOURCES]
    # NOTE: For object files, the first one is specified as a positional argument,
    # and the rest are specified as keyword argument.
    logging.debug('Generating per file line by line coverage reports using '
                  '"llvm-cov show" command.')

    subprocess_cmd = [
        LLVM_COV_PATH, 'show', '-format={}'.format(output_format),
        '-compilation-dir={}'.format(BUILD_DIR),
        '-output-dir={}'.format(OUTPUT_DIR),
        '-instr-profile={}'.format(profdata_file_path), binary_paths[0]
    ]
    subprocess_cmd.extend(
        ['-object=' + binary_path for binary_path in binary_paths[1:]])
    _AddArchArgumentForIOSIfNeeded(subprocess_cmd, len(binary_paths))
    if coverage_utils.GetHostPlatform() in ['linux', 'mac']:
        subprocess_cmd.extend(['-Xdemangler', 'c++filt', '-Xdemangler', '-n'])
    subprocess_cmd.extend(filters)
    if ignore_filename_regex:
        subprocess_cmd.append('-ignore-filename-regex=%s' %
                              ignore_filename_regex)

    subprocess.check_call(subprocess_cmd)

    logging.debug('Finished running "llvm-cov show" command.')
Ejemplo n.º 7
0
def _GetBinaryPathsFromTargets(targets, build_dir):
    """Return binary paths from target names."""
    # FIXME: Derive output binary from target build definitions rather than
    # assuming that it is always the same name.
    binary_paths = []
    for target in targets:
        binary_path = os.path.join(build_dir, target)
        if coverage_utils.GetHostPlatform() == 'win':
            binary_path += '.exe'

        if os.path.exists(binary_path):
            binary_paths.append(binary_path)
        else:
            logging.warning(
                'Target binary "%s" not found in build directory, skipping.',
                os.path.basename(binary_path))

    return binary_paths
Ejemplo n.º 8
0
def _GetBinaryPath(command):
    """Returns a relative path to the binary to be run by the command.

  Currently, following types of commands are supported (e.g. url_unittests):
  1. Run test binary direcly: "out/coverage/url_unittests <arguments>"
  2. Use xvfb.
    2.1. "python testing/xvfb.py out/coverage/url_unittests <arguments>"
    2.2. "testing/xvfb.py out/coverage/url_unittests <arguments>"
  3. Use iossim to run tests on iOS platform, please refer to testing/iossim.mm
    for its usage.
    3.1. "out/Coverage-iphonesimulator/iossim
          <iossim_arguments> -c <app_arguments>
          out/Coverage-iphonesimulator/url_unittests.app"

  Args:
    command: A command used to run a target.

  Returns:
    A relative path to the binary.
  """
    xvfb_script_name = os.extsep.join(['xvfb', 'py'])

    command_parts = _SplitCommand(command)
    if os.path.basename(command_parts[0]) == 'python':
        assert os.path.basename(command_parts[1]) == xvfb_script_name, (
            'This tool doesn\'t understand the command: "%s".' % command)
        return command_parts[2]

    if os.path.basename(command_parts[0]) == xvfb_script_name:
        return command_parts[1]

    if _IsIOSCommand(command):
        # For a given application bundle, the binary resides in the bundle and has
        # the same name with the application without the .app extension.
        app_path = command_parts[1].rstrip(os.path.sep)
        app_name = os.path.splitext(os.path.basename(app_path))[0]
        return os.path.join(app_path, app_name)

    if coverage_utils.GetHostPlatform() == 'win' \
       and not command_parts[0].endswith('.exe'):
        return command_parts[0] + '.exe'

    return command_parts[0]
Ejemplo n.º 9
0
def _BuildTargets(targets, jobs_count):
    """Builds target with Clang coverage instrumentation.

  This function requires current working directory to be the root of checkout.

  Args:
    targets: A list of targets to build with coverage instrumentation.
    jobs_count: Number of jobs to run in parallel for compilation. If None, a
                default value is derived based on CPUs availability.
  """
    logging.info('Building %s.', str(targets))
    autoninja = 'autoninja'
    if coverage_utils.GetHostPlatform() == 'win':
        autoninja += '.bat'

    subprocess_cmd = [autoninja, '-C', BUILD_DIR]
    if jobs_count is not None:
        subprocess_cmd.append('-j' + str(jobs_count))

    subprocess_cmd.extend(targets)
    subprocess.check_call(subprocess_cmd)
    logging.debug('Finished building %s.', str(targets))
Ejemplo n.º 10
0
def _ConfigureLLVMCoverageTools(args):
    """Configures llvm coverage tools."""
    if args.coverage_tools_dir:
        llvm_bin_dir = coverage_utils.GetFullPath(args.coverage_tools_dir)
        global LLVM_COV_PATH
        global LLVM_PROFDATA_PATH
        LLVM_COV_PATH = os.path.join(llvm_bin_dir, 'llvm-cov')
        LLVM_PROFDATA_PATH = os.path.join(llvm_bin_dir, 'llvm-profdata')
    else:
        subprocess.check_call([
            sys.executable, 'tools/clang/scripts/update.py', '--package',
            'coverage_tools'
        ])

    if coverage_utils.GetHostPlatform() == 'win':
        LLVM_COV_PATH += '.exe'
        LLVM_PROFDATA_PATH += '.exe'

    coverage_tools_exist = (os.path.exists(LLVM_COV_PATH)
                            and os.path.exists(LLVM_PROFDATA_PATH))
    assert coverage_tools_exist, (
        'Cannot find coverage tools, please make sure '
        'both \'%s\' and \'%s\' exist.') % (LLVM_COV_PATH, LLVM_PROFDATA_PATH)
Ejemplo n.º 11
0
def _SplitCommand(command):
    """Split a command string into parts in a platform-specific way."""
    if coverage_utils.GetHostPlatform() == 'win':
        return command.split()
    return shlex.split(command)
Ejemplo n.º 12
0
def DownloadCoverageToolsIfNeeded():
  """Temporary solution to download llvm-profdata and llvm-cov tools."""

  def _GetRevisionFromStampFile(stamp_file_path):
    """Returns a pair of revision number by reading the build stamp file.

    Args:
      stamp_file_path: A path the build stamp file created by
                       tools/clang/scripts/update.py.
    Returns:
      A pair of integers represeting the main and sub revision respectively.
    """
    if not os.path.exists(stamp_file_path):
      return 0, 0

    with open(stamp_file_path) as stamp_file:
      stamp_file_line = stamp_file.readline()
      if ',' in stamp_file_line:
        package_version = stamp_file_line.rstrip().split(',')[0]
      else:
        package_version = stamp_file_line.rstrip()

      clang_revision_str, clang_sub_revision_str = package_version.split('-')
      return int(clang_revision_str), int(clang_sub_revision_str)

  host_platform = coverage_utils.GetHostPlatform()
  clang_revision, clang_sub_revision = _GetRevisionFromStampFile(
      clang_update.STAMP_FILE)

  coverage_revision_stamp_file = os.path.join(
      os.path.dirname(clang_update.STAMP_FILE), 'cr_coverage_revision')
  coverage_revision, coverage_sub_revision = _GetRevisionFromStampFile(
      coverage_revision_stamp_file)

  has_coverage_tools = (
      os.path.exists(LLVM_COV_PATH) and os.path.exists(LLVM_PROFDATA_PATH))

  if (has_coverage_tools and coverage_revision == clang_revision and
      coverage_sub_revision == clang_sub_revision):
    # LLVM coverage tools are up to date, bail out.
    return

  package_version = '%d-%d' % (clang_revision, clang_sub_revision)
  coverage_tools_file = 'llvm-code-coverage-%s.tgz' % package_version

  # The code bellow follows the code from tools/clang/scripts/update.py.
  if host_platform == 'mac':
    coverage_tools_url = clang_update.CDS_URL + '/Mac/' + coverage_tools_file
  elif host_platform == 'linux':
    coverage_tools_url = (
        clang_update.CDS_URL + '/Linux_x64/' + coverage_tools_file)
  else:
    assert host_platform == 'win'
    coverage_tools_url = (clang_update.CDS_URL + '/Win/' + coverage_tools_file)

  try:
    clang_update.DownloadAndUnpack(coverage_tools_url,
                                   clang_update.LLVM_BUILD_DIR)
    with open(coverage_revision_stamp_file, 'w') as file_handle:
      file_handle.write('%s,%s' % (package_version, host_platform))
      file_handle.write('\n')
  except urllib2.URLError:
    raise Exception(
        'Failed to download coverage tools: %s.' % coverage_tools_url)
Ejemplo n.º 13
0
def DownloadCoverageToolsIfNeeded():
    """Temporary solution to download llvm-profdata and llvm-cov tools."""
    def _GetRevisionFromStampFile(stamp_file_path):
        """Returns revision by reading the build stamp file.

    Args:
      stamp_file_path: A path the build stamp file created by
                       tools/clang/scripts/update.py.
    Returns:
      A string represeting the revision of the tool, such as 361212-67510fac-2.
    """
        if not os.path.exists(stamp_file_path):
            return ''

        with open(stamp_file_path) as stamp_file:
            stamp_file_line = stamp_file.readline()
            if ',' in stamp_file_line:
                package_version = stamp_file_line.rstrip().split(',')[0]
            else:
                package_version = stamp_file_line.rstrip()

            return package_version

    cov_path = os.path.join(clang_update.LLVM_BUILD_DIR, 'llvm-cov')
    profdata_path = os.path.join(clang_update.LLVM_BUILD_DIR, 'llvm-profdata')

    host_platform = coverage_utils.GetHostPlatform()
    clang_revision = _GetRevisionFromStampFile(clang_update.STAMP_FILE)
    coverage_revision_stamp_file = os.path.join(
        os.path.dirname(clang_update.STAMP_FILE), 'cr_coverage_revision')
    coverage_revision = _GetRevisionFromStampFile(coverage_revision_stamp_file)

    has_coverage_tools = (os.path.exists(cov_path)
                          and os.path.exists(profdata_path))

    if (has_coverage_tools and clang_revision == coverage_revision):
        # LLVM coverage tools are up to date, bail out.
        return

    package_version = clang_revision
    coverage_tools_file = 'llvm-code-coverage-%s.tgz' % package_version

    # The code below follows the code from tools/clang/scripts/update.py.
    if host_platform == 'mac':
        coverage_tools_url = clang_update.CDS_URL + '/Mac/' + coverage_tools_file
    elif host_platform == 'linux':
        coverage_tools_url = (clang_update.CDS_URL + '/Linux_x64/' +
                              coverage_tools_file)
    else:
        assert host_platform == 'win'
        coverage_tools_url = (clang_update.CDS_URL + '/Win/' +
                              coverage_tools_file)

    try:
        clang_update.DownloadAndUnpack(coverage_tools_url,
                                       clang_update.LLVM_BUILD_DIR)
        with open(coverage_revision_stamp_file, 'w') as file_handle:
            file_handle.write('%s,%s' % (package_version, host_platform))
            file_handle.write('\n')
    except urllib2.URLError:
        raise Exception('Failed to download coverage tools: %s.' %
                        coverage_tools_url)
Ejemplo n.º 14
0
def Main():
  """Execute tool commands."""
  # Setup coverage binaries even when script is called with empty params. This
  # is used by coverage bot for initial setup.
  if len(sys.argv) == 1:
    update.UpdatePackage('coverage_tools', coverage_utils.GetHostPlatform())
    print(__doc__)
    return

  # Change directory to source root to aid in relative paths calculations.
  global SRC_ROOT_PATH
  SRC_ROOT_PATH = coverage_utils.GetFullPath(
      os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))
  os.chdir(SRC_ROOT_PATH)

  args = _ParseCommandArguments()
  coverage_utils.ConfigureLogging(verbose=args.verbose, log_file=args.log_file)
  _ConfigureLLVMCoverageTools(args)

  global BUILD_DIR
  BUILD_DIR = coverage_utils.GetFullPath(args.build_dir)

  global OUTPUT_DIR
  OUTPUT_DIR = coverage_utils.GetFullPath(args.output_dir)

  assert args.web_tests or args.command or args.profdata_file, (
      'Need to either provide commands to run using -c/--command option OR '
      'provide prof-data file as input using -p/--profdata-file option OR '
      'run web tests using -wt/--run-web-tests.')

  assert not args.command or (len(args.targets) == len(args.command)), (
      'Number of targets must be equal to the number of test commands.')

  assert os.path.exists(BUILD_DIR), (
      'Build directory: "%s" doesn\'t exist. '
      'Please run "gn gen" to generate.' % BUILD_DIR)

  _ValidateCurrentPlatformIsSupported()
  _ValidateBuildingWithClangCoverage()

  absolute_filter_paths = []
  if args.filters:
    absolute_filter_paths = _VerifyPathsAndReturnAbsolutes(args.filters)

  _SetupOutputDir()

  # Get .profdata file and list of binary paths.
  if args.web_tests:
    commands = [_GetCommandForWebTests(args.web_tests)]
    profdata_file_path = _CreateCoverageProfileDataForTargets(
        args.targets, commands, args.jobs)
    binary_paths = [_GetBinaryPathForWebTests()]
  elif args.command:
    for i in range(len(args.command)):
      assert not 'run_web_tests.py' in args.command[i], (
          'run_web_tests.py is not supported via --command argument. '
          'Please use --run-web-tests argument instead.')

    # A list of commands are provided. Run them to generate profdata file, and
    # create a list of binary paths from parsing commands.
    _VerifyTargetExecutablesAreInBuildDirectory(args.command)
    profdata_file_path = _CreateCoverageProfileDataForTargets(
        args.targets, args.command, args.jobs)
    binary_paths = [_GetBinaryPath(command) for command in args.command]
  else:
    # An input prof-data file is already provided. Just calculate binary paths.
    profdata_file_path = args.profdata_file
    binary_paths = _GetBinaryPathsFromTargets(args.targets, args.build_dir)

  # If the checkout uses the hermetic xcode binaries, then otool must be
  # directly invoked. The indirection via /usr/bin/otool won't work unless
  # there's an actual system install of Xcode.
  otool_path = None
  if sys.platform == 'darwin':
    hermetic_otool_path = os.path.join(
        SRC_ROOT_PATH, 'build', 'mac_files', 'xcode_binaries', 'Contents',
        'Developer', 'Toolchains', 'XcodeDefault.xctoolchain', 'usr', 'bin',
        'otool')
    if os.path.exists(hermetic_otool_path):
      otool_path = hermetic_otool_path
  if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
    binary_paths.extend(
        coverage_utils.GetSharedLibraries(binary_paths, BUILD_DIR, otool_path))

  assert args.format == 'html' or args.format == 'text', (
      '%s is not a valid output format for "llvm-cov show". Only "text" and '
      '"html" formats are supported.' % (args.format))
  logging.info('Generating code coverage report in %s (this can take a while '
               'depending on size of target!).' % (args.format))
  per_file_summary_data = _GeneratePerFileCoverageSummary(
      binary_paths, profdata_file_path, absolute_filter_paths,
      args.ignore_filename_regex)
  _GeneratePerFileLineByLineCoverageInFormat(
      binary_paths, profdata_file_path, absolute_filter_paths,
      args.ignore_filename_regex, args.format)
  component_mappings = None
  if not args.no_component_view:
    component_mappings = json.load(urllib2.urlopen(COMPONENT_MAPPING_URL))

  # Call prepare here.
  processor = coverage_utils.CoverageReportPostProcessor(
      OUTPUT_DIR,
      SRC_ROOT_PATH,
      per_file_summary_data,
      no_component_view=args.no_component_view,
      no_file_view=args.no_file_view,
      component_mappings=component_mappings)

  if args.format == 'html':
    processor.PrepareHtmlReport()