NOTE: Make sure to configure your drivers as mentioned in ESP-IDF Configure JTAG interface documentation.
This tutorial shows the user how to debug ESP-IDF projects using the Visual Studio Code extension for ESP-IDF. If you haven't configured the extension as explained in Install tutorial please do it first.
NOTE: If there is any Python package error, please try to reinstall the required python packages with the ESP-IDF: Install ESP-IDF Python Packages command.
NOTE: Currently the python package
pygdbmi
used by the debug adapter still depends on some Python 2.7 libraries (libpython2.7.so.1.0) so make sure that the Python executable inidf.pythonBinPath
you use contains these libraries. This will be dropped in later versions of ESP-IDF.
- Configure, build and flash your project as explained in Basic use tutorial.
- Set the proper values for openOCD Configuration files in the
idf.openOCDConfigs
configuration setting. You can choose a specific board listed in openOCD using ESP-IDF: Select OpenOCD Board Configuration or use ESP-IDF: Device configuration to manually set any value you desire.
When you use ESP-IDF: Set Espressif device target the following files are set:
- Choosing esp32 as IDF_TARGET will set
idf.openOCDConfigs
to ["interface/ftdi/esp32_devkitj_v1.cfg", "target/esp32.cfg"] - Choosing esp32s2 as IDF_TARGET will set
idf.openOCDConfigs
to ["interface/ftdi/esp32_devkitj_v1.cfg", "target/esp32s2.cfg"] - Choosing esp32s3 as IDF_TARGET will set
idf.openOCDConfigs
to ["interface/ftdi/esp32_devkitj_v1.cfg", "target/esp32s3.cfg"] - Choosing esp32c3 as IDF_TARGET will set
idf.openOCDConfigs
to ["board/esp32c3-builtin.cfg"] if using built-in usb jtag or ["board/esp32c3-ftdi.cfg"] if using ESP-PROG-JTAG.
NOTE: Please take a look at Configuring of OpenOCD for specific target for more information about these configuration files.
- With the blink example folder open in your visual studio code window, press F5.
Several steps will be automatically done for you but explained for clarity. You can skip to step 6 to continue the debug tutorial part.
- OpenOCD server is launched in the background and the output is shown in menu
View
-> Output -> OpenOCD. By default it will be launched using localhost, port 4444 for Telnet communication, port 6666 for TCL communication and port 3333 for gdb.
NOTE: The user can start or stop the openOCD from Visual Studio Code using the ESP-IDF: OpenOCD Manager command or from the
OpenOCD Server (Running | Stopped)
button in the visual studio code status bar.
NOTE: The user can modify
openocd.tcl.host
andopenocd.tcl.port
configuration settings to modify these values. You can also setidf.openOcdDebugLevel
to lower or increase (0-4) the messages from OpenOCD in the OpenOCD output. Please review ESP-IDF Settings to see how to modify these configuration settings.
-
The ESP-IDF Debug adapter server is launched in the background and the output is shown in menu View -> Output ->
ESP-IDF Debug Adapter
. This server is a proxy between Visual Studio Code, configured toolchain GDB and OpenOCD server. It will be launched at port43474
by default. Please review DEBUGGING for more information how to customize the debugging behavior like application offset, logging level and set your own gdb startup commands. -
The debug session will start after the debug adapter server is launched and ready.
- When the target is halted, the editor will show the line of code where the program halted and the list of threads in the
Call Stack
sub-window on theRun
icon in the Activity Bar on the side of Visual Studio Code. The first line of call stack under Thread #1 contains the last called functionapp_main()
, which in turned was called frommain_task()
as shown in the previous image. Each line of the stack also contains the file name and line number where the function was called. By clicking on each of the stack entries, you will see the file opened.
By expanding threads you can navigate throughout the application. Thread #5 that contains much longer call stack where the user can see, besides function calls, numbers like 0x4000bff0
representing address of binary code not provided in source form.
Go back to the app_main()
in Thread #1 to familiar code of blink.c file that will be examined in more details in the following examples. Debugger makes it easy to navigate through the code of entire application. This comes handy when stepping through the code and working with breakpoints and will be discussed below.
When debugging, we would like to be able to stop the application at critical lines of code and then examine the state of specific variables, memory and registers / peripherals. To do so we are using breakpoints. They provide a convenient way to quickly get to and halt the application at specific line.
- Let’s establish two breakpoints when the state of LED changes. Based on the code listing above, this happens at lines 57 and 80. To set a breakpoint, go to the desired line and press F9 or click on the circle shown next to the line number (editor margin).
The list of breakpoints is shown in the
Breakpoints
sub-window on theRun
icon in the Activity Bar on the side of Visual Studio Code.
NOTE: Consider that ESP32 has a maximum of 2 hardware breakpoints. Please look at ESP-IDF Debugging tips: Breakpoints for more information.
The Visual Studio Code shows a Debug toolbar on the top of the editor with several actions as explained in Visual Studio Code Debug Actions.
- If you press F5 (Continue/Pause) the processor will run and halt at the next breakpoint. If you press F5 again, it will stop at the next breakpoint and so on. The user will be able to see that LED is changing the state after each click to "Continue" program execution.
Read more about breakpoints under breakpoints and watchpoints available and what else should i know about breakpoints?
- When debugging, you may resume application and enter code waiting for some event or staying in infinite loop without any break points defined. In such case, to go back to debugging mode, you can break program execution manually by pressing "Continue/Pause" button. To check it, delete all breakpoints and click "Continue". Then click “Pause”. Application will be halted at some random point and LED will stop blinking.
It is also possible to step through the code using “Step Into (F11)” and “Step Over (F10)” commands. The difference is that “Step Into (F11)” is entering inside subroutines calls, while “Step Over (F10)” steps over the call, treating it as a single source line.
Before being able to demonstrate this functionality, using information discussed in previous paragraph, make sure that you have only one breakpoint defined at line 57 of blink.c
.
- Resume program by entering pressing F5 and let it halt. Now press “Step Over (F10)”, one by one couple of times, to see how debugger is stepping one program line at a time.
- If you press “Step Into (F11)” instead, then debugger will step inside subroutine call.
- If you press “Step Out (Shift + F11)” instead, then debugger will step outside the subroutine call.
In this particular case debugger stepped inside vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS)
and effectively moved to tasks.c
code. See Why stepping with “next” does not bypass subroutine calls? for potential limitations using the next
command.
A common debugging tasks is checking the value of a program variable as the program runs. To be able to demonstrate this functionality, update file blink.c
by adding a declaration of a global variable int i above definition of function blink_task
. Then add i++
inside while(1)
of this function to get i
incremented on each blink.
-
Stop debugging by pressing "Stop (Shift + F5)". Build and flash the code to the ESP and restart the debugger by pressing F5. Once the application is halted, set a breakpoint in the line where
i++
is. -
Next in the
Watch
sub-window on theRun
icon in the Activity Bar on the side of Visual Studio Code, click the+
and enteri
to start watching its value. -
Continue the program execution by pressing F5. Each time the program is halted, you will see
i
being incremented.
You can also set a breakpoint to halt the program execution if a certain condition is satisfied. See Visual Studio Code conditional breakpoints.
To set a new conditional breakpoint, go to the desired line and right click on the circle shown next to the line number (editor margin) and select Add Conditional Breakpoint
action. You can also modify a breakpoint to add a condition in the list of breakpoints in the Breakpoints
sub-window on the Run
icon in the Activity Bar on the side of Visual Studio Code. Click the pencil
icon on the breakpoint and set the breakpoint condition.
For this example, go to line 79 and right click on the circle shown next to the line number (editor margin) and select Add Conditional Breakpoint
action and set i=2
. When you start the debug, it will stop on line 79 when i
has value of 2.
You can send any GDB commands in the Debug console with --exec COMMAND
. You need to set logLevel: 5
in the project's launch.json to see the command output.
More about command line debugging.
You can start a monitor session that can capture fatal error events with ESP-IDF: Launch IDF Monitor for CoreDump / GDB-Stub Mode
command and, if configured in your project's sdkconfig, trigger the start of a debug session for GDB remote protocol server (GDBStub) or ESP-IDF Core Dump when an error is found. Read more in the panic handler documentation.
- Core dump is configured when
Core dump's Data destination
is set to eitherUART
orFLASH
using theESP-IDF: SDK Configuration Editor
extension command oridf.py menuconfig
in a terminal. - GDB Stub is configured when
Panic handler behaviour
is set toInvoke GDBStub
using theESP-IDF: SDK Configuration Editor
extension command oridf.py menuconfig
in a terminal.
The user can modify the debug session as shown in the DEBUGGING documentation by customizing settings such as the program start address offset, the ESP-IDF Debug Adapter server port, logging level and custom initial gdb commands.
See other ESP-IDF extension features.