twister: fix Ztest C++ test names extraction from ELF

Fix Ztest test function name extraction from ELF symbols
for C++ compiled binaries where symbol names need additional
'demangling' to match with corresponding test names.

The `c++filt` utility (part of binutils) is called for
demangling when it is needed.

Twister test suite extension and adjustment.

Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
This commit is contained in:
Dmitrii Golovanov 2024-11-01 12:07:02 +01:00 committed by Anas Nashif
commit e11aecaed5
21 changed files with 288 additions and 35 deletions

View file

@ -814,6 +814,10 @@ class ProjectBuilder(FilterBuilder):
self.env = env
self.duts = None
@property
def trace(self) -> bool:
return self.options.verbose > 2
def log_info(self, filename, inline_logs, log_testcases=False):
filename = os.path.abspath(os.path.realpath(filename))
if inline_logs:
@ -1087,6 +1091,18 @@ class ProjectBuilder(FilterBuilder):
self.instance.reason = reason
self.instance.add_missing_case_status(TwisterStatus.BLOCK, reason)
def demangle(self, symbol_name):
if symbol_name[:2] == '_Z':
try:
cpp_filt = subprocess.run('c++filt', input=symbol_name, text=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if self.trace:
logger.debug(f"Demangle: '{symbol_name}'==>'{cpp_filt.stdout}'")
return cpp_filt.stdout.strip()
except Exception as e:
logger.error(f"Failed to demangle '{symbol_name}': {e}")
return symbol_name
def determine_testcases(self, results):
yaml_testsuite_name = self.instance.testsuite.id
logger.debug(f"Determine test cases for test suite: {yaml_testsuite_name}")
@ -1102,19 +1118,22 @@ class ProjectBuilder(FilterBuilder):
for sym in section.iter_symbols():
# It is only meant for new ztest fx because only new ztest fx exposes test functions
# precisely.
m_ = new_ztest_unit_test_regex.search(sym.name)
if not m_:
continue
# Demangle C++ symbols
m_ = new_ztest_unit_test_regex.search(self.demangle(sym.name))
if not m_:
continue
# The 1st capture group is new ztest suite name.
# The 2nd capture group is new ztest unit test name.
matches = new_ztest_unit_test_regex.findall(sym.name)
if matches:
for m in matches:
new_ztest_suite = m[0]
if new_ztest_suite not in self.instance.testsuite.ztest_suite_names:
logger.warning(f"Unexpected Ztest suite '{new_ztest_suite}' "
f"not present in: {self.instance.testsuite.ztest_suite_names}")
test_func_name = m[1].replace("test_", "", 1)
testcase_id = f"{yaml_testsuite_name}.{new_ztest_suite}.{test_func_name}"
detected_cases.append(testcase_id)
new_ztest_suite = m_[1]
if new_ztest_suite not in self.instance.testsuite.ztest_suite_names:
logger.warning(f"Unexpected Ztest suite '{new_ztest_suite}' "
f"not present in: {self.instance.testsuite.ztest_suite_names}")
test_func_name = m_[2].replace("test_", "", 1)
testcase_id = f"{yaml_testsuite_name}.{new_ztest_suite}.{test_func_name}"
detected_cases.append(testcase_id)
if detected_cases:
logger.debug(f"Detected Ztest cases: [{', '.join(detected_cases)}] in {elf_file}")