From c91939b5c0b7713153969577375d59fa742e682f Mon Sep 17 00:00:00 2001 From: Matias N Date: Mon, 23 Nov 2020 16:05:34 -0300 Subject: [PATCH] Documentation: improve debugging section --- Documentation/quickstart/debugging.rst | 237 +++++++++++++++++-------- 1 file changed, 163 insertions(+), 74 deletions(-) diff --git a/Documentation/quickstart/debugging.rst b/Documentation/quickstart/debugging.rst index 299326b73c..cc3489eca8 100644 --- a/Documentation/quickstart/debugging.rst +++ b/Documentation/quickstart/debugging.rst @@ -1,6 +1,7 @@ .. include:: /substitutions.rst .. _debugging: +========= Debugging ========= @@ -9,7 +10,7 @@ to use debugging techniques to understand how the system works. Two tools that a debugging using the GNU Debugger (gdb). Debug Logging -------------- +============= NuttX has a powerful system logging facility (syslog) with ``info``, ``warn``, and ``error`` levels. You can enable debugging for your build for the subsystem or feature by using the ``menuconfig`` system. @@ -17,15 +18,15 @@ debugging for your build for the subsystem or feature by using the ``menuconfig` The debug options are available under :menuselection:`Build Setup --> Debug Options`. You will most likely have to enable the following options: -* ``Enable Debug Features`` — selecting this will turn on subsystem-level debugging options, they will become visible +* :menuselection:`Enable Debug Features` — selecting this will turn on subsystem-level debugging options, they will become visible on the page below. You can then select the ones you want. -* ``Enable Error Output`` — this will only log errors. -* ``Enable Warnings Output`` — this will log warnings and errors. -* ``Enable Informational Debug Output`` — this will produce informational output, warnings, and errors. +* :menuselection:`Enable Error Output` — this will only log errors. +* :menuselection:`Enable Warnings Output` — this will log warnings and errors. +* :menuselection:`Enable Informational Debug Output` — this will produce informational output, warnings, and errors. You can then select from the subsystems that are available, Network, Scheduler, USB, etc. Note that you will need to separately enable the subsystem elsewhere in the ``menuconfig`` system. To see the ``CONFIG`` define that is set, -use the arrow keys to highlight the subsystem (for instance, ``Network Debug Features``) and type '?'. This will show +use the arrow keys to highlight the subsystem (for instance, :menuselection:`Network Debug Features`) and type :kbd:`?`. This will show you that the C macro that is set is called ``CONFIG_DEBUG_NET``. ``debug.h`` defines the ``netinfo()`` logging function that will log output if this macro is set. You can search the source code for ``netinfo`` to see how it is used. @@ -51,7 +52,7 @@ There are also subsystems that enable USB trace debugging, and you can log to me faster than what the console can output. Debugging with ``openocd`` and ``gdb`` --------------------------------------- +====================================== To debug our Nucleo board using its embedded SWD debug adapter, start ``openocd`` with the following command: @@ -73,107 +74,195 @@ Inside ``gdb`` console, connect to the ``gdb`` server with: (gdb) target extended-remote :3333 -You can now use standard ``gdb`` commands. +You can now use standard ``gdb`` commands. For example, to +reset the board: -Debugging with an external JTAG adapter ---------------------------------------- +.. code-block:: -.. todo:: - Explain this with openocd. It gives the impression that JTAG requires - a specific tool. Also, some of the example commands apply to both cases. - This repeats some of the above. + (gdb) mon reset -If your board does not have an embedded programmer and uses -`JTAG `_ connector instead, -things are a bit different. This guide assumes you have a JTAG hardware debugger like a -`Segger J-Link `_. -JTAG is a set of standards that let you -attach a hardware device to your embedded board, and then remotely control the CPU. -You can load code, start, stop, step through the program, and examine variables and memory. +To halt the board: -#. Attach the Debugger Cables +.. code-block:: -#. Start the Debugger + (gdb) mon halt + +To set a breakpoint: - Refer to your JTAG debugger's documentation for information on how to start a GDB Server process that gdb can - communicate with to load code and start, stop, and step the embedded board's CPU. Your command line may be - different from this one. +.. code-block:: - .. code-block:: console + (gdb) breakpoint nsh_main - $ JLinkGDBServer -device ATSAMA5D27 -if JTAG -speed 1000 -JTAGConf -1,-1 +and to finally start nuttx: -#. Launch the GNU Debugger +.. code-block:: - In another terminal window, launch the GDB. In the case of this guide, this came with the - ARM Embedded GNU Toolchain we downloaded in the Install step. + (gdb) continue + Continuing. - .. code-block:: console + Breakpoint 1, nsh_main (argc=1, argv=0x200ddfac) at nsh_main.c:208 + 208 sched_getparam(0, ¶m); + (gdb) continue + Continuing. + +.. tip:: - $ cd nuttx/ - $ gdb-multiarch nuttx/nuttx + You can abbreviate ``gdb`` commands: ``info b`` is a shortcut for + ``information breakpoints``; ``c`` works the same as ``continue``, etc. -#. Set gdb to talk with the J-Link +NuttX aware debugging +--------------------- - :: +Since NuttX is actually an RTOS, it is useful to have ``gdb`` be aware of the different +tasks/threads that are running. There are two ways to do this: via ``openocd`` +itself or via ``gdb``. Note that in both cases, you need to enable debug symbols +(``CONFIG_DEBUG_SYMBOLS``). - (gdb) target extended-remote :2331 +With openocd +~~~~~~~~~~~~ -#. Reset the board +``openocd`` supports various RTOS directly, including NuttX. It works by reading +into internal NuttX symbols which define the active tasks and their properties. +As a result, the ``gdb`` server will directly be aware of each task as a different +`thread`. The downside of this approach is that it depends on how you build NuttX +as there are some options hardcoded into +opencd. By default, it assumes: - :: + * ``CONFIG_DISABLE_MQUEUE=y`` + * ``CONFIG_PAGING=n`` + +If you need these options to be set differently, you will have to edit ``./src/rtos/nuttx_header.h`` from ``openocd``, +change the corresponding settings and then rebuild it. - (gdb) mon reset +Finally, to enable NuttX integration, you need to supply an additional ``openocd`` argument: -#. You may need to switch to the serial console to hit a key to stop the board from booting from its boot monitor - (U-Boot, in the case of the SAMA5 boards from Microchip). +.. code-block:: console -#. Halt the board + $ openocd -f interface/st-link-v2.cfg -f target/stm32f1x.cfg -c '$_TARGETNAME configure -rtos nuttx' + +Since ``openocd`` also needs to know the memory layout of certain datastructures, you need to have ``gdb`` +run the following commands once the ``nuttx`` binary is loaded: - :: +.. code-block:: - (gdb) mon halt + eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid + eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs + eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state + eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name + eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name) + +One way to do this is to define a gdb `hook` function that will be called when running ``file`` command: -#. Load nuttx +.. code-block:: - :: + define hookpost-file + eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid + eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs + eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state + eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name + eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name) + end + +You will see that ``openocd`` has received the memory offsets in its output: - (gdb) load nuttx - `/home/adamf/src/nuttx-sama5d36-xplained/nuttx/nuttx' has changed; re-reading symbols. - Loading section .text, size 0x9eae4 lma 0x20008000 - Loading section .ARM.exidx, size 0x8 lma 0x200a6ae4 - Loading section .data, size 0x125c lma 0x200a6aec - Start address 0x20008040, load size 654664 - Transfer rate: 75 KB/sec, 15587 bytes/write. - (gdb) +.. code-block:: -#. Set a breakpoint + Open On-Chip Debugger 0.10.0+dev-01514-ga8edbd020-dirty (2020-11-20-14:23) + Licensed under GNU GPL v2 + For bug reports, read + http://openocd.org/doc/doxygen/bugs.html + Info : auto-selecting first available session transport "swd". To override use 'transport select '. + Info : target type name = cortex_m + Info : Listening on port 6666 for tcl connections + Info : Listening on port 4444 for telnet connections + 15:41:23: Debugging starts + Info : CMSIS-DAP: SWD Supported + Info : CMSIS-DAP: FW Version = 1.10 + Info : CMSIS-DAP: Interface Initialised (SWD) + Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1 + Info : CMSIS-DAP: Interface ready + Info : clock speed 1000 kHz + Info : SWD DPIDR 0x2ba01477 + Info : nrf52.cpu: hardware has 6 breakpoints, 4 watchpoints + Info : starting gdb server for nrf52.cpu on 3333 + Info : Listening on port 3333 for gdb connections + Info : accepting 'gdb' connection on tcp/3333 + Error: No symbols for NuttX + Info : nRF52832-QFAA(build code: B0) 512kB Flash, 64kB RAM + undefined debug reason 8 - target needs reset + Warn : Prefer GDB command "target extended-remote 3333" instead of "target remote 3333" + Info : pid_offset: 12 + Info : xcpreg_offset: 132 + Info : state_offset: 26 + Info : name_offset: 208 + Info : name_size: 32 + target halted due to debug-request, current mode: Thread + xPSR: 0x01000000 pc: 0x000000dc msp: 0x20000cf0 + target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x000000dc msp: 0x20000cf0 + +.. note:: You will probably see the ``Error: No symbols for NuttX`` error appear once at startup. This is OK + unless you see it every time you step the debugger. In this case, it would mean you did not enable debug symbols. - :: +Now, You can now inspect threads: - (gdb) breakpoint nsh_main +.. code-block:: -#. Start nuttx + (gdb) info threads + Id Target Id Frame + * 1 Remote target nx_start_application () at init/nx_bringup.c:261 + (gdb) info registers + r0 0x0 0 + r1 0x2f 47 + r2 0x0 0 + r3 0x0 0 + r4 0x0 0 + r5 0x0 0 + r6 0x0 0 + r7 0x20000ca0 536874144 + r8 0x0 0 + r9 0x0 0 + r10 0x0 0 + r11 0x0 0 + r12 0x9 9 + sp 0x20000c98 0x20000c98 + lr 0x19c5 6597 + pc 0x1996 0x1996 + xPSR 0x41000000 1090519040 + fpscr 0x0 0 + msp 0x20000c98 0x20000c98 + psp 0x0 0x0 <_vectors> + primask 0x0 0 + basepri 0xe0 -32 + faultmask 0x0 0 + control 0x0 0 - :: +With gdb +~~~~~~~~ - (gdb) continue - Continuing. +You can also do NuttX aware debugging using ``gdb`` scripting support. +The benefit is that it works also for the sim build where ``openocd`` is +not applicable. For this to work, you will need to enable PROC filesystem support +which will expose required task information (``CONFIG_FS_PROCFS=y``). - Breakpoint 1, nsh_main (argc=1, argv=0x200ddfac) at nsh_main.c:208 - 208 sched_getparam(0, ¶m); - (gdb) continue - Continuing. +To use this approach, you can load the ``nuttx/tools/nuttx-gdbinit`` file. An +easy way to do this is to create a symbolic link: -Debugging Shortcuts -------------------- +.. code-block:: console -Note that you can abbreviate ``gdb`` commands, ``info b`` is a shortcut for -``information breakpoints``; ``c`` works the same as ``continue``, etc. + $ cd $HOME + $ ln -s nuttx/tools/nuttx-gdbinit .gdbinit + +This way whenever gdb is started it will run the appropriate commands. To inspect +the threads you can now use the following ``gdb`` command: -See this article for more info: -`Debugging a Apache NuttX target with GDB and OpenOCD `_. +.. code-block:: ----- + (gdb) info_nxthreads + target examined + _target_arch.name=armv7e-m + $_target_has_fpu : 0 + $_target_has_smp : 0 + saved current_tcb (pid=0) + * 0 Thread 0x20000308 (Name: Idle Task, State: Running, Priority: 0) 0xdc in __start() + 1 Thread 0x20001480 (Name: init, State: Waiting,Semaphore, Priority: 100) 0x7e08 in arm_switchcontext() -Next up is :ref:`organization`.