diff --git a/coverage-tool/coverage-reporting/intermediate_layer.py b/coverage-tool/coverage-reporting/intermediate_layer.py index 55474b91eaa9aa10809ee93f8c87c91be3408e5d..21ba356666508c68efa449fdc9d212f2fa404d76 100644 --- a/coverage-tool/coverage-reporting/intermediate_layer.py +++ b/coverage-tool/coverage-reporting/intermediate_layer.py @@ -1,6 +1,6 @@ # !/usr/bin/env python ############################################################################### -# Copyright (c) 2020-2023, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2020-2025, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause ############################################################################### @@ -29,6 +29,8 @@ from typing import Generator from typing import Union from typing import Tuple import logging +import sys +from collections import defaultdict __version__ = "7.0" @@ -218,10 +220,6 @@ def get_function_line_numbers(source_file: str) -> Dict[str, int]: except BaseException: logger.warning("Warning: Can't get all function line numbers from %s" % source_file) - except Exception as ex: - logger.warning(f"Warning: Unknown error '{ex}' when executing command " - f"'{command}'") - return {} return fln @@ -564,6 +562,24 @@ class CoverageHandler(object): return self._source_files +IDENTIFIER = re.compile(r'\b[A-Za-z_]\w*\b') + +def identifiers(filename): + with open(filename) as f: + for line in f: + for match in IDENTIFIER.finditer(line): + yield match.group() + +def locate_identifiers(ids, filenames): + """Map each identifier to the set of files in which it appears.""" + result = defaultdict(set) + for filename in filenames: + for s in identifiers(filename): + if s in ids: + result[s].add(filename) + return result + + class IntermediateCodeCoverage(object): """Class used to process the trace data along with the dwarf signature files to produce an intermediate layer in json with @@ -595,6 +611,7 @@ class IntermediateCodeCoverage(object): self.elf_map = {} # For elf custom mappings self.elf_custom = None + self.found_source_files = None def process(self): """ @@ -690,6 +707,15 @@ class IntermediateCodeCoverage(object): self._process_fn_no_sources(parser) return parser + def find_source_files(self): + # Cache the list of .c, .s and .S files found in the local workspace. + if self.found_source_files is None: + self.found_source_files = [sys.intern(f) for f in subprocess.check_output([ + 'find', self.local_workspace, + '(', '-name', '*.c', '-o', '-name', '*.s', '-o', '-name', '*.S', ')', + '-type', 'f', '-print0']).decode('utf-8').rstrip('\0').split('\0')] + return self.found_source_files + def _process_fn_no_sources(self, parser: BinaryParser): """ Checks function coverage for functions with no dwarf signature i.e. @@ -705,17 +731,14 @@ class IntermediateCodeCoverage(object): traces_address_pointer = 0 _functions = parser.no_source_functions functions_addresses = sorted(_functions.keys()) + function_names = [v['name'] for v in _functions.values()] + hits = locate_identifiers(function_names, self.find_source_files()) address_size = 4 for start_address in functions_addresses: function_covered = False function_name = _functions[start_address]['name'] # Get all files in the source code where the function is defined - source_files = os_command("grep --include '*.c' --include '*.s' " - "--include '*.S' -nrw '{}' {}" - "| cut -d: -f1". - format(function_name, - self.local_workspace)) - unique_files = set(source_files.split()) + unique_files = hits[function_name] sources_found = [] for source_file in unique_files: line_number = parser.function_line_numbers.get_line_number(