Add a testcase to run the same test application and GDB script which we use for Zephyr GDB stub testing, but now with the GDB stub enabled at QEMU itself using it as a reference RDP backend implementation. This allows to check the Zephyr's gdbstub implementation has similar behavior as the reference. Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
84 lines
3.0 KiB
Python
Executable File
84 lines
3.0 KiB
Python
Executable File
# Copyright (c) 2023 Intel Corporation.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import logging
|
|
import shlex
|
|
import re
|
|
import pytest
|
|
from twister_harness import DeviceAdapter
|
|
|
|
ZEPHYR_BASE = os.getenv("ZEPHYR_BASE")
|
|
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts", "pylib", "twister"))
|
|
from twisterlib.cmakecache import CMakeCache
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@pytest.fixture()
|
|
def gdb_process(dut: DeviceAdapter, gdb_script, gdb_timeout, gdb_target_remote) -> subprocess.CompletedProcess:
|
|
build_dir = dut.device_config.build_dir
|
|
cmake_cache = CMakeCache.from_file(os.path.join(build_dir, 'CMakeCache.txt'))
|
|
gdb_exec = cmake_cache.get('CMAKE_GDB', None)
|
|
assert gdb_exec
|
|
source_dir = cmake_cache.get('APPLICATION_SOURCE_DIR', None)
|
|
assert source_dir
|
|
build_image = cmake_cache.get('BYPRODUCT_KERNEL_ELF_NAME', None)
|
|
assert build_image
|
|
gdb_log_file = os.path.join(build_dir, 'gdb.log')
|
|
cmd = [gdb_exec, '-batch',
|
|
'-ex', f'set pagination off',
|
|
'-ex', f'set trace-commands on',
|
|
'-ex', f'set logging file {gdb_log_file}',
|
|
'-ex', f'set logging enabled on',
|
|
'-ex', f'target remote {gdb_target_remote}',
|
|
'-x', f'{source_dir}/{gdb_script}', build_image]
|
|
logger.info(f'Run GDB: {shlex.join(cmd)}')
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=gdb_timeout)
|
|
logger.info(f'GDB ends rc={result.returncode}')
|
|
return result
|
|
#
|
|
|
|
@pytest.fixture(scope="module")
|
|
def expected_app():
|
|
return [
|
|
re.compile(r"Booting from ROM"),
|
|
re.compile(r"Booting Zephyr OS build"),
|
|
re.compile(r"main\(\):enter"),
|
|
]
|
|
|
|
@pytest.fixture(scope="module")
|
|
def expected_gdb():
|
|
return [
|
|
re.compile(r'Breakpoint 1 at 0x'),
|
|
re.compile(r'Breakpoint 2 at 0x'),
|
|
re.compile(r'Breakpoint 1, test '),
|
|
re.compile(r'Breakpoint 2, main '),
|
|
re.compile(r'GDB:PASSED'),
|
|
]
|
|
|
|
@pytest.fixture(scope="module")
|
|
def expected_gdb_detach():
|
|
return [
|
|
re.compile(r'Inferior.*will be killed'), # Zephyr gdbstub
|
|
re.compile(r'Inferior.*detached') # QEMU gdbstub
|
|
]
|
|
|
|
|
|
def test_gdbstub(dut: DeviceAdapter, gdb_process, expected_app, expected_gdb, expected_gdb_detach):
|
|
"""
|
|
Test gdbstub feature using a GDB script. We connect to the DUT, run the
|
|
GDB script then evaluate return code and expected patterns at the GDB
|
|
and Test Applicaiton outputs.
|
|
"""
|
|
logger.debug(f"GDB output:\n{gdb_process.stdout}\n")
|
|
assert gdb_process.returncode == 0
|
|
assert all([ex_re.search(gdb_process.stdout, re.MULTILINE) for ex_re in expected_gdb]), 'No expected GDB output'
|
|
assert any([ex_re.search(gdb_process.stdout, re.MULTILINE) for ex_re in expected_gdb_detach]), 'No expected GDB quit'
|
|
app_output = '\n'.join(dut.readlines(print_output = False))
|
|
logger.debug(f"App output:\n{app_output}\n")
|
|
assert all([ex_re.search(app_output, re.MULTILINE) for ex_re in expected_app]), 'No expected Application output'
|
|
#
|