mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/check_callgraph_ignore_indirect_calls' into 'master'
fix(check_callgraph): rework --ignore-symbols to be more generic See merge request espressif/esp-idf!29850
This commit is contained in:
commit
279b67cc5e
@ -9,6 +9,14 @@ set(COMPONENTS main)
|
||||
|
||||
project(test_heap)
|
||||
|
||||
string(JOIN "," ignore_refs
|
||||
heap_caps_*/__func__*
|
||||
tlsf_*/__func__*
|
||||
multi_heap_*/__func__*
|
||||
dram_alloc_to_iram_addr/__func__*
|
||||
list_*/__func__*
|
||||
)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
@ -17,7 +25,7 @@ if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--ignore-symbols=__func__/__assert_func,__func__/heap_caps_alloc_failed
|
||||
--ignore-refs=${ignore_refs}
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
|
@ -1,14 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Based on cally.py (https://github.com/chaudron/cally/), Copyright 2018, Eelco Chaudron
|
||||
# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import os
|
||||
import re
|
||||
from functools import partial
|
||||
from typing import BinaryIO, Callable, Dict, Generator, List, Optional, Tuple
|
||||
from typing import BinaryIO
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
import elftools
|
||||
from elftools.elf import elffile
|
||||
@ -25,7 +31,6 @@ class RtlFunction(object):
|
||||
self.name = name
|
||||
self.rtl_filename = rtl_filename
|
||||
self.tu_filename = tu_filename
|
||||
self.calls: List[str] = list()
|
||||
self.refs: List[str] = list()
|
||||
self.sym = None
|
||||
|
||||
@ -96,8 +101,14 @@ class Reference(object):
|
||||
|
||||
|
||||
class IgnorePair():
|
||||
"""
|
||||
A pair of symbol names which should be ignored when checking references.
|
||||
"""
|
||||
def __init__(self, pair: str) -> None:
|
||||
self.symbol, self.function_call = pair.split('/')
|
||||
try:
|
||||
self.source, self.dest = pair.split('/')
|
||||
except ValueError:
|
||||
raise ValueError(f'Invalid ignore pair: {pair}. Must be in the form "source/dest".')
|
||||
|
||||
|
||||
class ElfInfo(object):
|
||||
@ -164,7 +175,7 @@ class ElfInfo(object):
|
||||
return None
|
||||
|
||||
|
||||
def load_rtl_file(rtl_filename: str, tu_filename: str, functions: List[RtlFunction], ignore_pairs: List[IgnorePair]) -> None:
|
||||
def load_rtl_file(rtl_filename: str, tu_filename: str, functions: List[RtlFunction]) -> None:
|
||||
last_function: Optional[RtlFunction] = None
|
||||
for line in open(rtl_filename):
|
||||
# Find function definition
|
||||
@ -176,32 +187,14 @@ def load_rtl_file(rtl_filename: str, tu_filename: str, functions: List[RtlFuncti
|
||||
continue
|
||||
|
||||
if last_function:
|
||||
# Find direct function calls
|
||||
match = re.match(CALL_REGEX, line)
|
||||
if match:
|
||||
target = match.group('target')
|
||||
|
||||
# if target matches on of the IgnorePair function_call attributes, remove
|
||||
# the last occurrence of the associated symbol from the last_function.refs list.
|
||||
call_matching_pairs = [pair for pair in ignore_pairs if pair.function_call == target]
|
||||
if call_matching_pairs and last_function and last_function.refs:
|
||||
for pair in call_matching_pairs:
|
||||
ignored_symbols = [ref for ref in last_function.refs if pair.symbol in ref]
|
||||
if ignored_symbols:
|
||||
last_ref = ignored_symbols.pop()
|
||||
last_function.refs = [ref for ref in last_function.refs if last_ref != ref]
|
||||
|
||||
if target not in last_function.calls:
|
||||
last_function.calls.append(target)
|
||||
continue
|
||||
|
||||
# Find symbol references
|
||||
match = re.match(SYMBOL_REF_REGEX, line)
|
||||
if match:
|
||||
target = match.group('target')
|
||||
if target not in last_function.refs:
|
||||
last_function.refs.append(target)
|
||||
continue
|
||||
# Find direct calls and indirect references
|
||||
for regex in [CALL_REGEX, SYMBOL_REF_REGEX]:
|
||||
match = re.match(regex, line)
|
||||
if match:
|
||||
target = match.group('target')
|
||||
if target not in last_function.refs:
|
||||
last_function.refs.append(target)
|
||||
continue
|
||||
|
||||
|
||||
def rtl_filename_matches_sym_filename(rtl_filename: str, symbol_filename: str) -> bool:
|
||||
@ -219,6 +212,18 @@ def rtl_filename_matches_sym_filename(rtl_filename: str, symbol_filename: str) -
|
||||
return os.path.basename(rtl_filename).startswith(symbol_filename)
|
||||
|
||||
|
||||
def filter_ignore_pairs(function: RtlFunction, ignore_pairs: List[IgnorePair]) -> None:
|
||||
"""
|
||||
Given a function S0 and a list of ignore pairs (S, T),
|
||||
remove all references to T for which S==S0.
|
||||
"""
|
||||
for ignore_pair in ignore_pairs:
|
||||
if fnmatch.fnmatch(function.name, ignore_pair.source):
|
||||
for ref in function.refs:
|
||||
if fnmatch.fnmatch(ref, ignore_pair.dest):
|
||||
function.refs.remove(ref)
|
||||
|
||||
|
||||
class SymbolNotFound(RuntimeError):
|
||||
pass
|
||||
|
||||
@ -298,7 +303,7 @@ def match_rtl_funcs_to_symbols(rtl_functions: List[RtlFunction], elfinfo: ElfInf
|
||||
if sym_from not in symbols:
|
||||
symbols.append(sym_from)
|
||||
|
||||
for target_rtl_func_name in source_rtl_func.calls + source_rtl_func.refs:
|
||||
for target_rtl_func_name in source_rtl_func.refs:
|
||||
if '*.LC' in target_rtl_func_name: # skip local labels
|
||||
continue
|
||||
|
||||
@ -325,7 +330,10 @@ def get_symbols_and_refs(rtl_list: List[str], elf_file: BinaryIO, ignore_pairs:
|
||||
|
||||
rtl_functions: List[RtlFunction] = []
|
||||
for file_name in rtl_list:
|
||||
load_rtl_file(file_name, file_name, rtl_functions, ignore_pairs)
|
||||
load_rtl_file(file_name, file_name, rtl_functions)
|
||||
|
||||
for rtl_func in rtl_functions:
|
||||
filter_ignore_pairs(rtl_func, ignore_pairs)
|
||||
|
||||
return match_rtl_funcs_to_symbols(rtl_functions, elfinfo)
|
||||
|
||||
@ -378,8 +386,9 @@ def main() -> None:
|
||||
'--to-sections', help='comma-separated list of target sections'
|
||||
)
|
||||
find_refs_parser.add_argument(
|
||||
'--ignore-symbols', help='comma-separated list of symbol/function_name pairs. \
|
||||
This will force the parser to ignore the symbol preceding the call to function_name'
|
||||
'--ignore-refs', help='Comma-separated list of symbol pairs to exclude from the references list.'
|
||||
'The caller and the callee are separated by a slash. '
|
||||
'Wildcards are supported. Example: my_lib_*/some_lib_in_flash_*.'
|
||||
)
|
||||
find_refs_parser.add_argument(
|
||||
'--exit-code',
|
||||
@ -407,9 +416,10 @@ def main() -> None:
|
||||
if not rtl_list:
|
||||
raise RuntimeError('No RTL files specified')
|
||||
|
||||
ignore_pairs = []
|
||||
for pair in args.ignore_symbols.split(',') if args.ignore_symbols else []:
|
||||
ignore_pairs.append(IgnorePair(pair))
|
||||
if args.action == 'find-refs' and args.ignore_refs:
|
||||
ignore_pairs = [IgnorePair(pair) for pair in args.ignore_refs.split(',')]
|
||||
else:
|
||||
ignore_pairs = []
|
||||
|
||||
_, refs = get_symbols_and_refs(rtl_list, args.elf_file, ignore_pairs)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user