Documentation: improve debugging section
This commit is contained in:
parent
9f6a43d0b0
commit
c91939b5c0
@ -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,89 +74,28 @@ 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
|
||||
---------------------------------------
|
||||
|
||||
.. 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.
|
||||
|
||||
If your board does not have an embedded programmer and uses
|
||||
`JTAG <https://en.wikipedia.org/wiki/JTAG>`_ connector instead,
|
||||
things are a bit different. This guide assumes you have a JTAG hardware debugger like a
|
||||
`Segger J-Link <https://www.segger.com/products/debug-probes/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.
|
||||
|
||||
#. Attach the Debugger Cables
|
||||
|
||||
#. Start the Debugger
|
||||
|
||||
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:: console
|
||||
|
||||
$ JLinkGDBServer -device ATSAMA5D27 -if JTAG -speed 1000 -JTAGConf -1,-1
|
||||
|
||||
#. Launch the GNU Debugger
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cd nuttx/
|
||||
$ gdb-multiarch nuttx/nuttx
|
||||
|
||||
#. Set gdb to talk with the J-Link
|
||||
|
||||
::
|
||||
|
||||
(gdb) target extended-remote :2331
|
||||
|
||||
#. Reset the board
|
||||
|
||||
::
|
||||
.. code-block::
|
||||
|
||||
(gdb) mon reset
|
||||
|
||||
#. 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).
|
||||
To halt the board:
|
||||
|
||||
#. Halt the board
|
||||
|
||||
::
|
||||
.. code-block::
|
||||
|
||||
(gdb) mon halt
|
||||
|
||||
#. Load nuttx
|
||||
To set a breakpoint:
|
||||
|
||||
::
|
||||
|
||||
(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)
|
||||
|
||||
#. Set a breakpoint
|
||||
|
||||
::
|
||||
.. code-block::
|
||||
|
||||
(gdb) breakpoint nsh_main
|
||||
|
||||
#. Start nuttx
|
||||
and to finally start nuttx:
|
||||
|
||||
::
|
||||
.. code-block::
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
@ -165,15 +105,164 @@ You can load code, start, stop, step through the program, and examine variables
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Debugging Shortcuts
|
||||
-------------------
|
||||
.. tip::
|
||||
|
||||
Note that you can abbreviate ``gdb`` commands, ``info b`` is a shortcut for
|
||||
``information breakpoints``; ``c`` works the same as ``continue``, etc.
|
||||
You can abbreviate ``gdb`` commands: ``info b`` is a shortcut for
|
||||
``information breakpoints``; ``c`` works the same as ``continue``, etc.
|
||||
|
||||
See this article for more info:
|
||||
`Debugging a Apache NuttX target with GDB and OpenOCD <https://micro-ros.github.io/docs/tutorials/advanced/nuttx/debugging/>`_.
|
||||
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``).
|
||||
|
||||
With openocd
|
||||
~~~~~~~~~~~~
|
||||
|
||||
``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.
|
||||
|
||||
Finally, to enable NuttX integration, you need to supply an additional ``openocd`` argument:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ 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::
|
||||
|
||||
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:
|
||||
|
||||
.. 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:
|
||||
|
||||
.. code-block::
|
||||
|
||||
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 <transport>'.
|
||||
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:
|
||||
|
||||
.. code-block::
|
||||
|
||||
(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 <nx_start_application+10>
|
||||
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
|
||||
~~~~~~~~
|
||||
|
||||
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``).
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ 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:
|
||||
|
||||
.. 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`.
|
||||
|
Loading…
Reference in New Issue
Block a user