From 2df531c16083fbca748bc9f5d46432b1746d0fd9 Mon Sep 17 00:00:00 2001 From: Sean Fitzgibbon Date: Mon, 9 Jun 2025 12:34:21 +0100 Subject: [PATCH 1/2] Delete unreachable code Change-Id: I76079a1c16cd29474908059ed057da559e4ad901 --- coverage-tool/coverage-reporting/intermediate_layer.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/coverage-tool/coverage-reporting/intermediate_layer.py b/coverage-tool/coverage-reporting/intermediate_layer.py index 55474b9..60c8b3c 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 ############################################################################### @@ -218,10 +218,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 -- GitLab From f88a6782f0fe30818f7d862c2b69e5f1e33399db Mon Sep 17 00:00:00 2001 From: Sean Fitzgibbon Date: Mon, 9 Jun 2025 13:14:28 +0100 Subject: [PATCH 2/2] Optimize _process_fn_no_sources - find is faster than the -r option for grep, and its results can be cached. - It's faster to scan a file by hand and lookup all the function names at once than it is to invoke grep on it once per function name. Change-Id: I2888c60f019e218667b760f015bff86a09f11843 --- .../coverage-reporting/intermediate_layer.py | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/coverage-tool/coverage-reporting/intermediate_layer.py b/coverage-tool/coverage-reporting/intermediate_layer.py index 60c8b3c..21ba356 100644 --- a/coverage-tool/coverage-reporting/intermediate_layer.py +++ b/coverage-tool/coverage-reporting/intermediate_layer.py @@ -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" @@ -560,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 @@ -591,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): """ @@ -686,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. @@ -701,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( -- GitLab