Chromium Code Reviews| Index: tools/sanitizers/sancov_formatter.py |
| diff --git a/tools/sanitizers/sancov_formatter.py b/tools/sanitizers/sancov_formatter.py |
| index 35827de82668694e84053bf3128ff7a7e7105c02..3398610ca8addfe9ea3190c6d8c49637816401b3 100755 |
| --- a/tools/sanitizers/sancov_formatter.py |
| +++ b/tools/sanitizers/sancov_formatter.py |
| @@ -5,9 +5,10 @@ |
| """Script to transform and merge sancov files into human readable json-format. |
| -The script supports two actions: |
| +The script supports three actions: |
| all: Writes a json file with all instrumented lines of all executables. |
| merge: Merges sancov files with coverage output into an existing json file. |
| +split: Split json file into separate files per covered source file. |
| The json data is structured as follows: |
| { |
| @@ -29,6 +30,9 @@ the bitsets encoded as numbers. JS max safe int is (1 << 53) - 1. |
| The line-number-bit_mask pairs are sorted by line number and don't contain |
| duplicates. |
| +Split json data preserves the same format, but only contains one file per |
| +json file. |
| + |
| The sancov tool is expected to be in the llvm compiler-rt third-party |
| directory. It's not checked out by default and must be added as a custom deps: |
| 'v8/third_party/llvm/projects/compiler-rt': |
| @@ -213,7 +217,7 @@ def merge_instrumented_line_results(exe_list, results): |
| def write_instrumented(options): |
| """Implements the 'all' action of this tool.""" |
| exe_list = list(executables()) |
| - logging.info('Reading instrumented lines from %d executables.' % |
| + logging.info('Reading instrumented lines from %d executables.', |
| len(exe_list)) |
| pool = Pool(CPUS) |
| try: |
| @@ -224,9 +228,9 @@ def write_instrumented(options): |
| # Merge multiprocessing results and prepare output data. |
| data = merge_instrumented_line_results(exe_list, results) |
| - logging.info('Read data from %d executables, which covers %d files.' % |
| - (len(data['tests']), len(data['files']))) |
| - logging.info('Writing results to %s' % options.json_output) |
| + logging.info('Read data from %d executables, which covers %d files.', |
| + len(data['tests']), len(data['files'])) |
| + logging.info('Writing results to %s', options.json_output) |
| # Write json output. |
| with open(options.json_output, 'w') as f: |
| @@ -342,8 +346,8 @@ def merge(options): |
| if match: |
| inputs.append((options.coverage_dir, match.group(1), f)) |
| - logging.info('Merging %d sancov files into %s' % |
| - (len(inputs), options.json_input)) |
| + logging.info('Merging %d sancov files into %s', |
| + len(inputs), options.json_input) |
| # Post-process covered lines in parallel. |
| pool = Pool(CPUS) |
| @@ -359,28 +363,62 @@ def merge(options): |
| # Merge muliprocessing results. Mutates data. |
| merge_covered_line_results(data, results) |
| - logging.info('Merged data from %d executables, which covers %d files.' % |
| - (len(data['tests']), len(data['files']))) |
| - logging.info('Writing results to %s' % options.json_output) |
| + logging.info('Merged data from %d executables, which covers %d files.', |
| + len(data['tests']), len(data['files'])) |
| + logging.info('Writing results to %s', options.json_output) |
| # Write merged results to file. |
| with open(options.json_output, 'w') as f: |
| json.dump(data, f, sort_keys=True) |
| +def split(options): |
| + """Implements the 'split' action of this tool.""" |
| + # Load existing json data file for splitting. |
| + with open(options.json_input, 'r') as f: |
| + data = json.load(f) |
| + |
| + logging.info('Splitting off %d coverage files from %s', |
| + len(data['files']), options.json_input) |
| + |
| + for file_name, coverage in data['files'].iteritems(): |
| + # Preserve relative directories that are part of the file name. |
| + file_path = os.path.join(options.output_dir, file_name + '.json') |
|
tandrii(chromium)
2016/03/16 16:31:31
my only concern here, which I was not able to comp
Michael Achenbach
2016/03/16 17:34:00
Only normalized relative paths in the project dir
tandrii(chromium)
2016/03/16 17:49:46
Acknowledged.
|
| + try: |
| + os.makedirs(os.path.dirname(file_path)) |
| + except OSError: |
| + # Ignore existing directories. |
| + pass |
| + |
| + with open(file_path, 'w') as f: |
| + # Flat-copy the old dict. |
| + new_data = dict(data) |
|
Michael Hablich
2016/03/16 13:39:12
I thought you only want the data of the individual
Michael Achenbach
2016/03/16 14:13:32
This assignment copies the key/values of data flat
Michael Hablich
2016/03/17 08:16:47
Acknowledged.
|
| + |
| + # Update current file. |
| + new_data['files'] = {file_name: coverage} |
|
Michael Hablich
2016/03/16 13:39:12
'file_name: coverage' does not make sense to me in
Michael Achenbach
2016/03/16 14:13:32
These are not strings. These are the variables fro
Michael Hablich
2016/03/17 08:16:47
Huh, I never indicated that I think these are simp
Michael Achenbach
2016/03/17 08:34:54
How would you map it? The code coverage data conta
|
| + |
| + # Write json data. |
| + json.dump(new_data, f, sort_keys=True) |
| + |
| + |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--coverage-dir', |
| help='Path to the sancov output files.') |
| parser.add_argument('--json-input', |
| help='Path to an existing json file with coverage data.') |
| - parser.add_argument('--json-output', required=True, |
| + parser.add_argument('--json-output', |
| help='Path to a file to write json output to.') |
| - parser.add_argument('action', choices=['all', 'merge'], |
| + parser.add_argument('--output-dir', |
| + help='Directory where to put split output files to.') |
| + parser.add_argument('action', choices=['all', 'merge', 'split'], |
| help='Action to perform.') |
| options = parser.parse_args() |
| if options.action.lower() == 'all': |
| + if not options.json_output: |
| + print '--json-output is required' |
| + return 1 |
| write_instrumented(options) |
| elif options.action.lower() == 'merge': |
| if not options.coverage_dir: |
| @@ -389,7 +427,18 @@ def main(): |
| if not options.json_input: |
| print '--json-input is required' |
| return 1 |
| + if not options.json_output: |
| + print '--json-output is required' |
| + return 1 |
| merge(options) |
| + elif options.action.lower() == 'split': |
| + if not options.json_input: |
| + print '--json-input is required' |
| + return 1 |
| + if not options.output_dir: |
| + print '--output-dir is required' |
| + return 1 |
| + split(options) |
| return 0 |