Documentation: improve debugging section
This commit is contained in:
parent
9f6a43d0b0
commit
c91939b5c0
@ -1,6 +1,7 @@
|
|||||||
.. include:: /substitutions.rst
|
.. include:: /substitutions.rst
|
||||||
.. _debugging:
|
.. _debugging:
|
||||||
|
|
||||||
|
=========
|
||||||
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).
|
debugging using the GNU Debugger (gdb).
|
||||||
|
|
||||||
Debug Logging
|
Debug Logging
|
||||||
-------------
|
=============
|
||||||
|
|
||||||
NuttX has a powerful system logging facility (syslog) with ``info``, ``warn``, and ``error`` levels. You can enable
|
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.
|
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
|
The debug options are available under :menuselection:`Build Setup --> Debug Options`. You will most likely have to enable the
|
||||||
following options:
|
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.
|
on the page below. You can then select the ones you want.
|
||||||
* ``Enable Error Output`` — this will only log errors.
|
* :menuselection:`Enable Error Output` — this will only log errors.
|
||||||
* ``Enable Warnings Output`` — this will log warnings and errors.
|
* :menuselection:`Enable Warnings Output` — this will log warnings and errors.
|
||||||
* ``Enable Informational Debug Output`` — this will produce informational output, 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
|
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,
|
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
|
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
|
function that will log output if this macro is set. You can search the source code for ``netinfo`` to see how it is
|
||||||
used.
|
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.
|
faster than what the console can output.
|
||||||
|
|
||||||
Debugging with ``openocd`` and ``gdb``
|
Debugging with ``openocd`` and ``gdb``
|
||||||
--------------------------------------
|
======================================
|
||||||
|
|
||||||
To debug our Nucleo board using its embedded SWD debug adapter,
|
To debug our Nucleo board using its embedded SWD debug adapter,
|
||||||
start ``openocd`` with the following command:
|
start ``openocd`` with the following command:
|
||||||
@ -73,107 +74,195 @@ Inside ``gdb`` console, connect to the ``gdb`` server with:
|
|||||||
|
|
||||||
(gdb) target extended-remote :3333
|
(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::
|
(gdb) mon reset
|
||||||
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
|
To halt the board:
|
||||||
`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
|
.. 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
|
.. code-block::
|
||||||
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
|
(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
|
(gdb) continue
|
||||||
ARM Embedded GNU Toolchain we downloaded in the Install step.
|
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/
|
You can abbreviate ``gdb`` commands: ``info b`` is a shortcut for
|
||||||
$ gdb-multiarch nuttx/nuttx
|
``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
|
.. code-block:: console
|
||||||
(U-Boot, in the case of the SAMA5 boards from Microchip).
|
|
||||||
|
|
||||||
#. 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
|
.. code-block::
|
||||||
`/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
|
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:
|
||||||
|
|
||||||
(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 <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
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
(gdb) continue
|
You can also do NuttX aware debugging using ``gdb`` scripting support.
|
||||||
Continuing.
|
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
|
To use this approach, you can load the ``nuttx/tools/nuttx-gdbinit`` file. An
|
||||||
208 sched_getparam(0, ¶m);
|
easy way to do this is to create a symbolic link:
|
||||||
(gdb) continue
|
|
||||||
Continuing.
|
|
||||||
|
|
||||||
Debugging Shortcuts
|
.. code-block:: console
|
||||||
-------------------
|
|
||||||
|
|
||||||
Note that you can abbreviate ``gdb`` commands, ``info b`` is a shortcut for
|
$ cd $HOME
|
||||||
``information breakpoints``; ``c`` works the same as ``continue``, etc.
|
$ 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:
|
.. code-block::
|
||||||
`Debugging a Apache NuttX target with GDB and OpenOCD <https://micro-ros.github.io/docs/tutorials/advanced/nuttx/debugging/>`_.
|
|
||||||
|
|
||||||
----
|
(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