mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
fix(check_callgraph): rework --ignore-refs to be more generic
This commit is contained in:
parent
73041ea10a
commit
64757e9fea
@ -9,6 +9,14 @@ set(COMPONENTS main)
|
|||||||
|
|
||||||
project(test_heap)
|
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)
|
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||||
add_custom_target(check_test_app_sections ALL
|
add_custom_target(check_test_app_sections ALL
|
||||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||||
@ -17,7 +25,7 @@ if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
|||||||
find-refs
|
find-refs
|
||||||
--from-sections=.iram0.text
|
--from-sections=.iram0.text
|
||||||
--to-sections=.flash.text,.flash.rodata
|
--to-sections=.flash.text,.flash.rodata
|
||||||
--ignore-symbols=__func__/__assert_func,__func__/heap_caps_alloc_failed
|
--ignore-refs=${ignore_refs}
|
||||||
--exit-code
|
--exit-code
|
||||||
DEPENDS ${elf}
|
DEPENDS ${elf}
|
||||||
)
|
)
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# Based on cally.py (https://github.com/chaudron/cally/), Copyright 2018, Eelco Chaudron
|
# 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
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import fnmatch
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from functools import partial
|
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
|
import elftools
|
||||||
from elftools.elf import elffile
|
from elftools.elf import elffile
|
||||||
@ -25,7 +31,6 @@ class RtlFunction(object):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.rtl_filename = rtl_filename
|
self.rtl_filename = rtl_filename
|
||||||
self.tu_filename = tu_filename
|
self.tu_filename = tu_filename
|
||||||
self.calls: List[str] = list()
|
|
||||||
self.refs: List[str] = list()
|
self.refs: List[str] = list()
|
||||||
self.sym = None
|
self.sym = None
|
||||||
|
|
||||||
@ -96,8 +101,14 @@ class Reference(object):
|
|||||||
|
|
||||||
|
|
||||||
class IgnorePair():
|
class IgnorePair():
|
||||||
|
"""
|
||||||
|
A pair of symbol names which should be ignored when checking references.
|
||||||
|
"""
|
||||||
def __init__(self, pair: str) -> None:
|
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):
|
class ElfInfo(object):
|
||||||
@ -164,7 +175,7 @@ class ElfInfo(object):
|
|||||||
return None
|
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
|
last_function: Optional[RtlFunction] = None
|
||||||
for line in open(rtl_filename):
|
for line in open(rtl_filename):
|
||||||
# Find function definition
|
# Find function definition
|
||||||
@ -176,32 +187,14 @@ def load_rtl_file(rtl_filename: str, tu_filename: str, functions: List[RtlFuncti
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if last_function:
|
if last_function:
|
||||||
# Find direct function calls
|
# Find direct calls and indirect references
|
||||||
match = re.match(CALL_REGEX, line)
|
for regex in [CALL_REGEX, SYMBOL_REF_REGEX]:
|
||||||
if match:
|
match = re.match(regex, line)
|
||||||
target = match.group('target')
|
if match:
|
||||||
|
target = match.group('target')
|
||||||
# if target matches on of the IgnorePair function_call attributes, remove
|
if target not in last_function.refs:
|
||||||
# the last occurrence of the associated symbol from the last_function.refs list.
|
last_function.refs.append(target)
|
||||||
call_matching_pairs = [pair for pair in ignore_pairs if pair.function_call == target]
|
continue
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def rtl_filename_matches_sym_filename(rtl_filename: str, symbol_filename: str) -> bool:
|
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)
|
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):
|
class SymbolNotFound(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -298,7 +303,7 @@ def match_rtl_funcs_to_symbols(rtl_functions: List[RtlFunction], elfinfo: ElfInf
|
|||||||
if sym_from not in symbols:
|
if sym_from not in symbols:
|
||||||
symbols.append(sym_from)
|
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
|
if '*.LC' in target_rtl_func_name: # skip local labels
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -325,7 +330,10 @@ def get_symbols_and_refs(rtl_list: List[str], elf_file: BinaryIO, ignore_pairs:
|
|||||||
|
|
||||||
rtl_functions: List[RtlFunction] = []
|
rtl_functions: List[RtlFunction] = []
|
||||||
for file_name in rtl_list:
|
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)
|
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'
|
'--to-sections', help='comma-separated list of target sections'
|
||||||
)
|
)
|
||||||
find_refs_parser.add_argument(
|
find_refs_parser.add_argument(
|
||||||
'--ignore-symbols', help='comma-separated list of symbol/function_name pairs. \
|
'--ignore-refs', help='Comma-separated list of symbol pairs to exclude from the references list.'
|
||||||
This will force the parser to ignore the symbol preceding the call to function_name'
|
'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(
|
find_refs_parser.add_argument(
|
||||||
'--exit-code',
|
'--exit-code',
|
||||||
@ -407,9 +416,10 @@ def main() -> None:
|
|||||||
if not rtl_list:
|
if not rtl_list:
|
||||||
raise RuntimeError('No RTL files specified')
|
raise RuntimeError('No RTL files specified')
|
||||||
|
|
||||||
ignore_pairs = []
|
if args.action == 'find-refs' and args.ignore_refs:
|
||||||
for pair in args.ignore_symbols.split(',') if args.ignore_symbols else []:
|
ignore_pairs = [IgnorePair(pair) for pair in args.ignore_refs.split(',')]
|
||||||
ignore_pairs.append(IgnorePair(pair))
|
else:
|
||||||
|
ignore_pairs = []
|
||||||
|
|
||||||
_, refs = get_symbols_and_refs(rtl_list, args.elf_file, ignore_pairs)
|
_, refs = get_symbols_and_refs(rtl_list, args.elf_file, ignore_pairs)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user