gdbstub: doc: Update documentation with example

Update the Zephyr `gdbstub` documentation with its 'how to use'
example to align with former sample application replaced by the test.

Fixes: zephyrproject-rtos#70966

Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
This commit is contained in:
Dmitrii Golovanov 2024-04-03 17:58:17 +02:00 committed by Carles Cufí
commit 3614d4cb88

View file

@ -17,7 +17,7 @@ using GDB.
The protocol supports different connection types: serial, UDP/IP and The protocol supports different connection types: serial, UDP/IP and
TCP/IP. Zephyr currently supports only serial device communication. TCP/IP. Zephyr currently supports only serial device communication.
The GDB program acts as the client while Zephyr acts as the 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 server. When this feature is enabled, Zephyr stops its execution after
:c:func:`gdb_init` starts gdbstub service and waits for a GDB :c:func:`gdb_init` starts gdbstub service and waits for a GDB
connection. Once a connection is established it is possible to connection. Once a connection is established it is possible to
@ -87,20 +87,39 @@ Using Serial Backend
Example Example
******* *******
This is an example to demonstrate how GDB stub works. There is a test application :zephyr_file:`tests/subsys/debug/gdbstub` with one of its
You can also refer to ``tests/subsys/debug/gdbstub`` test cases ``debug.gdbstub.breakpoints`` demonstrating how the Zephyr GDB stub can be used.
for its implementation as a Twister test. 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. #. Open two terminal windows.
#. On the first terminal, build and run the sample: #. On the first terminal, build and run the test application:
.. zephyr-app-commands:: .. zephyr-app-commands::
:zephyr-app: samples/subsys/debug/gdbstub :zephyr-app: tests/subsys/debug/gdbstub
:host-os: unix :host-os: unix
:board: qemu_x86 :board: qemu_x86
:gen-args: '-DCONFIG_QEMU_EXTRA_FLAGS="-serial tcp:localhost:5678,server"'
:goals: build run :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: #. On the second terminal, start GDB:
.. code-block:: bash .. code-block:: bash
@ -111,7 +130,7 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
(gdb) file <build directory>/zephyr/zephyr.elf (gdb) symbol-file <build directory>/zephyr/zephyr.elf
Response from GDB: Response from GDB:
@ -119,38 +138,38 @@ for its implementation as a Twister test.
Reading symbols from <build directory>/zephyr/zephyr.elf... Reading symbols from <build directory>/zephyr/zephyr.elf...
#. Tell GDB to connect to the server: #. 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 .. code-block:: text
(gdb) target remote localhost:5678 (gdb) target remote localhost:5678
Note that QEMU is setup to redirect the serial used for GDB stub in
the Zephyr image to a networking port. Hence the connection to
localhost, port 5678.
Response from GDB: Response from GDB:
.. code-block:: text .. code-block:: text
Remote debugging using :5678 Remote debugging using localhost:5678
arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:232 arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:252
232 } 252 }
GDB also shows where the code execution is stopped. In this case, GDB also shows where the code execution is stopped. In this case,
it is at :file:`arch/x86/core/ia32/gdbstub.c`, line 232. 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. #. Use command ``bt`` or ``backtrace`` to show the backtrace of stack frames.
.. code-block:: text .. code-block:: text
(gdb) bt (gdb) bt
#0 arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:232 #0 arch_gdb_init () at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:252
#1 0x00105068 in gdb_init (arg=0x0) at <ZEPHYR_BASE>/subsys/debug/gdbstub.c:833 #1 0x00104140 in gdb_init () at <ZEPHYR_BASE>/zephyr/subsys/debug/gdbstub.c:852
#2 0x00109d6f in z_sys_init_run_level (level=0x1) at <ZEPHYR_BASE>/kernel/device.c:72 #2 0x00109c13 in z_sys_init_run_level (level=INIT_LEVEL_PRE_KERNEL_2) at <ZEPHYR_BASE>/kernel/init.c:360
#3 0x0010a40b in z_cstart () at <ZEPHYR_BASE>/kernel/init.c:423 #3 0x00109e73 in z_cstart () at <ZEPHYR_BASE>/kernel/init.c:630
#4 0x00105383 in z_prep_c (arg=0x9500) at <ZEPHYR_BASE>/arch/x86/core/prep_c.c:58 #4 0x00104422 in z_prep_c (arg=0x1245bc <x86_cpu_boot_arg>) at <ZEPHYR_BASE>/arch/x86/core/prep_c.c:80
#5 0x001000a9 in __csSet () at <ZEPHYR_BASE>/arch/x86/core/ia32/crt0.S:273 #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 #. Use command ``list`` to show the source code and surroundings where
code execution is stopped. code execution is stopped.
@ -158,16 +177,16 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
(gdb) list (gdb) list
227 } 247 __asm__ volatile ("int3");
228 248
229 void arch_gdb_init(void) 249 #ifdef CONFIG_GDBSTUB_TRACE
230 { 250 printk("gdbstub:%s GDB is connected\n", __func__);
231 __asm__ volatile ("int3"); 251 #endif
232 } 252 }
233 253
234 /* Hook current IDT. */ 254 /* Hook current IDT. */
235 _EXCEPTION_CONNECT_NOCODE(z_gdb_debug_isr, IV_DEBUG, 3); 255 _EXCEPTION_CONNECT_NOCODE(z_gdb_debug_isr, IV_DEBUG, 3);
236 _EXCEPTION_CONNECT_NOCODE(z_gdb_break_isr, IV_BREAKPOINT, 3); 256 _EXCEPTION_CONNECT_NOCODE(z_gdb_break_isr, IV_BREAKPOINT, 3);
#. Use command ``s`` or ``step`` to step through program until it reaches #. 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` a different source line. Now that it finished executing :c:func:`arch_gdb_init`
@ -176,31 +195,34 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
(gdb) s (gdb) s
gdb_init (arg=0x0) at /home/dleung5/zephyr/rtos/zephyr/subsys/debug/gdbstub.c:834 gdb_init () at <ZEPHYR_BASE>/subsys/debug/gdbstub.c:857
834 return 0; 857 return 0;
.. code-block:: text .. code-block:: text
(gdb) list (gdb) list
829 LOG_ERR("Could not initialize gdbstub backend."); 852 arch_gdb_init();
830 return -1; 853
831 } 854 #ifdef CONFIG_GDBSTUB_TRACE
832 855 printk("gdbstub:%s exit\n", __func__);
833 arch_gdb_init(); 856 #endif
834 return 0; 857 return 0;
835 } 858 }
836 859
837 #ifdef CONFIG_XTENSA 860 #ifdef CONFIG_XTENSA
838 /* 861 /*
#. Use command ``br`` or ``break`` to setup a breakpoint. This example #. Use command ``br`` or ``break`` to setup a breakpoint. For this example
sets up a breakpoint at :c:func:`main`, and let code execution continue set up a breakpoint at :c:func:`main`, and let code execution continue
without any intervention using command ``c`` (or ``continue``). without any intervention using command ``c`` (or ``continue``).
.. code-block:: text .. code-block:: text
(gdb) break main (gdb) break main
Breakpoint 1 at 0x1005a9: file <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c, line 32. Breakpoint 1 at 0x10064d: file <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c, line 27.
.. code-block:: text
(gdb) continue (gdb) continue
Continuing. Continuing.
@ -209,24 +231,24 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
Breakpoint 1, main () at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:32 Breakpoint 1, main () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:27
32 ret = test(); 27 printk("%s():enter\n", __func__);
Now GDB is waiting at the beginning of :c:func:`main`: Now GDB is waiting at the beginning of :c:func:`main`:
.. code-block:: text .. code-block:: text
(gdb) list (gdb) list
27 22
28 int main(void) 23 int main(void)
29 { 24 {
30 int ret; 25 int ret;
31 26
32 ret = test(); 27 printk("%s():enter\n", __func__);
33 printk("%d\n", ret); 28 ret = test();
34 } 29 printk("ret=%d\n", ret);
35 30 return 0;
36 K_THREAD_DEFINE(thread, STACKSIZE, thread_entry, NULL, NULL, NULL, 31 }
#. To examine the value of ``ret``, the command ``p`` or ``print`` #. To examine the value of ``ret``, the command ``p`` or ``print``
can be used. can be used.
@ -234,48 +256,26 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
(gdb) p ret (gdb) p ret
$1 = 0x11318c $1 = 1273788
Since ``ret`` has not been assigned a value yet, what it contains is Since ``ret`` has not been initialized, it contains some random value.
simply a random value.
#. If step (``s`` or ``step``) is used here, it will continue execution #. If step (``s`` or ``step``) is used here, it will continue execution
until :c:func:`printk` is reached, thus skipping the interior of skipping the interior of :c:func:`test`.
:c:func:`test`. To examine code execution inside :c:func:`test`, To examine code execution inside :c:func:`test`,
a breakpoint can be set for :c:func:`test`, or simply using a breakpoint can be set for :c:func:`test`, or simply using
``si`` (or ``stepi``) to execute one machine instruction, where it has ``si`` (or ``stepi``) to execute one machine instruction, where it has
the side effect of going into the function. the side effect of going into the function. The GDB command ``finish``
can be used to continue execution without intervention until the function
.. code-block:: text returns.
(gdb) si
test () at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:13
13 {
(gdb) list
8 #include <zephyr/sys/printk.h>
9
10 #define STACKSIZE 512
11
12 static int test(void)
13 {
14 int a;
15 int b;
16
17 a = 10;
#. Here, ``step`` can be used to go through all code inside :c:func:`test`
until it returns. Or the command ``finish`` can be used to continue
execution without intervention until the function returns.
.. code-block:: text .. code-block:: text
(gdb) finish (gdb) finish
Run till exit from #0 test () at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:13 Run till exit from #0 test () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:17
0x001005ae in main () at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:32 0x00100667 in main () at <ZEPHYR_BASE>/tests/subsys/debug/gdbstub/src/main.c:28
32 ret = test(); 28 ret = test();
Value returned is $2 = 0x1e Value returned is $2 = 30
And now, execution is back to :c:func:`main`.
#. Examine ``ret`` again which should have the return value from #. Examine ``ret`` again which should have the return value from
:c:func:`test`. Sometimes, the assignment is not done until another :c:func:`test`. Sometimes, the assignment is not done until another
@ -287,13 +287,16 @@ for its implementation as a Twister test.
.. code-block:: text .. code-block:: text
(gdb) p ret (gdb) p ret
$3 = 0x11318c $3 = 1273788
(gdb) s (gdb) step
33 printk("%d\n", ret); 29 printk("ret=%d\n", ret);
(gdb) p ret (gdb) p ret
$4 = 0x1e $4 = 30
#. If ``continue`` is issued here, code execution will continue indefinitely #. If ``continue`` is issued here, code execution will continue indefinitely
as there are no breakpoints to further stop execution. Breaking execution as there are no breakpoints to further stop execution. Breaking execution
in GDB via Ctrl-C does not currently work as the GDB stub does not in GDB via :kbd:`Ctrl-C` does not currently work as the Zephyr gdbstub does
support this functionality (yet). 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.