Using GDB to Debug Applications

ESP applications can be debugged using GDB, the GNU debugger, which is included with the standard IDE installation. This note will only discuss the ESP specific steps, so please refer to the main GNU GDB documentation.

Note that as of 2.5.0, the toolchain moved from the ESPRESSIF patched, closed-source version of GDB to the main GNU version. The debugging formats are different, so please be sure to use only the latest Arduino toolchain GDB executable.

CLI and IDE Note

Because the Arduino IDE doesn’t support interactive debugging, the following sections describe debugging using the command line. Other IDEs which use GDB in their debug backends should work identically, but you may need to edit their configuration files or options to enable the remote serial debugging required and to set the standard options. PRs are happily accepted for updates to this document with additional IDEs!

Preparing your application for GDB

Applications need to be changed to enable GDB debugging support. This change will add 2-3KB of flash and around 700 bytes of IRAM usage, but should not affect operation of the application.

In your main sketch.ino file, add the following line to the top of the application:

#include<GDBStub.h>

And in the void setup() function ensure the serial port is initialized and call gdbstub_init():

Serial.begin(115200);
gdbstub_init();

Rebuild and reupload your application and it should run exactly as before.

Starting a Debug Session

Once your application is running, the process to attach a debugger is quite simple: . Close the Arduino Serial Monitor . Locate Application.ino.elf File . Open a Command Prompt and Start GDB . Apply the GDB configurations . Attach the Debugger . Debug Away!

Close the Arduino Serial Monitor

Because GDB needs full control of the serial port, you will need to close any Arduino Serial Monitor windows you may have open. Otherwise GDB will report an error while attempting to debug.

Locate Application.ino.elf File

In order for GDB to debug your application, you need to locate the compiled ELF format version of it (which includes needed debug symbols).

Under Linux these files are stored in /tmp/arduino_build_* and the following command will help locate the right file for your app:

find/tmp-name"*.elf"-print

Under Windows these files are stored in %userprofile%\AppData\Local\Temp\arduino_build_* and the following command will help locate the right file for your app:

dir%userprofile%\appdata\*.elf/s/b

Note the full path of ELF file that corresponds to your sketch name, it will be needed later once GDB is started.

Open a Command Prompt and Start GDB

Open a terminal or CMD prompt and navigate to the proper ESP8266 toolchain directory.

Linux

~/.arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/2.5.0-4-b40a506/bin/xtensa-lx106-elf-gdb

Windows (Using Board Manager version)

%userprofile%\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc2円.5.0-3-20ed2b9\bin\xtensa-lx106-elf-gdb.exe

Windows (Using Git version)

%userprofile%\Documents\Arduino\hardware\esp8266com\esp8266\tools\xtensa-lx106-elf\bin\xtensa-lx106-elf-gdb.exe

Please note the proper GDB name is "xtensa-lx106-elf-gdb". If you accidentally run "gdb" you may start your own operating system’s GDB, which will not know how to talk to the ESP8266.

Apply the GDB Configurations

At the (gdb) prompt, enter the following options to configure GDB for the ESP8266 memory map and configuration:

setremotehardware-breakpoint-limit1
setremotehardware-watchpoint-limit1
setremoteinterrupt-on-connecton
setremotekill-packetoff
setremotesymbol-lookup-packetoff
setremoteverbose-resume-packetoff
mem0x200000000x3fefffffrocache
mem0x3ff000000x3fffffffrw
mem0x400000000x400fffffrocache
mem0x401000000x4013ffffrwcache
mem0x401400000x5fffffffrocache
mem0x600000000x60001fffrw
setserialbaud115200

Now tell GDB where your compiled ELF file is located:

file/tmp/arduino_build_257110/sketch_dec26a.ino.elf

Attach the Debugger

Once GDB has been configured properly and loaded your debugging symbols, connect it to the ESP with the command (replace the ttyUSB0 or COM9 with your ESP’s serial port):

targetremote/dev/ttyUSB0

or

targetremote\\.\COM9

At this point GDB will send a stop the application on the ESP8266 and you can begin setting a breakpoint (break loop) or any other debugging operation.

Example Debugging Session

Create a new sketch and paste the following code into it:

#include<GDBStub.h>
voidsetup(){
Serial.begin(115200);
gdbstub_init();
Serial.printf("Starting...\n");
}
voidloop(){
staticuint32_tcnt=0;
Serial.printf("%d\n",cnt++);
delay(100);
}

Save it and then build and upload to your ESP8266. On the Serial monitor you should see something like

1
2
3
....

Now close the Serial Monitor.

Open a command prompt and find the ELF file:

earle@server:~$find/tmp-name"*.elf"-print
/tmp/arduino_build_257110/testgdb.ino.elf
/tmp/arduino_build_531411/listfiles.ino.elf
/tmp/arduino_build_156712/SDWebServer.ino.elf

In this example there are multiple elf files found, but we only care about the one we just built, testgdb.ino.elf.

Open up the proper ESP8266-specific GDB

earle@server:~$~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb
GNUgdb(GDB)8.2.50.20180723-git
Copyright(C)2018FreeSoftwareFoundation,Inc.
LicenseGPLv3+:GNUGPLversion3orlater<https://gnu.org/licenses/gpl.html>
Thisisfreesoftware:youarefreetochangeandredistributeit.
ThereisNOWARRANTY,totheextentpermittedbylaw.
Type"show copying"and"show warranty"fordetails.
ThisGDBwasconfiguredas"--host=x86_64-linux-gnu --target=xtensa-lx106-elf".
Type"show configuration"forconfigurationdetails.
Forbugreportinginstructions,pleasesee:
<https://www.gnu.org/software/gdb/bugs/>.
FindtheGDBmanualandotherdocumentationresourcesonlineat:
<https://www.gnu.org/software/gdb/documentation/>.
Forhelp,type"help".
Type"apropos word"tosearchforcommandsrelatedto"word".
(gdb)

We’re now at the GDB prompt, but nothing has been set up for the ESP8266 and no debug information has been loaded. Cut-and-paste the setup options:

(gdb)setremotehardware-breakpoint-limit1
(gdb)setremotehardware-watchpoint-limit1
(gdb)setremoteinterrupt-on-connecton
(gdb)setremotekill-packetoff
(gdb)setremotesymbol-lookup-packetoff
(gdb)setremoteverbose-resume-packetoff
(gdb)mem0x200000000x3fefffffrocache
(gdb)mem0x3ff000000x3fffffffrw
(gdb)mem0x400000000x400fffffrocache
(gdb)mem0x401000000x4013ffffrwcache
(gdb)mem0x401400000x5fffffffrocache
(gdb)mem0x600000000x60001fffrw
(gdb)setserialbaud115200
(gdb)

And tell GDB where the debugging info ELF file is located:

(gdb)file/tmp/arduino_build_257110/testgdb.ino.elf
Readingsymbolsfrom/tmp/arduino_build_257110/testgdb.ino.elf...done.

Now, connect to the running ESP8266:

(gdb)targetremote/dev/ttyUSB0
Remotedebuggingusing/dev/ttyUSB0
0x40000f68in??()
(gdb)

Don’t worry that GDB doesn’t know what is at our present address, we broke into the code at a random spot and we could be in an interrupt, in the ROM, or elsewhere. The important bit is that we’re now connected and two things will now happen: we can debug, and the app’s regular serial output will be displayed on the GDB console..

Continue the running app to see the serial output:

(gdb)cont
Continuing.
74
75
76
77
...

The app is back running and we can stop it at any time using Ctrl-C:

113
^C
ProgramreceivedsignalSIGINT,Interrupt.
0x40000f68in??()
(gdb)

At this point we can set a breakpoint on the main loop() and restart to get into our own code:

(gdb)breakloop
Breakpoint1at0x40202e33:file/home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino,line10.
(gdb)cont
Continuing.
Note:automaticallyusinghardwarebreakpointsforread-onlyaddresses.
bcn_timout,ap_probe_send_start
Breakpoint1,loop()at/home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10voidloop()
(gdb)

Let’s examine the local variable:

(gdb)next
loop()at/home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:13
13Serial.printf("%d\n",cnt++);
(gdb)printcnt
1ドル=114
(gdb)

And change it:

2ドル=114
(gdb)setcnt=2000
(gdb)printcnt
3ドル=2000
(gdb)

And restart the app and see our changes take effect:

(gdb)cont
Continuing.
2000
Breakpoint1,loop()at/home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10voidloop(){
(gdb)cont
Continuing.
2001
Breakpoint1,loop()at/home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
10voidloop(){
(gdb)

Looks like we left the breakpoint on loop(), let’s get rid of it and try again:

(gdb)delete
Deleteallbreakpoints?(yorn)y
(gdb)cont
Continuing.
2002
2003
2004
2005
2006
....

At this point we can exit GDB with quit or do further debugging.

ESP8266 Hardware Debugging Limitations

The ESP8266 only supports a single hardware breakpoint and a single hardware data watchpoint. This means only one breakpoint in user code is allowed at any time. Consider using the thb (temporary hardware breakpoint) command in GDB while debugging instead of the more common break command, since thb will remove the breakpoint once it is reached automatically and save you some trouble.

Because of the single hardware breakpoint limitation, you must pay careful attention to the output from gdb when you set a breakpoint. If your breakpoint expression matches multiple locations, as in this example:

(gdb)breakloop
Breakpoint1at0x40202c84:loop.(2locations)

Then you will be unable to continue:

(gdb)cont
Continuing.
Note:automaticallyusinghardwarebreakpointsforread-onlyaddresses.
Warning:
Cannotinserthardwarebreakpoint1.
Couldnotinserthardwarebreakpoints:
Youmayhaverequestedtoomanyhardwarebreakpoints/watchpoints.

You can resolve this situation by deleting the previous breakpoint and using a more specific breakpoint expression:

(gdb)delete
Deleteallbreakpoints?(yorn)y
(gdb)breakmysketch.ino:loop
Breakpoint2at0x40202c84:file.../mysketch.ino,line113.