Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: gdbstub: simple test #63672

Merged
merged 6 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions boards/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ config QEMU_GDBSERVER_LISTEN_DEV
as the `QEMU_EXTRA_FLAGS` environment variable. Refer to application
development doc and/or QEMU invocation doc for more info.

config QEMU_EXTRA_FLAGS
string "QEMU extra flags"
depends on QEMU_TARGET
default ""
help
This option is to pass onto QEMU an extra list of parameters
to setup devices, for example to allocate interface for Zephyr
GDBstub over serial with `-serial tcp:127.0.0.1:5678,server`

# There might not be any board options, hence the optional source
osource "$(BOARD_DIR)/Kconfig"
endmenu
Expand Down
7 changes: 7 additions & 0 deletions cmake/emu/qemu.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,13 @@ set(env_qemu $ENV{QEMU_EXTRA_FLAGS})
separate_arguments(env_qemu)
list(APPEND QEMU_EXTRA_FLAGS ${env_qemu})

# Also append QEMU flags from config
if(NOT CONFIG_QEMU_EXTRA_FLAGS STREQUAL "")
set(config_qemu_flags ${CONFIG_QEMU_EXTRA_FLAGS})
separate_arguments(config_qemu_flags)
list(APPEND QEMU_EXTRA_FLAGS "${config_qemu_flags}")
endif()

list(APPEND MORE_FLAGS_FOR_debugserver_qemu -S)

if(NOT CONFIG_QEMU_GDBSERVER_LISTEN_DEV STREQUAL "")
Expand Down
5 changes: 3 additions & 2 deletions doc/services/debugging/gdbstub.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ Using Serial Backend
Example
*******

This is an example using ``samples/subsys/debug/gdbstub`` to demonstrate
how GDB stub works.
This is an example to demonstrate how GDB stub works.
You can also refer to ``tests/subsys/debug/gdbstub``
for its implementation as a Twister test.

#. Open two terminal windows.

Expand Down
31 changes: 0 additions & 31 deletions samples/subsys/debug/gdbstub/README.rst

This file was deleted.

7 changes: 0 additions & 7 deletions samples/subsys/debug/gdbstub/boards/qemu_x86_64.overlay

This file was deleted.

34 changes: 0 additions & 34 deletions samples/subsys/debug/gdbstub/pytest/test_gdbstub.py

This file was deleted.

17 changes: 0 additions & 17 deletions samples/subsys/debug/gdbstub/run.gdbinit

This file was deleted.

15 changes: 0 additions & 15 deletions samples/subsys/debug/gdbstub/sample.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

cmake_minimum_required(VERSION 3.20.0)

if(BOARD MATCHES "qemu_x86")
list(APPEND QEMU_EXTRA_FLAGS -serial tcp:127.0.0.1:5678,server)
endif()

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(debug)
project(debug_gdbstub)

target_sources(app PRIVATE src/main.c)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
CONFIG_GDBSTUB=y
golowanow marked this conversation as resolved.
Show resolved Hide resolved
CONFIG_GDBSTUB_SERIAL_BACKEND=y
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_USERSPACE=y
CONFIG_KOBJECT_TEXT_AREA=4096
25 changes: 25 additions & 0 deletions tests/subsys/debug/gdbstub/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#
# Copyright (c) 2023 intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
#

import pytest

def pytest_addoption(parser):
parser.addoption('--gdb_target_remote')
parser.addoption('--gdb_timeout')
parser.addoption('--gdb_script')

@pytest.fixture()
def gdb_script(request):
return request.config.getoption('--gdb_script')

@pytest.fixture()
def gdb_timeout(request):
return int(request.config.getoption('--gdb_timeout', default=60))

@pytest.fixture()
def gdb_target_remote(request):
return request.config.getoption('--gdb_target_remote', default=":5678")
#
83 changes: 83 additions & 0 deletions tests/subsys/debug/gdbstub/pytest/test_gdbstub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 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():
golowanow marked this conversation as resolved.
Show resolved Hide resolved
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'
#
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,12 @@ static int test(void)
return a + b;
}

static void thread_entry(void *p1, void *p2, void *p3)
{
printk("Hello from user thread!\n");
}

int main(void)
{
int ret;

printk("%s():enter\n", __func__);
ret = test();
printk("%d\n", ret);
printk("ret=%d\n", ret);
return 0;
}

K_THREAD_DEFINE(thread, STACKSIZE, thread_entry, NULL, NULL, NULL,
7, K_USER, 0);
17 changes: 17 additions & 0 deletions tests/subsys/debug/gdbstub/test_breakpoints.gdbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
b test
b main.c:29
c

# break at test()
s
set var a = 2
c

# break at main()
if ret == 6
printf "GDB:PASSED\n"
quit 0
else
printf "GDB:FAILED\n"
quit 1
end
59 changes: 59 additions & 0 deletions tests/subsys/debug/gdbstub/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#
# Copyright (c) 2020, 2023 intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
#

tests:
# Connect to Zephyr gdbstub and run a simple GDB script
debug.gdbstub.breakpoints:
platform_allow: qemu_x86
harness: pytest
harness_config:
pytest_root:
- "pytest/test_gdbstub.py"
pytest_args:
- "--gdb_timeout"
- "20"
- "--gdb_script"
- "test_breakpoints.gdbinit"
- "--gdb_target_remote"
- "tcp::5678"
tags:
- debug
- gdbstub
extra_configs:
# Make sure the gdbstub port chosen is unique for this test to avoid conflicts
# when Twister runs tests in parallel on the same host.
- CONFIG_QEMU_EXTRA_FLAGS="-serial tcp::5678,server"

# Connect to QEMU gdbstub backend and run the same GDB test script
# to check it against a reference RDP backend implementation expecting
# similar behavior as for Zephyr's gdbstub.
# Use non-default QEMU gdbstub port 1235 for this test to avoid conflicts
# with some other test on QEMU running in parallel.
debug.gdbstub_qemu.breakpoints:
nashif marked this conversation as resolved.
Show resolved Hide resolved
platform_allow: qemu_x86
harness: pytest
harness_config:
pytest_root:
- "pytest/test_gdbstub.py"
pytest_args:
- "--gdb_timeout"
- "20"
- "--gdb_script"
- "test_breakpoints.gdbinit"
- "--gdb_target_remote"
- "tcp::1235"
tags:
- debug
- gdbstub
extra_configs:
# Turn off Zephyr's gdbstub for this test case.
- CONFIG_GDBSTUB=n
- CONFIG_GDBSTUB_SERIAL_BACKEND=n
# Make sure the gdbstub port chosen is unique for this test to avoid conflicts
# when Twister runs tests in parallel on the same host.
- CONFIG_QEMU_EXTRA_FLAGS="-S -gdb tcp::1235"
# Clear QEMU default 'tcp::1234'
- CONFIG_QEMU_GDBSERVER_LISTEN_DEV=""
Loading