mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-12 01:53:07 +03:00
GDB support w/new toolchain and UART driver (#5559)
* Add full gdb support with uart/Serial integration * Fix GDB merge errors * Update to unpatched GDB protocol specification It appears that Espressif patched the open source xtensa GDB port in order to build their old GDB executable and their old gdbstub (basically removing any register in a generic xtensa and only leaving those present in the chip they synthesized). Their GDBStub also assumed this behavior. Unpatched upstream GNU GDB now expects all the registers in xtensa-config.c to be sent/read on a 'g' command. Change the GDB stub to send "xxxxxxxx"s (legal per the spec) for unimplemented registers. This makes the 'g' response much longer, but it's results are cached and in an interactive debugger it isn't noticeable. * Fix .iram.literal to come before .iram.text for GDB * Move functions to flash, call using wrappers All functions which are not interrupt or exception called are now in flash. A small IRAM wrapper enables flash when processing main GDB ops by calling Cache_Read_Enable_New() and then jumping to the main flash code. This seems to work for catching exceptions, data and code breaks, and Ctrl-C. The UART ISR handler and exception handler register-saving bits of code in ASM are still in IRAM. GDB IRAM usage is now about 670 bytes. * Remove LWIP2 builder commit * Add documentation and gdbstub_init header Add some simple GDB documentation to the main tree showing a worked example. Adds the definition of `void gdbstub_init()` to <GDBStub.h> * Clean up GDB include and library dir Replace GDBstub.h with the version in the internal/ directory, and adjust stub code accordingly. This way, only one copy of a file called "GDBstub.h" will exist. Update the gdbcommands and replace the obsolete ESPRESSIF readme with @kylefleming's version since we're mainly doing serial, not TCP, connected debugging. Bump the library rev. number since this is a pretty big functionality change. Minor documentation tweak. * Undo much of UART refactoring, set fifo IRQ to 16 Remove the refactoring of pin control and other little things not directly related to GDB processing. Should greatly reduce the diff size in uart.c. Should also remove any register value changes (intended or otherwise) introduced in the original PR from @kylefleming. Set the FIFO interrupt to 16 chars when in GDB mode, matching the latest UART configuration for highest speed. * Add architecture comments, cleanup uart.c code Comments added to UART.c trying to explain (as best as I understand it) the changes done to support GDB and how they interact with standard operation. Fix the uart_uninit to stop the ISR and then free appropriately. Fix uart_isr_handle_data (GDB's shim for sending chars to the 8266 app) to do the exact same thing as the standard UART handler including set the overflow properly and either discard or overwrite in that case. Fix serial reception when GDB enabled by enabling the user recv ISR. Remove commented attributes from gdbstub, leftover from the move to flash. General logic cleanup per comments in the PR. * Also set the UART flags for HW error in GDB Ensure we also check the UART flags and set the uart status appropriately when in GDB mode.
This commit is contained in:
committed by
Develo
parent
4657666319
commit
bff3a6d963
365
doc/gdb.rst
Normal file
365
doc/gdb.rst
Normal file
@ -0,0 +1,365 @@
|
||||
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
|
||||
<//sourceware.org/gdb/download/onlinedocs/gdb/index.html>`__.
|
||||
|
||||
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:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <GDBStub.h>
|
||||
|
||||
And in the ``void setup()`` function ensure the serial port is initialized
|
||||
and call ``gdbstub_init()``:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
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
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
find /tmp -name "*.elf" -print
|
||||
|
||||
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.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
cd TODO WINDOWS
|
||||
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:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
set remote hardware-breakpoint-limit 1
|
||||
set remote hardware-watchpoint-limit 1
|
||||
set remote interrupt-on-connect on
|
||||
set remote kill-packet off
|
||||
set remote symbol-lookup-packet off
|
||||
set remote verbose-resume-packet off
|
||||
mem 0x20000000 0x3fefffff ro cache
|
||||
mem 0x3ff00000 0x3fffffff rw
|
||||
mem 0x40000000 0x400fffff ro cache
|
||||
mem 0x40100000 0x4013ffff rw cache
|
||||
mem 0x40140000 0x5fffffff ro cache
|
||||
mem 0x60000000 0x60001fff rw
|
||||
set serial baud 115200
|
||||
|
||||
Now tell GDB where your compiled ELF file is located:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
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):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
target remote /dev/ttyUSB0
|
||||
|
||||
or
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
target remote \\.\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:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <GDBStub.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
gdbstub_init();
|
||||
Serial.printf("Starting...\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint32_t cnt = 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
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
1
|
||||
2
|
||||
3
|
||||
....
|
||||
|
||||
|
||||
Now close the Serial Monitor.
|
||||
|
||||
Open a command prompt and find the ELF file:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
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
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
earle@server:~$ ~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb
|
||||
GNU gdb (GDB) 8.2.50.20180723-git
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
Type "show copying" and "show warranty" for details.
|
||||
This GDB was configured as "--host=x86_64-linux-gnu --target=xtensa-lx106-elf".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
<http://www.gnu.org/software/gdb/bugs/>.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
<http://www.gnu.org/software/gdb/documentation/>.
|
||||
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "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:
|
||||
|
||||
.. code:: cpp
|
||||
(gdb) set remote hardware-breakpoint-limit 1
|
||||
(gdb) set remote hardware-watchpoint-limit 1
|
||||
(gdb) set remote interrupt-on-connect on
|
||||
(gdb) set remote kill-packet off
|
||||
(gdb) set remote symbol-lookup-packet off
|
||||
(gdb) set remote verbose-resume-packet off
|
||||
(gdb) mem 0x20000000 0x3fefffff ro cache
|
||||
(gdb) mem 0x3ff00000 0x3fffffff rw
|
||||
(gdb) mem 0x40000000 0x400fffff ro cache
|
||||
(gdb) mem 0x40100000 0x4013ffff rw cache
|
||||
(gdb) mem 0x40140000 0x5fffffff ro cache
|
||||
(gdb) mem 0x60000000 0x60001fff rw
|
||||
(gdb) set serial baud 115200
|
||||
(gdb)
|
||||
|
||||
And tell GDB where the debugging info ELF file is located:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) file /tmp/arduino_build_257110/testgdb.ino.elf
|
||||
Reading symbols from /tmp/arduino_build_257110/testgdb.ino.elf...done.
|
||||
|
||||
Now, connect to the running ESP8266:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) target remote /dev/ttyUSB0
|
||||
Remote debugging using /dev/ttyUSB0
|
||||
0x40000f68 in ?? ()
|
||||
(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:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) cont
|
||||
Continuing.
|
||||
74
|
||||
75
|
||||
76
|
||||
77
|
||||
...
|
||||
|
||||
The app is back running and we can stop it at any time using ``Ctrl-C``:
|
||||
|
||||
.. code:: cpp
|
||||
113
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x40000f68 in ?? ()
|
||||
(gdb)
|
||||
|
||||
At this point we can set a breakpoint on the main ``loop()`` and restart
|
||||
to get into our own code:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) break loop
|
||||
Breakpoint 1 at 0x40202e33: file /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino, line 10.
|
||||
(gdb) cont
|
||||
Continuing.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
bcn_timout,ap_probe_send_start
|
||||
|
||||
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
|
||||
10 void loop()
|
||||
(gdb)
|
||||
|
||||
Let's examine the local variable:
|
||||
|
||||
.. code:: cpp
|
||||
(gdb) next
|
||||
loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:13
|
||||
13 Serial.printf("%d\n", cnt++);
|
||||
(gdb) print cnt
|
||||
$1 = 114
|
||||
(gdb)
|
||||
|
||||
And change it:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
$2 = 114
|
||||
(gdb) set cnt = 2000
|
||||
(gdb) print cnt
|
||||
$3 = 2000
|
||||
(gdb)
|
||||
|
||||
And restart the app and see our changes take effect:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) cont
|
||||
Continuing.
|
||||
2000
|
||||
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
|
||||
10 void loop() {
|
||||
(gdb) cont
|
||||
Continuing.
|
||||
2001
|
||||
Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10
|
||||
10 void loop() {
|
||||
(gdb)
|
||||
|
||||
Looks like we left the breakpoint on loop(), let's get rid of it and try again:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
(gdb) delete
|
||||
Delete all breakpoints? (y or n) 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.
|
||||
|
||||
|
@ -12,6 +12,7 @@ Welcome to ESP8266 Arduino Core's documentation!
|
||||
ESP8266WiFi <esp8266wifi/readme>
|
||||
OTA Updates <ota_updates/readme>
|
||||
PROGMEM <PROGMEM>
|
||||
Using GDB to debug <gdb>
|
||||
|
||||
Boards <boards>
|
||||
FAQ <faq/readme>
|
||||
|
Reference in New Issue
Block a user