zephyr/doc/services/debugging/gdbstub.rst

303 lines
9.9 KiB
ReStructuredText

.. _gdbstub:
GDB stub
########
.. contents::
:local:
:depth: 2
Overview
********
The gdbstub feature provides an implementation of the GDB Remote
Serial Protocol (RSP) that allows you to remotely debug Zephyr
using GDB.
The protocol supports different connection types: serial, UDP/IP and
TCP/IP. Zephyr currently supports only serial device communication.
The GDB program acts as a client while the Zephyr gdbstub acts as a
server. When this feature is enabled, Zephyr stops its execution after
:c:func:`gdb_init` starts gdbstub service and waits for a GDB
connection. Once a connection is established it is possible to
synchronously interact with Zephyr. Note that currently it is not
possible to asynchronously send commands to the target.
Features
********
The following features are supported:
* Add and remove breakpoints
* Continue and step the target
* Print backtrace
* Read or write general registers
* Read or write the memory
Enabling GDB Stub
*****************
GDB stub can be enabled with the :kconfig:option:`CONFIG_GDBSTUB` option.
Using Serial Backend
====================
The serial backend for GDB stub can be enabled with
the :kconfig:option:`CONFIG_GDBSTUB_SERIAL_BACKEND` option.
Since serial backend utilizes UART devices to send and receive GDB commands,
* If there are spare UART devices on the board, set ``zephyr,gdbstub-uart``
property of the chosen node to the spare UART device so that :c:func:`printk`
and log messages are not being printed to the same UART device used for GDB.
* For boards with only one UART device, :c:func:`printk` and logging
must be disabled if they are also using the same UART device for output.
GDB related messages may interleave with log messages which may have
unintended consequences. Usually this can be done by disabling
:kconfig:option:`CONFIG_PRINTK` and :kconfig:option:`CONFIG_LOG`.
Debugging
*********
Using Serial Backend
====================
#. Build with GDB stub and serial backend enabled.
#. Flash built image onto board and reset the board.
* Execution should now be paused at :c:func:`gdb_init`.
#. Execute GDB on development machine and connect to the GDB stub.
.. code-block:: bash
target remote <serial device>
For example,
.. code-block:: bash
target remote /dev/ttyUSB1
#. GDB commands can be used to start debugging.
Example
*******
There is a test application :zephyr_file:`tests/subsys/debug/gdbstub` with one of its
test cases ``debug.gdbstub.breakpoints`` demonstrating how the Zephyr GDB stub can be used.
The test also has a case to connect to the QEMU's GDB stub implementation (at a custom
port ``tcp:1235``) as a reference to validate the test script itself.
Run the test with the following command from your :envvar:`ZEPHYR_BASE` directory:
.. code-block:: console
./scripts/twister -p qemu_x86 -T tests/subsys/debug/gdbstub
The test should run successfully, and now let's do something similar step-by-step
to demonstrate how the Zephyr GDB stub works from the GDB user's perspective.
In the snippets below use and expect your appropriate directories instead of
``<SDK install directory>``, ``<build_directory>``, ``<ZEPHYR_BASE>``.
#. Open two terminal windows.
#. On the first terminal, build and run the test application:
.. zephyr-app-commands::
:zephyr-app: tests/subsys/debug/gdbstub
:host-os: unix
:board: qemu_x86
:gen-args: '-DCONFIG_QEMU_EXTRA_FLAGS="-serial tcp:localhost:5678,server"'
:goals: build run
Note how we set :kconfig:option:`CONFIG_QEMU_EXTRA_FLAGS` to direct QEMU serial
console port to the ``localhost`` TCP port ``5678`` to wait for a connection
from the GDB ``remote`` command we are going to do on the next steps.
#. On the second terminal, start GDB:
.. code-block:: bash
<SDK install directory>/x86_64-zephyr-elf/bin/x86_64-zephyr-elf-gdb
#. Tell GDB where to look for the built ELF file:
.. code-block:: text
(gdb) symbol-file <build directory>/zephyr/zephyr.elf
Response from GDB:
.. code-block:: text
Reading symbols from <build directory>/zephyr/zephyr.elf...
#. Tell GDB to connect to the Zephyr gdbstub serial backend which is exposed
earlier as a server through the TCP port ``-serial`` redirection at QEMU.
.. code-block:: text
(gdb) target remote localhost:5678
Response from GDB:
.. code-block:: text
Remote debugging using localhost:5678
arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:252
252 }
GDB also shows where the code execution is stopped. In this case,
it is at :zephyr_file:`arch/x86/core/ia32/gdbstub.c`, line 252.
#. Use command ``bt`` or ``backtrace`` to show the backtrace of stack frames.
.. code-block:: text
(gdb) bt
#0 arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:252
#1 0x00104140 in gdb_init () at <ZEPHYR_BASE>/zephyr/subsys/debug/gdbstub.c:852
#2 0x00109c13 in z_sys_init_run_level (level=INIT_LEVEL_PRE_KERNEL_2) at <ZEPHYR_BASE>/kernel/init.c:360
#3 0x00109e73 in z_cstart () at <ZEPHYR_BASE>/kernel/init.c:630
#4 0x00104422 in z_prep_c (arg=0x1245bc <x86_cpu_boot_arg>) at <ZEPHYR_BASE>/arch/x86/core/prep_c.c:80
#5 0x001000c9 in __csSet () at <ZEPHYR_BASE>/arch/x86/core/ia32/crt0.S:290
#6 0x001245bc in uart_dev ()
#7 0x00134988 in z_interrupt_stacks ()
#8 0x00000000 in ?? ()
#. Use command ``list`` to show the source code and surroundings where
code execution is stopped.
.. code-block:: text
(gdb) list
247 __asm__ volatile ("int3");
248
249 #ifdef CONFIG_GDBSTUB_TRACE
250 printk("gdbstub:%s GDB is connected\n", __func__);
251 #endif
252 }
253
254 /* Hook current IDT. */
255 _EXCEPTION_CONNECT_NOCODE(z_gdb_debug_isr, IV_DEBUG, 3);
256 _EXCEPTION_CONNECT_NOCODE(z_gdb_break_isr, IV_BREAKPOINT, 3);
#. Use command ``s`` or ``step`` to step through program until it reaches
a different source line. Now that it finished executing :c:func:`arch_gdb_init`
and is continuing in :c:func:`gdb_init`.
.. code-block:: text
(gdb) s
gdb_init () at <ZEPHYR_BASE>/subsys/debug/gdbstub.c:857
857 return 0;
.. code-block:: text
(gdb) list
852 arch_gdb_init();
853
854 #ifdef CONFIG_GDBSTUB_TRACE
855 printk("gdbstub:%s exit\n", __func__);
856 #endif
857 return 0;
858 }
859
860 #ifdef CONFIG_XTENSA
861 /*
#. Use command ``br`` or ``break`` to setup a breakpoint. For this example
set up a breakpoint at :c:func:`main`, and let code execution continue
without any intervention using command ``c`` (or ``continue``).
.. code-block:: text
(gdb) break main
Breakpoint 1 at 0x10064d: file <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c, line 27.
.. code-block:: text
(gdb) continue
Continuing.
Once code execution reaches :c:func:`main`, execution will be stopped
and GDB prompt returns.
.. code-block:: text
Breakpoint 1, main () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:27
27 printk("%s():enter\n", __func__);
Now GDB is waiting at the beginning of :c:func:`main`:
.. code-block:: text
(gdb) list
22
23 int main(void)
24 {
25 int ret;
26
27 printk("%s():enter\n", __func__);
28 ret = test();
29 printk("ret=%d\n", ret);
30 return 0;
31 }
#. To examine the value of ``ret``, the command ``p`` or ``print``
can be used.
.. code-block:: text
(gdb) p ret
$1 = 1273788
Since ``ret`` has not been initialized, it contains some random value.
#. If step (``s`` or ``step``) is used here, it will continue execution
skipping the interior of :c:func:`test`.
To examine code execution inside :c:func:`test`,
a breakpoint can be set for :c:func:`test`, or simply using
``si`` (or ``stepi``) to execute one machine instruction, where it has
the side effect of going into the function. The GDB command ``finish``
can be used to continue execution without intervention until the function
returns.
.. code-block:: text
(gdb) finish
Run till exit from #0 test () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:17
0x00100667 in main () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:28
28 ret = test();
Value returned is $2 = 30
#. Examine ``ret`` again which should have the return value from
:c:func:`test`. Sometimes, the assignment is not done until another
``step`` is issued, as in this case. This is due to the assignment
code is done after returning from function. The assignment code is
generated by the toolchain as machine instructions which are not
visible when viewing the corresponding C source file.
.. code-block:: text
(gdb) p ret
$3 = 1273788
(gdb) step
29 printk("ret=%d\n", ret);
(gdb) p ret
$4 = 30
#. If ``continue`` is issued here, code execution will continue indefinitely
as there are no breakpoints to further stop execution. Breaking execution
in GDB via :kbd:`Ctrl-C` does not currently work as the Zephyr gdbstub does
not support this functionality yet. Switch to the first console with QEMU
running the Zephyr image and stop it manually with :kbd:`Ctrl+a x`.
When the same test is executed by Twister, it automatically takes care of
stopping the QEMU instance.