ESP8266 SDK: how to debug with gdb
by kacang bawang
Not long ago, to little fanfare, Espressif (the makers of ESP8266) released gdbstub
– a library that allows use of gdb
on ESP8266. It works with both non-OS and RTOS SDKs, and works over UART, not JTAG. You get 1 breakpoint for RAM and 1 breakpoint for FLASH. Is this awesome, or what!? Authorship is unattributed, but is believed to have been written by Sprite_tm
.
In this post, I will show you how to add gdb
support to your project. This example will use the RTOS SDK, but applies just the same to the non-OS SDK.
First, let’s talk a little bit about project organization. We will use esp-open-sdk
on Linux. See previous articles about setting up non-os and rtos sdks.
I will skip over parts of the setup that have been discussed in the previous articles. You can find the exact copy of this project here, to use as a reference.
First, clone the git repository for the gdbstub somewhere locally. Then, set up the following folder structure.
1 2 3 4 5 6 7 8 9 10 11 12 |
/home/my_esp8266_projects/hello_world_rtos_debug src ............ renamed copy of "RTOS_SDK/examples/smart_config" directory include ...... contains user_config.h user ......... contains user_main.h gdbstub ...... *NEW* copy or link to the `esp-gdbstub` directory Makefile ..... "inner makefile", modified Makefile ....... "outer makefile", modified ld ............. link to "RTOS_SDK/ld" lib ............ link to "RTOS_SDK/tools" include ........ link to "RTOS_SDK/include" bin ............ create empty, output will be placed here flash .......... a small script to ease flashing |
Each directory inside src
is compiled into its own .a
. Then, the outer makefile rolls them into flash.bin
and irom0text.bin
.
Thus, gdbstub
is added as a module, inside its own directory. Now, we need to connect it to the rest of the project.
First, we need a “module makefile” inside the directory. As a base, we will use the one found inside src/user/
. I realize that there is already a Makefile inside the gdbstub
, but it gives errors with the latest RTOS SDK.
Take src/user/Makefile
copy (and overwrite) into src/gdbstub/
, and within, change the GEN_LIBS
line to look like:
1 2 |
//results in building of this static library, placed in .output/ directory GEN_LIBS = libgdbstub.a |
Then, edit the “inner makefile” in src/Makefile
to look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
... //added gdbstub as one of the directories that needs to be built SUBDIRS= \ user \ gdbstub ... //need to let linker know about gdb via -ggdb TARGET_LDFLAGS = \ -ggdb \ -nostdlib \ -Wl,-EL \ --longcalls \ --text-section-literals ... //I think these were switched around, should //end up like this: ifeq ($(FLAVOR),release) TARGET_LDFLAGS += -g -O2 endif ifeq ($(FLAVOR),debug) TARGET_LDFLAGS += -g -O1 endif ... //This is in the "don't touch" part. //Allows to include from src/ directory INCLUDES := $(INCLUDES) -I $(PDIR)include -I $(PDIR) ... |
Now let’s take a look at some small tweaks to the source of gdbstub
. The only file we need to modify is gdbstub.c
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... // clean up double include, reorder, get rid of unused //#include "gdbstub.h" #include "espressif/c_types.h" #include "espressif/esp8266/ets_sys.h" #include "espressif/esp8266/eagle_soc.h" //#include "gpio.h" #include "xtensa/corebits.h" #include "gdbstub.h" #include "gdbstub-entry.h" #include "gdbstub-cfg.h" ... |
Lastly, let’s add an instruction to wait for debugger to attach into user_main.c
.
1 2 3 4 5 6 7 8 9 10 |
... void user_init(void) { ... uart_div_modify(0, UART_CLK_FREQ / 115200); //only after we set uart frequency gdbstub_init(); ... } |
Now we can compile by running make
on the “outer makefile” and flash the resulting output from ./bin
.
Ok, but how to we launch into a debug session? Use gdbcmds
file from gdbstub
like this:
1 |
$ xtensa-lx106-elf-gdb -x gdbcmds -b 115200 |
Make sure that /dev/ttyUSBx
specified in gdbcmds matches what is found /dev. Now we find ourselves at a gdb prompt:
1 2 3 4 |
... gdbstub_do_break_breakpoint_addr () at gdbstub-entry.S:399 399 break 0,0 (gdb) |
We can either just continue (c
) or set a breakpoint in RAM or FLASH. Breakpoints in RAM are set with br
, while breakpoints in FLASH are set with hbr
. Remember, we only have 1 of each.
Stepping is possible only in RAM. So, if you need to step through a function, make sure to place it into ram like this:
1 2 3 |
void IRAM_ATTR whatever(void) { ... } |
By default (without any *_ATTR) code is placed into FLASH. If you try to step through a function located in FLASH you will see an error message like this:
1 2 3 4 5 6 |
(gdb) n Warning: Cannot insert breakpoint 0. Error accessing memory address 0x12345678: Input/output error. 0x12345678 in some_function () |
Happy debugging!
A really basic – dumb – question.
I using a tool chain in Windows.
The gdbcmds file used by GDB specifies:
file ../.output/eagle/debug/image/eagle.app.v6.out
The “.out” file – is that generated by the compiler/linker or by the esp_tool?
My toolchain output does not include that file so I’m try to figure out what I need to do to generate it.
Thanks
My dev box died a couple of days ago, so unfortunately I cannot give a definitive answer right now, but I believe the .out file is a concatenation of binaries produced by compilation. That is, it is generated by the compiler/linker.
Thanks for replying.
It ends up being the elf file.
None of the make files worked so I had to figure out how to create a simple make to compile and test.
Thanks – nice blog
Hi,
After compiling, I get an error “gdbstub.c:394:55: error: ‘XCHAL_DEBUGLEVEL’ undeclared”. Is this because the Makefile not compiling the .S file?. Any idea, how to fix this.
I solved it. The gdbstub.c was looking for some header files including core-isa.h that was present in the ESP_RTOS_SDK. Then just added core-isa in the gdbstub.c and it compiled fine. Have to still test on the hardware though.
Hello,
I followed your instruction. i downloaded your project and compile it successfully .
But when I read through gdb it shows nothing.
and when i insert break point , gdb exit with segmentation fault (sore dump) Error.