[test] Add build_dir option to sancov formatter

This'll make the workflow more flexible as in GN world, the builddir
is often not in the currently hardcoded out/Release.

NOTRY=true

Bug: chromium:645890
Change-Id: Id2afc8d8fea322ac98b65b7e9d28a2ba8c0dd0ac
Reviewed-on: https://chromium-review.googlesource.com/549899
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46258}
This commit is contained in:
Michael Achenbach 2017-06-27 14:32:47 +02:00 committed by Commit Bot
parent 8c38248b95
commit 3c41aba2ab
2 changed files with 33 additions and 24 deletions

View File

@ -78,13 +78,6 @@ EXE_BLACKLIST = [
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(
os.path.abspath(__file__))))
# Executable location.
# FIXME(machenbach): Pass real builddir to work with developer gn checkouts.
BUILD_DIR = os.path.join(BASE_DIR, 'out', 'Release')
# Path prefix added by the llvm symbolizer including trailing slash.
OUTPUT_PATH_PREFIX = os.path.join(BUILD_DIR, '..', '..', '')
# The sancov tool location.
SANCOV_TOOL = os.path.join(
BASE_DIR, 'third_party', 'llvm', 'projects', 'compiler-rt',
@ -106,17 +99,17 @@ CPUS = cpu_count()
SANCOV_FILE_RE = re.compile(r'^(.*)\.result.sancov$')
def executables():
def executables(build_dir):
"""Iterates over executable files in the build directory."""
for f in os.listdir(BUILD_DIR):
file_path = os.path.join(BUILD_DIR, f)
for f in os.listdir(build_dir):
file_path = os.path.join(build_dir, f)
if (os.path.isfile(file_path) and
os.access(file_path, os.X_OK) and
f not in EXE_BLACKLIST):
yield file_path
def process_symbolizer_output(output):
def process_symbolizer_output(output, build_dir):
"""Post-process llvm symbolizer output.
Excludes files outside the v8 checkout or given in exclusion list above
@ -126,13 +119,16 @@ def process_symbolizer_output(output):
have relative paths to the v8 base directory. The lists of line
numbers don't contain duplicate lines and are sorted.
"""
# Path prefix added by the llvm symbolizer including trailing slash.
output_path_prefix = os.path.join(build_dir, '..', '..', '')
# Drop path prefix when iterating lines. The path is redundant and takes
# too much space. Drop files outside that path, e.g. generated files in
# the build dir and absolute paths to c++ library headers.
def iter_lines():
for line in output.strip().splitlines():
if line.startswith(OUTPUT_PATH_PREFIX):
yield line[len(OUTPUT_PATH_PREFIX):]
if line.startswith(output_path_prefix):
yield line[len(output_path_prefix):]
# Map file names to sets of instrumented line numbers.
file_map = {}
@ -182,7 +178,7 @@ def get_instrumented_lines(executable):
)
output, _ = process.communicate()
assert process.returncode == 0
return process_symbolizer_output(output)
return process_symbolizer_output(output, os.path.dirname(executable))
def merge_instrumented_line_results(exe_list, results):
@ -217,7 +213,7 @@ def merge_instrumented_line_results(exe_list, results):
def write_instrumented(options):
"""Implements the 'all' action of this tool."""
exe_list = list(executables())
exe_list = list(executables(options.build_dir))
logging.info('Reading instrumented lines from %d executables.',
len(exe_list))
pool = Pool(CPUS)
@ -243,8 +239,8 @@ def get_covered_lines(args):
Called trough multiprocessing pool. The args are expected to unpack to:
cov_dir: Folder with sancov files merged by sancov_merger.py.
executable: The executable that was called to produce the given coverage
data.
executable: Absolute path to the executable that was called to produce the
given coverage data.
sancov_file: The merged sancov file with coverage data.
Returns: A tuple of post-processed llvm output as returned by
@ -260,7 +256,7 @@ def get_covered_lines(args):
(SANCOV_TOOL,
os.path.join(cov_dir, sancov_file),
SYMBOLIZER,
os.path.join(BUILD_DIR, executable)),
executable),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
@ -269,7 +265,10 @@ def get_covered_lines(args):
)
output, _ = process.communicate()
assert process.returncode == 0
return process_symbolizer_output(output), executable
return (
process_symbolizer_output(output, os.path.dirname(executable)),
os.path.basename(executable),
)
def merge_covered_line_results(data, results):
@ -340,12 +339,16 @@ def merge(options):
os.path.isdir(options.coverage_dir))
# Inputs for multiprocessing. List of tuples of:
# Coverage dir, executable name, sancov file name.
# Coverage dir, absoluate path to executable, sancov file name.
inputs = []
for f in os.listdir(options.coverage_dir):
match = SANCOV_FILE_RE.match(f)
for sancov_file in os.listdir(options.coverage_dir):
match = SANCOV_FILE_RE.match(sancov_file)
if match:
inputs.append((options.coverage_dir, match.group(1), f))
inputs.append((
options.coverage_dir,
os.path.join(options.build_dir, match.group(1)),
sancov_file,
))
logging.info('Merging %d sancov files into %s',
len(inputs), options.json_input)
@ -404,6 +407,10 @@ def split(options):
def main(args=None):
parser = argparse.ArgumentParser()
# TODO(machenbach): Make this required and deprecate the default.
parser.add_argument('--build-dir',
default=os.path.join(BASE_DIR, 'out', 'Release'),
help='Path to the build output directory.')
parser.add_argument('--coverage-dir',
help='Path to the sancov output files.')
parser.add_argument('--json-input',
@ -416,6 +423,7 @@ def main(args=None):
help='Action to perform.')
options = parser.parse_args(args)
options.build_dir = os.path.abspath(options.build_dir)
if options.action.lower() == 'all':
if not options.json_output:
print '--json-output is required'

View File

@ -185,7 +185,8 @@ class FormatterTests(unittest.TestCase):
cls._cov.report()
def test_process_symbolizer_output(self):
result = sancov_formatter.process_symbolizer_output(SYMBOLIZER_OUTPUT)
result = sancov_formatter.process_symbolizer_output(
SYMBOLIZER_OUTPUT, BUILD_DIR)
self.assertEquals(EXPECTED_PROCESSED_OUTPUT, result)
def test_merge_instrumented_line_results(self):