123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- .. include:: /substitutions.rst
- .. _debugging:
- =========
- Debugging
- =========
- Finding and fixing bugs is an important part of the hardware and software development process. Sometimes you also need
- to use debugging techniques to understand how the system works. Two tools that are helpful are debug logging and
- 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.
- The debug options are available under :menuselection:`Build Setup --> Debug Options`. You will most likely have to enable the
- following options:
- * :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.
- * :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, :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.
- .. image:: ../_static/images/menuconfig-debug.png
- :width: 800px
- :align: center
- :alt: Screenshot of menuconfig system main screen
- Note that enabling all these will produce an incredible amount of logging output. Enable the level you want and
- the area you're interested in, and leave the rest disabled, save the config, and then recompile. You can see the full
- list of debug feature logging functions in the file
- `debug.h <https://github.com/apache/incubator-nuttx/blob/master/include/debug.h>`__.
- Syslog timestamps can be enabled in the configuration in :menuselection:`Device Drivers --> System Logging --> Prepend
- timestamp to syslog message` (``CONFIG_SYSLOG_TIMESTAMP``).
- You may need to do a little bit of experimenting to find the combination of logging settings that work for the problem
- you're trying to solve. See the file `debug.h <https://github.com/apache/incubator-nuttx/blob/master/include/debug.h>`_
- for available debug settings that are available.
- There are also subsystems that enable USB trace debugging, and you can log to memory too, if you need the logging to be
- 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:
- .. code-block:: console
- $ openocd -f interface/st-link-v2.cfg -f target/stm32f1x.cfg
- This will start a ``gdb`` server. Then, start ``gdb`` with:
- .. code-block:: console
- $ cd nuttx/
- $ gdb-multiarch nuttx/nuttx
- Inside ``gdb`` console, connect to the ``gdb`` server with:
- .. code-block::
- (gdb) target extended-remote :3333
- You can now use standard ``gdb`` commands. For example, to
- reset the board:
- .. code-block::
- (gdb) mon reset
- To halt the board:
- .. code-block::
- (gdb) mon halt
-
- To set a breakpoint:
- .. code-block::
- (gdb) breakpoint nsh_main
- and to finally start nuttx:
- .. code-block::
- (gdb) continue
- Continuing.
- Breakpoint 1, nsh_main (argc=1, argv=0x200ddfac) at nsh_main.c:208
- 208 sched_getparam(0, ¶m);
- (gdb) continue
- Continuing.
-
- .. tip::
- You can abbreviate ``gdb`` commands: ``info b`` is a shortcut for
- ``information breakpoints``; ``c`` works the same as ``continue``, etc.
- 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()
|