13b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch#!/usr/bin/env python
23b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Copyright 2016 the V8 project authors. All rights reserved.
33b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Use of this source code is governed by a BSD-style license that can be
43b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# found in the LICENSE file.
53b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
63b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch"""Script to transform and merge sancov files into human readable json-format.
73b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
83b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochThe script supports three actions:
93b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochall: Writes a json file with all instrumented lines of all executables.
103b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochmerge: Merges sancov files with coverage output into an existing json file.
113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochsplit: Split json file into separate files per covered source file.
123b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochThe json data is structured as follows:
143b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch{
153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  "version": 1,
163b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  "tests": ["executable1", "executable2", ...],
173b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  "files": {
183b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    "file1": [[<instr line 1>, <bit_mask>], [<instr line 2>, <bit_mask>], ...],
193b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    "file2": [...],
203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    ...
213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch}
233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochThe executables are sorted and determine the test bit mask. Their index+1 is
253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochthe bit, e.g. executable1 = 1, executable3 = 4, etc. Hence, a line covered by
263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochexecutable1 and executable3 will have bit_mask == 5 == 0b101. The number of
273b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochtests is restricted to 52 in version 1, to allow javascript JSON parsing of
283b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochthe bitsets encoded as numbers. JS max safe int is (1 << 53) - 1.
293b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
303b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochThe line-number-bit_mask pairs are sorted by line number and don't contain
313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochduplicates.
323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochSplit json data preserves the same format, but only contains one file per
343b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochjson file.
353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochThe sancov tool is expected to be in the llvm compiler-rt third-party
373b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdirectory. It's not checked out by default and must be added as a custom deps:
383b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch'v8/third_party/llvm/projects/compiler-rt':
393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'https://chromium.googlesource.com/external/llvm.org/compiler-rt.git'
403b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch"""
413b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
423b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport argparse
433b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport json
443b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport logging
453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport os
463b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport re
473b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport subprocess
483b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochimport sys
493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochfrom multiprocessing import Pool, cpu_count
513b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
523b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
533b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochlogging.basicConfig(level=logging.INFO)
543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Files to exclude from coverage. Dropping their data early adds more speed.
563b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# The contained cc files are already excluded from instrumentation, but inlined
573b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# data is referenced through v8's object files.
583b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochEXCLUSIONS = [
593b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'buildtools',
603b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'src/third_party',
613b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'third_party',
623b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'test',
633b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'testing',
643b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch]
653b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
663b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Executables found in the build output for which no coverage is generated.
673b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Exclude them from the coverage data file.
683b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochEXE_BLACKLIST = [
693b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'generate-bytecode-expectations',
703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'hello-world',
713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'mksnapshot',
723b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'parser-shell',
733b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'process',
743b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  'shell',
753b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch]
763b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
773b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# V8 checkout directory.
783b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochBASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(
793b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    os.path.abspath(__file__))))
803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
813b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Executable location. TODO(machenbach): Only release is supported for now.
823b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochBUILD_DIR = os.path.join(BASE_DIR, 'out', 'Release')
833b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
843b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Path prefix added by the llvm symbolizer including trailing slash.
853b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochOUTPUT_PATH_PREFIX = os.path.join(BUILD_DIR, '..', '..', '')
863b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# The sancov tool location.
883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochSANCOV_TOOL = os.path.join(
893b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    BASE_DIR, 'third_party', 'llvm', 'projects', 'compiler-rt',
903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'lib', 'sanitizer_common', 'scripts', 'sancov.py')
913b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
923b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Simple script to sanitize the PCs from objdump.
933b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochSANITIZE_PCS = os.path.join(BASE_DIR, 'tools', 'sanitizers', 'sanitize_pcs.py')
943b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
953b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# The llvm symbolizer location.
963b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochSYMBOLIZER = os.path.join(
973b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    BASE_DIR, 'third_party', 'llvm-build', 'Release+Asserts', 'bin',
983b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'llvm-symbolizer')
993b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1003b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Number of cpus.
1013b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochCPUS = cpu_count()
1023b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1033b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# Regexp to find sancov files as output by sancov_merger.py. Also grabs the
1043b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch# executable name in group 1.
1053b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben MurdochSANCOV_FILE_RE = re.compile(r'^(.*)\.result.sancov$')
1063b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1073b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1083b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef executables():
1093b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Iterates over executable files in the build directory."""
1103b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  for f in os.listdir(BUILD_DIR):
1113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    file_path = os.path.join(BUILD_DIR, f)
1123b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (os.path.isfile(file_path) and
1133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        os.access(file_path, os.X_OK) and
1143b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        f not in EXE_BLACKLIST):
1153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      yield file_path
1163b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1173b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1183b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef process_symbolizer_output(output):
1193b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Post-process llvm symbolizer output.
1203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Excludes files outside the v8 checkout or given in exclusion list above
1223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  from further processing. Drops the character index in each line.
1233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Returns: A mapping of file names to lists of line numbers. The file names
1253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           have relative paths to the v8 base directory. The lists of line
1263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           numbers don't contain duplicate lines and are sorted.
1273b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """
1283b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Drop path prefix when iterating lines. The path is redundant and takes
1293b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # too much space. Drop files outside that path, e.g. generated files in
1303b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # the build dir and absolute paths to c++ library headers.
1313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  def iter_lines():
1323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    for line in output.strip().splitlines():
1333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      if line.startswith(OUTPUT_PATH_PREFIX):
1343b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        yield line[len(OUTPUT_PATH_PREFIX):]
1353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Map file names to sets of instrumented line numbers.
1373b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  file_map = {}
1383b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  for line in iter_lines():
1393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    # Drop character number, we only care for line numbers. Each line has the
1403b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    # form: <file name>:<line number>:<character number>.
1413b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    file_name, number, _ = line.split(':')
1423b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    file_map.setdefault(file_name, set([])).add(int(number))
1433b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1443b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Remove exclusion patterns from file map. It's cheaper to do it after the
1453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # mapping, as there are few excluded files and we don't want to do this
1463b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # check for numerous lines in ordinary files.
1473b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  def keep(file_name):
1483b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    for e in EXCLUSIONS:
1493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      if file_name.startswith(e):
1503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        return False
1513b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    return True
1523b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1533b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Return in serializable form and filter.
1543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  return {k: sorted(file_map[k]) for k in file_map if keep(k)}
1553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1563b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1573b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef get_instrumented_lines(executable):
1583b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Return the instrumented lines of an executable.
1593b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1603b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Called trough multiprocessing pool.
1613b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1623b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Returns: Post-processed llvm output as returned by process_symbolizer_output.
1633b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """
1643b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # The first two pipes are from llvm's tool sancov.py with 0x added to the hex
1653b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # numbers. The results are piped into the llvm symbolizer, which outputs for
1663b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # each PC: <file name with abs path>:<line number>:<character number>.
1673b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # We don't call the sancov tool to get more speed.
1683b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  process = subprocess.Popen(
1693b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      'objdump -d %s | '
1703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      'grep \'^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ '
1713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      '<__sanitizer_cov\(_with_check\|\)\(@plt\|\)>\' | '
1723b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      'grep \'^\s\+[0-9a-f]\+\' -o | '
1733b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      '%s | '
1743b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      '%s --obj %s -functions=none' %
1753b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          (executable, SANITIZE_PCS, SYMBOLIZER, executable),
1763b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stdout=subprocess.PIPE,
1773b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stderr=subprocess.PIPE,
1783b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stdin=subprocess.PIPE,
1793b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      cwd=BASE_DIR,
1803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      shell=True,
1813b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  )
1823b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  output, _ = process.communicate()
1833b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  assert process.returncode == 0
1843b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  return process_symbolizer_output(output)
1853b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1863b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef merge_instrumented_line_results(exe_list, results):
1883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Merge multiprocessing results for all instrumented lines.
1893b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Args:
1913b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    exe_list: List of all executable names with absolute paths.
1923b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    results: List of results as returned by get_instrumented_lines.
1933b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
1943b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Returns: Dict to be used as json data as specified on the top of this page.
1953b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           The dictionary contains all instrumented lines of all files
1963b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           referenced by all executables.
1973b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """
1983b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  def merge_files(x, y):
1993b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    for file_name, lines in y.iteritems():
2003b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      x.setdefault(file_name, set([])).update(lines)
2013b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    return x
2023b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  result = reduce(merge_files, results, {})
2033b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2043b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Return data as file->lines mapping. The lines are saved as lists
2053b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # with (line number, test bits (as int)). The test bits are initialized with
2063b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # 0, meaning instrumented, but no coverage.
2073b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # The order of the test bits is given with key 'tests'. For now, these are
2083b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # the executable names. We use a _list_ with two items instead of a tuple to
2093b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # ease merging by allowing mutation of the second item.
2103b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  return {
2113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'version': 1,
2123b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'tests': sorted(map(os.path.basename, exe_list)),
2133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    'files': {f: map(lambda l: [l, 0], sorted(result[f])) for f in result},
2143b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
2153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2163b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2173b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef write_instrumented(options):
2183b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Implements the 'all' action of this tool."""
2193b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  exe_list = list(executables())
2203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Reading instrumented lines from %d executables.',
2213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch               len(exe_list))
2223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  pool = Pool(CPUS)
2233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  try:
2243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    results = pool.imap_unordered(get_instrumented_lines, exe_list)
2253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  finally:
2263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    pool.close()
2273b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2283b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Merge multiprocessing results and prepare output data.
2293b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  data = merge_instrumented_line_results(exe_list, results)
2303b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Read data from %d executables, which covers %d files.',
2323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch               len(data['tests']), len(data['files']))
2333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Writing results to %s', options.json_output)
2343b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Write json output.
2363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  with open(options.json_output, 'w') as f:
2373b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    json.dump(data, f, sort_keys=True)
2383b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2403b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef get_covered_lines(args):
2413b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Return the covered lines of an executable.
2423b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2433b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Called trough multiprocessing pool. The args are expected to unpack to:
2443b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    cov_dir: Folder with sancov files merged by sancov_merger.py.
2453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    executable: The executable that was called to produce the given coverage
2463b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                data.
2473b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    sancov_file: The merged sancov file with coverage data.
2483b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Returns: A tuple of post-processed llvm output as returned by
2503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           process_symbolizer_output and the executable name.
2513b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """
2523b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  cov_dir, executable, sancov_file = args
2533b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Let the sancov tool print the covered PCs and pipe them through the llvm
2553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # symbolizer.
2563b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  process = subprocess.Popen(
2573b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      '%s print %s 2> /dev/null | '
2583b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      '%s --obj %s -functions=none' %
2593b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          (SANCOV_TOOL,
2603b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           os.path.join(cov_dir, sancov_file),
2613b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           SYMBOLIZER,
2623b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch           os.path.join(BUILD_DIR, executable)),
2633b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stdout=subprocess.PIPE,
2643b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stderr=subprocess.PIPE,
2653b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      stdin=subprocess.PIPE,
2663b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      cwd=BASE_DIR,
2673b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      shell=True,
2683b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  )
2693b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  output, _ = process.communicate()
2703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  assert process.returncode == 0
2713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  return process_symbolizer_output(output), executable
2723b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2733b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2743b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef merge_covered_line_results(data, results):
2753b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Merge multiprocessing results for covered lines.
2763b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2773b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  The data is mutated, the results are merged into it in place.
2783b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2793b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  Args:
2803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    data: Existing coverage data from json file containing all instrumented
2813b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          lines.
2823b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    results: List of results as returned by get_covered_lines.
2833b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """
2843b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2853b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # List of executables and mapping to the test bit mask. The number of
2863b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # tests is restricted to 52, to allow javascript JSON parsing of
2873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # the bitsets encoded as numbers. JS max safe int is (1 << 53) - 1.
2883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  exe_list = data['tests']
2893b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  assert len(exe_list) <= 52, 'Max 52 different tests are supported.'
2903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  test_bit_masks = {exe:1<<i for i, exe in enumerate(exe_list)}
2913b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2923b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  def merge_lines(old_lines, new_lines, mask):
2933b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    """Merge the coverage data of a list of lines.
2943b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
2953b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    Args:
2963b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      old_lines: Lines as list of pairs with line number and test bit mask.
2973b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                 The new lines will be merged into the list in place.
2983b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      new_lines: List of new (covered) lines (sorted).
2993b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      mask: The bit to be set for covered lines. The bit index is the test
3003b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch            index of the executable that covered the line.
3013b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    """
3023b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    i = 0
3033b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    # Iterate over old and new lines, both are sorted.
3043b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    for l in new_lines:
3053b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      while old_lines[i][0] < l:
3063b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        # Forward instrumented lines not present in this coverage data.
3073b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        i += 1
3083b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        # TODO: Add more context to the assert message.
3093b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        assert i < len(old_lines), 'Covered line %d not in input file.' % l
3103b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      assert old_lines[i][0] == l, 'Covered line %d not in input file.' % l
3113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3123b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      # Add coverage information to the line.
3133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      old_lines[i][1] |= mask
3143b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  def merge_files(data, result):
3163b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    """Merge result into data.
3173b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3183b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    The data is mutated in place.
3193b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    Args:
3213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      data: Merged coverage data from the previous reduce step.
3223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      result: New result to be merged in. The type is as returned by
3233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch              get_covered_lines.
3243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    """
3253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    file_map, executable = result
3263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    files = data['files']
3273b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    for file_name, lines in file_map.iteritems():
3283b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      merge_lines(files[file_name], lines, test_bit_masks[executable])
3293b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    return data
3303b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  reduce(merge_files, results, data)
3323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3343b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef merge(options):
3353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Implements the 'merge' action of this tool."""
3363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3373b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Check if folder with coverage output exists.
3383b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  assert (os.path.exists(options.coverage_dir) and
3393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          os.path.isdir(options.coverage_dir))
3403b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3413b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Inputs for multiprocessing. List of tuples of:
3423b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Coverage dir, executable name, sancov file name.
3433b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  inputs = []
3443b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  for f in os.listdir(options.coverage_dir):
3453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    match = SANCOV_FILE_RE.match(f)
3463b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if match:
3473b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      inputs.append((options.coverage_dir, match.group(1), f))
3483b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Merging %d sancov files into %s',
3503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch               len(inputs), options.json_input)
3513b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3523b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Post-process covered lines in parallel.
3533b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  pool = Pool(CPUS)
3543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  try:
3553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    results = pool.imap_unordered(get_covered_lines, inputs)
3563b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  finally:
3573b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    pool.close()
3583b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3593b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Load existing json data file for merging the results.
3603b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  with open(options.json_input, 'r') as f:
3613b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    data = json.load(f)
3623b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3633b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Merge muliprocessing results. Mutates data.
3643b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  merge_covered_line_results(data, results)
3653b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3663b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Merged data from %d executables, which covers %d files.',
3673b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch               len(data['tests']), len(data['files']))
3683b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Writing results to %s', options.json_output)
3693b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Write merged results to file.
3713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  with open(options.json_output, 'w') as f:
3723b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    json.dump(data, f, sort_keys=True)
3733b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3743b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3753b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef split(options):
3763b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  """Implements the 'split' action of this tool."""
3773b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  # Load existing json data file for splitting.
3783b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  with open(options.json_input, 'r') as f:
3793b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    data = json.load(f)
3803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3813b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  logging.info('Splitting off %d coverage files from %s',
3823b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch               len(data['files']), options.json_input)
3833b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3843b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  for file_name, coverage in data['files'].iteritems():
3853b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    # Preserve relative directories that are part of the file name.
3863b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    file_path = os.path.join(options.output_dir, file_name + '.json')
3873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    try:
3883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      os.makedirs(os.path.dirname(file_path))
3893b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    except OSError:
3903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      # Ignore existing directories.
3913b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      pass
3923b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3933b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    with open(file_path, 'w') as f:
3943b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      # Flat-copy the old dict.
3953b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      new_data = dict(data)
3963b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
3973b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      # Update current file.
3983b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      new_data['files'] = {file_name: coverage}
3993b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4003b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      # Write json data.
4013b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      json.dump(new_data, f, sort_keys=True)
4023b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4033b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4043b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochdef main(args=None):
4053b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser = argparse.ArgumentParser()
4063b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser.add_argument('--coverage-dir',
4073b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                      help='Path to the sancov output files.')
4083b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser.add_argument('--json-input',
4093b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                      help='Path to an existing json file with coverage data.')
4103b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser.add_argument('--json-output',
4113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                      help='Path to a file to write json output to.')
4123b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser.add_argument('--output-dir',
4133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                      help='Directory where to put split output files to.')
4143b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  parser.add_argument('action', choices=['all', 'merge', 'split'],
4153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch                      help='Action to perform.')
4163b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4173b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  options = parser.parse_args(args)
4183b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  if options.action.lower() == 'all':
4193b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.json_output:
4203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--json-output is required'
4213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    write_instrumented(options)
4233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  elif options.action.lower() == 'merge':
4243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.coverage_dir:
4253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--coverage-dir is required'
4263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4273b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.json_input:
4283b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--json-input is required'
4293b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4303b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.json_output:
4313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--json-output is required'
4323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    merge(options)
4343b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  elif options.action.lower() == 'split':
4353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.json_input:
4363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--json-input is required'
4373b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4383b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if not options.output_dir:
4393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      print '--output-dir is required'
4403b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      return 1
4413b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    split(options)
4423b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  return 0
4433b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4443b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
4453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochif __name__ == '__main__':
4463b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  sys.exit(main())
447