From 3614d4cb88f4714c47af66d7053304092659fb43 Mon Sep 17 00:00:00 2001 From: Dmitrii Golovanov Date: Wed, 3 Apr 2024 17:58:17 +0200 Subject: [PATCH] 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 --- doc/services/debugging/gdbstub.rst | 199 +++++++++++++++-------------- 1 file changed, 101 insertions(+), 98 deletions(-) diff --git a/doc/services/debugging/gdbstub.rst b/doc/services/debugging/gdbstub.rst index c2062ec304d..67282749bb6 100644 --- a/doc/services/debugging/gdbstub.rst +++ b/doc/services/debugging/gdbstub.rst @@ -17,7 +17,7 @@ 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 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 :c:func:`gdb_init` starts gdbstub service and waits for a GDB connection. Once a connection is established it is possible to @@ -87,20 +87,39 @@ Using Serial Backend Example ******* -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. +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 +````, ````, ````. + #. 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: samples/subsys/debug/gdbstub + :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 @@ -111,7 +130,7 @@ for its implementation as a Twister test. .. code-block:: text - (gdb) file /zephyr/zephyr.elf + (gdb) symbol-file /zephyr/zephyr.elf Response from GDB: @@ -119,38 +138,38 @@ for its implementation as a Twister test. Reading symbols from /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 (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: .. code-block:: text - Remote debugging using :5678 - arch_gdb_init () at /arch/x86/core/ia32/gdbstub.c:232 - 232 } + Remote debugging using localhost:5678 + arch_gdb_init () at /arch/x86/core/ia32/gdbstub.c:252 + 252 } 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. .. code-block:: text (gdb) bt - #0 arch_gdb_init () at /arch/x86/core/ia32/gdbstub.c:232 - #1 0x00105068 in gdb_init (arg=0x0) at /subsys/debug/gdbstub.c:833 - #2 0x00109d6f in z_sys_init_run_level (level=0x1) at /kernel/device.c:72 - #3 0x0010a40b in z_cstart () at /kernel/init.c:423 - #4 0x00105383 in z_prep_c (arg=0x9500) at /arch/x86/core/prep_c.c:58 - #5 0x001000a9 in __csSet () at /arch/x86/core/ia32/crt0.S:273 + #0 arch_gdb_init () at /arch/x86/core/ia32/gdbstub.c:252 + #1 0x00104140 in gdb_init () at /zephyr/subsys/debug/gdbstub.c:852 + #2 0x00109c13 in z_sys_init_run_level (level=INIT_LEVEL_PRE_KERNEL_2) at /kernel/init.c:360 + #3 0x00109e73 in z_cstart () at /kernel/init.c:630 + #4 0x00104422 in z_prep_c (arg=0x1245bc ) at /arch/x86/core/prep_c.c:80 + #5 0x001000c9 in __csSet () at /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. @@ -158,16 +177,16 @@ for its implementation as a Twister test. .. code-block:: text (gdb) list - 227 } - 228 - 229 void arch_gdb_init(void) - 230 { - 231 __asm__ volatile ("int3"); - 232 } - 233 - 234 /* Hook current IDT. */ - 235 _EXCEPTION_CONNECT_NOCODE(z_gdb_debug_isr, IV_DEBUG, 3); - 236 _EXCEPTION_CONNECT_NOCODE(z_gdb_break_isr, IV_BREAKPOINT, 3); + 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` @@ -176,31 +195,34 @@ for its implementation as a Twister test. .. code-block:: text (gdb) s - gdb_init (arg=0x0) at /home/dleung5/zephyr/rtos/zephyr/subsys/debug/gdbstub.c:834 - 834 return 0; + gdb_init () at /subsys/debug/gdbstub.c:857 + 857 return 0; .. code-block:: text (gdb) list - 829 LOG_ERR("Could not initialize gdbstub backend."); - 830 return -1; - 831 } - 832 - 833 arch_gdb_init(); - 834 return 0; - 835 } - 836 - 837 #ifdef CONFIG_XTENSA - 838 /* + 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. This example - sets up a breakpoint at :c:func:`main`, and let code execution continue + #. 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 0x1005a9: file /samples/subsys/debug/gdbstub/src/main.c, line 32. + Breakpoint 1 at 0x10064d: file /tests/subsys/debug/gdbstub/src/main.c, line 27. + + .. code-block:: text + (gdb) continue Continuing. @@ -209,24 +231,24 @@ for its implementation as a Twister test. .. code-block:: text - Breakpoint 1, main () at /samples/subsys/debug/gdbstub/src/main.c:32 - 32 ret = test(); + Breakpoint 1, main () at /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 - 27 - 28 int main(void) - 29 { - 30 int ret; - 31 - 32 ret = test(); - 33 printk("%d\n", ret); - 34 } - 35 - 36 K_THREAD_DEFINE(thread, STACKSIZE, thread_entry, NULL, NULL, NULL, + 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. @@ -234,48 +256,26 @@ for its implementation as a Twister test. .. code-block:: text (gdb) p ret - $1 = 0x11318c + $1 = 1273788 - Since ``ret`` has not been assigned a value yet, what it contains is - simply a random value. + Since ``ret`` has not been initialized, it contains some random value. #. If step (``s`` or ``step``) is used here, it will continue execution - until :c:func:`printk` is reached, thus skipping the interior of - :c:func:`test`. To examine code execution inside :c:func:`test`, + 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. - - .. code-block:: text - - (gdb) si - test () at /samples/subsys/debug/gdbstub/src/main.c:13 - 13 { - (gdb) list - 8 #include - 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. + 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 /samples/subsys/debug/gdbstub/src/main.c:13 - 0x001005ae in main () at /samples/subsys/debug/gdbstub/src/main.c:32 - 32 ret = test(); - Value returned is $2 = 0x1e - - And now, execution is back to :c:func:`main`. + Run till exit from #0 test () at /tests/subsys/debug/gdbstub/src/main.c:17 + 0x00100667 in main () at /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 @@ -287,13 +287,16 @@ for its implementation as a Twister test. .. code-block:: text (gdb) p ret - $3 = 0x11318c - (gdb) s - 33 printk("%d\n", ret); + $3 = 1273788 + (gdb) step + 29 printk("ret=%d\n", ret); (gdb) p ret - $4 = 0x1e + $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 Ctrl-C does not currently work as the GDB stub does not - support this functionality (yet). + 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.