|
| 1 | +--- |
| 2 | +title: Debugging a NuttX target with GDB and OpenOCD |
| 3 | +author: Ingo Lütkebohle |
| 4 | +permalink: /docs/tutorials/debugging-gdb-openocd/ |
| 5 | +--- |
| 6 | + |
| 7 | +Rare is the program that works on the first try -- so you will usually need a debugger. This is even more true on an embedded device, where "printf"-style debugging is very cumbersome. |
| 8 | + |
| 9 | +There are many tools for embedded debugging. This tutorial will show you how to debug on the command line, using the [GNU Debugger gdb](https://www.gnu.org/software/gdb/) and the [Open On-Chip Debugger, OpenOCD](http://openocd.org/). These two open source tools are readily available on Linux, and they form the basis for many more advanced tools, including graphical debuggers. |
| 10 | + |
| 11 | +NuttX integration for OpenOCD is relatively new as of the time of writing (early 2019), so this tutorial also includes instructions on how to get and configure it. |
| 12 | + |
| 13 | +## Pre-Requisites |
| 14 | + |
| 15 | +### Hardware |
| 16 | + |
| 17 | + * a [supported embedded board](/docs/hardware_support#evaluation-boards) |
| 18 | + * a [support debugger probe](/docs/hardware_support#development-tools) |
| 19 | + |
| 20 | +### Software |
| 21 | + |
| 22 | + * a NuttX development setup, including gdb |
| 23 | + * OpenOCD-Nuttx (but we will show to install that) |
| 24 | + |
| 25 | + |
| 26 | +## Install OpenOCD-Nuttx |
| 27 | + |
| 28 | +Sony has added NuttX support to OpenOCD, and most importantly, this includes thread info. Since NuttX is a real RTOS with support multiple tasks/threads, you need thread support to look at anything other than the currently active task. |
| 29 | + |
| 30 | +### Get the code |
| 31 | + |
| 32 | +The repository is on Github at [https://github.com/sony/openocd-nuttx](https://github.com/sony/openocd-nuttx). Check it out like this: |
| 33 | +``` |
| 34 | +git clone --depth 1 https://github.com/sony/openocd-nuttx |
| 35 | +``` |
| 36 | +(the '--depth 1' is not required, but it saves you from downloading unnecessary data) |
| 37 | + |
| 38 | +Do *not* compile openocd just yet! |
| 39 | + |
| 40 | +### Determine your NuttX configuration |
| 41 | + |
| 42 | +NuttX sometimes switches around the memory location of the necessary information, so we need to configure OpenOCD for the currently used NuttX version. |
| 43 | + |
| 44 | +Put the following into your `.gdbinit`: |
| 45 | +``` |
| 46 | +define print-offset |
| 47 | + printf "#define PID %p\n",&((struct tcb_s *)(0))->pid |
| 48 | + printf "#define XCPREG %p\n",&((struct tcb_s *)(0))->xcp.regs |
| 49 | + printf "#define STATE %p\n",&((struct tcb_s *)(0))->task_state |
| 50 | + printf "#define NAME %p\n",&((struct tcb_s *)(0))->name |
| 51 | + printf "#define NAME_SIZE %d\n",sizeof(((struct tcb_s *)(0))->name) |
| 52 | +end |
| 53 | +``` |
| 54 | + |
| 55 | +Then run gdb on your nuttx binary and run this function: |
| 56 | +``` |
| 57 | +arm-none-eabi-gdb nuttx |
| 58 | +(gdb) print-offset |
| 59 | +``` |
| 60 | +On Nuttx 7.27, the result should look something like this: |
| 61 | + |
| 62 | +The interesting bits are the `#define` statements at the end, |
| 63 | + |
| 64 | +Now open `openocd-nuttx/src/nuttx_header.h` in your favor editor, locate the existing `#define` lines |
| 65 | +and replace them with what you got. The result should be like this: |
| 66 | + |
| 67 | + |
| 68 | +### Configure OpenOCD for NuttX support |
| 69 | + |
| 70 | +OpenOCD has a set of target configurations for the various boards. Since the boards could run one of many RTOS's, the default configuration doesn't specify any particular one -- so we have to add it. |
| 71 | + |
| 72 | +When using the Olimex STM32-E407 board, one of our standard boards, the target configuration file is `stm32f4x.cfg` and it is normally located in `tcl/target/`. For your hardware, it might be a different file, so be sure to use the right one. |
| 73 | + |
| 74 | +Open the target configuration and locate a line starting with `$_TARGETNAME configure`. Then add `-rtos nuttx` to this line. |
| 75 | + |
| 76 | +### Compile OpenOCD |
| 77 | + |
| 78 | +To compile and install OpenOCD, after you made your changes, run |
| 79 | +```bash |
| 80 | +./bootstrap |
| 81 | +./configure |
| 82 | +make |
| 83 | +sudo make install |
| 84 | +``` |
| 85 | + |
| 86 | +### Test OpenOCD |
| 87 | + |
| 88 | +To test OpenOCD, try the following command line: |
| 89 | +```bash |
| 90 | +/usr/local/bin/openocd -f /usr/share/openocd/scripts/interface/ftdi/olimex-arm-usb-ocd-h.cfg -f stm32f4x.cfg -c init -c "reset halt" |
| 91 | +``` |
| 92 | + |
| 93 | +The output should look as in the following image: |
| 94 | + |
| 95 | + |
| 96 | +OpenOCD will then block, waiting for a debugger to attach, so lets do that in the next section. |
| 97 | + |
| 98 | +## Running GDB with OpenOCD |
| 99 | + |
| 100 | +Run gdb in your NuttX directory as follows: |
| 101 | +```bash |
| 102 | +arm-none-eabi-gdb nuttx |
| 103 | +(gdb) target extended-remote :3333 |
| 104 | +(gdb) cont |
| 105 | +``` |
| 106 | +This connected to the gdb server running on port 3333 (OpenOCD default) of the same machine. It will sit on the NuttX `_start` function, which isn't very interesting, so we let it continue. |
| 107 | + |
| 108 | +At this moment we have not defined any breakpoints, yet, so you can just press `Ctrl-C` to interrupt the running program again. This will interrupt it after NuttX had a chance to do initialization, so we will actually get to see some data. |
| 109 | + |
| 110 | +### Inspect the program |
| 111 | + |
| 112 | +Now, if everything worked correctly, we should get some information from the RTOS, such as thread info. To test, type `info threads` at the gdb prompt to get a thread info table. Your output will very depending on the NuttX configuration. On my bare-bones NSH-only configuration, it looks as follows: |
| 113 | + |
| 114 | +So we see four threads, two of which are the OS work queues, one is the init thread, and one is the idle thread. |
| 115 | + |
| 116 | +Most likely, NuttX has stopped in the idle thread, which isn't very interesting. To inspect the others, we can use the `thread` command to switch a thread and then maybe display a backtrace and some variables. Try the following: |
| 117 | + |
| 118 | +```gdb |
| 119 | +thread 2 |
| 120 | +info locals |
| 121 | +print rtcb |
| 122 | +print *rtcb |
| 123 | +``` |
| 124 | +This switches to thread 2 and then inspects the local variables, of which there are two, one being called `rtcb`. We print it, see its a pointer to a structure, so we dereference and print again to display all the structure fields. This should look something like the following: |
| 125 | + |
| 126 | +In my case, this is the NSH thread which is waiting for some input. |
| 127 | + |
| 128 | + |
| 129 | +## Conclusion |
| 130 | + |
| 131 | +This concludes this basic tutorial on getting gdb to run with OpenOCD and NuttX support. |
| 132 | + |
| 133 | +Using gdb on the command line is considered a bit cumbersome by many. So if you know your way |
| 134 | +around an IDE with gdb support, integrating it should be easy. We leave that as an exercise |
| 135 | +for the reader ;-) |
| 136 | + |
| 137 | +There are also IDEs with microcontroller support -- stay tuned for another tutorial with more |
| 138 | +details on that. |
0 commit comments