|
| 1 | +--- |
| 2 | +title: "Debugging with ESP-IDF VS Code extension: Part 2" |
| 3 | +date: 2025-06-12 |
| 4 | +showAuthor: false |
| 5 | +authors: |
| 6 | + - francesco-bez |
| 7 | +tags: |
| 8 | + - Debugging |
| 9 | + - ESP-IDF |
| 10 | + - ESP32-C3 |
| 11 | + - ESP32-S3 |
| 12 | + - VSCode |
| 13 | + |
| 14 | +summary: "This two-part guide shows how to set up VS Code with the ESP-IDF extension to debug Espressif boards using JTAG. In this second part, we will debug a simple project using gdb through Espressif's VSCode extension. We will explore the debugging options for navigating the code and inspecting the variables." |
| 15 | +--- |
| 16 | + |
| 17 | + |
| 18 | +## Introduction |
| 19 | + |
| 20 | +In the first part, we introduced the essential tools for firmware debugging and explained how to establish an `openOCD` connection with the Espressif device. |
| 21 | + |
| 22 | +In this second part, we'll launch a debugging session, step through the code using navigation commands, and inspect variables in real time. By the end of this tutorial, you'll have a solid understanding of what a debugger can do and why it's often the most effective tool for diagnosing and fixing malfunctioning firmware. |
| 23 | + |
| 24 | +In this article, we will: |
| 25 | +1. Get an overview of the debug window and available commands. |
| 26 | +2. Learn the differences between key debugging navigation commands. |
| 27 | +3. Explore the tools available for inspecting variables. |
| 28 | + |
| 29 | +__Prerequisites__ |
| 30 | + |
| 31 | +This article is the second part of a series. For the setup, please refer to the [first part](). |
| 32 | +<!-- |
| 33 | +Before you begin, make sure you have the following: |
| 34 | +
|
| 35 | +- Visual Studio Code (VS Code) installed |
| 36 | + _Otherwise follow the [installation instructions](https://code.visualstudio.com/docs/setup/windows)_ |
| 37 | +
|
| 38 | +- ESP-IDF Extension for VS Code |
| 39 | + _If it’s not installed, follow the [setup instructions](https://github.com/espressif/vscode-esp-idf-extension?tab=readme-ov-file#esp-idf-extension-for-vs-code)_ |
| 40 | +
|
| 41 | +- Ability to build, flash, and monitor a simple example project, such as `blink` or `hello_world` |
| 42 | + _If you're unsure, check [the esp-idf guide](https://docs.espressif.com/projects/vscode-esp-idf-extension/en/latest/startproject.html#create-an-esp-idf-project)_ |
| 43 | +
|
| 44 | +- An Espressif evaluation board based on one of the following series: **ESP32-C3**, **ESP32-C6**, or **ESP32-S3** |
| 45 | +
|
| 46 | +- A working openOCD connection with your target. --> |
| 47 | + |
| 48 | +## Debug window overview |
| 49 | + |
| 50 | +A debugger gives you control over how a program runs, letting you pause execution, inspect variables, and step through code one instruction at a time. This level of control is what sets debugging apart from simply running the program normally. |
| 51 | + |
| 52 | +### Breakpoint |
| 53 | + |
| 54 | +One way to use this control is by stepping line by line through the code, watching how variables change and how the program flows. While this can be helpful, it quickly becomes impractical in larger programs. Instead, it's often more effective to let the program run freely until it reaches a specific point of interest. That’s where __breakpoints__ come in. A breakpoint pauses execution at a defined line, giving you the opportunity to examine the program's state and decide what to do next. |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | +### Starting a debugging section |
| 59 | + |
| 60 | +In this first debugging session, we'll set a breakpoint and reach it using the first navigation command: `Continue`. |
| 61 | + |
| 62 | +* Open `hello_world_main.c` from the `hello_world` example project. |
| 63 | + _If the project isn't open yet, refer to the [first part]() of this article._ |
| 64 | +* Click on line __24__ to set a breakpoint. A red dot will appear next to the line number (see Fig. 1). |
| 65 | +* Press `F5` (or go to __Run → Start Debugging__). This starts the debugging session. |
| 66 | + _If a dropdown menu appears instead, check that a `launch.json` file exists in the `.vscode` folder. You can find a basic `launch.json` [here](https://gist.github.com/FBEZ/90aacea6f32110b10e43391fdaf38a1e)._ |
| 67 | +* Hit `F5` again (`Continue`) to reach the breakpoint (see Fig.2). |
| 68 | + |
| 69 | +<!--  --> |
| 70 | + |
| 71 | +{{< figure |
| 72 | +default=true |
| 73 | +src="img/breakpoint.webp" |
| 74 | +height=50 |
| 75 | +caption="Fig.1 - Breakpoint set" |
| 76 | +>}} |
| 77 | +
|
| 78 | +{{< figure |
| 79 | +default=true |
| 80 | +src="img/debugging_screenshot.webp" |
| 81 | +height=50 |
| 82 | +caption="Fig.2 - Debugging session started" |
| 83 | +>}} |
| 84 | +
|
| 85 | +### Navigation commands explanation |
| 86 | + |
| 87 | +In the debugging interface, the available commands are located in the top-right corner. They are: |
| 88 | + |
| 89 | +Here's the polished version of your list: |
| 90 | + |
| 91 | +- __`Continue`__: Runs the code until the next breakpoint is reached. |
| 92 | +- __`Step Over`__: Executes the current instruction or the entire function, then moves to the next instruction at the same level. |
| 93 | +- __`Step Into`__: Executes the current instruction (like `Step Over`), but if it's a function call, it enters the function and allows you to debug it line by line. |
| 94 | +- __`Step Out`__: Executes the remaining code in the current function and exits to the calling function. |
| 95 | +- __`Restart`__: Restarts the debugging session from the beginning. |
| 96 | +- __`Disconnect`__: Disconnects from `openOCD`, effectively stopping the debugging session. |
| 97 | + |
| 98 | + |
| 99 | +All the commands are self-explanatory, except for the subtle yet important difference between `Step Over` and `Step Into`. |
| 100 | + |
| 101 | +In other words, `Step Into` allows you to enter a function call and debug it line by line, while `Step Over` executes the entire function without stepping into it, moving directly to the next line in the current function. |
| 102 | + |
| 103 | +## Navigation commands |
| 104 | + |
| 105 | +Now, let's put the navigation commands into action to see how they work in practice. |
| 106 | + |
| 107 | +In the remainder of this section, we will: |
| 108 | +1. Modify the `hello_world_main.c` example. |
| 109 | +2. Start a new debugging session. |
| 110 | +3. Explore the difference between `Continue` and `Step Over`. |
| 111 | +4. Examine the difference between `Step Over` and `Step Into`. |
| 112 | + |
| 113 | +### Modifying `hello_world_main.c` |
| 114 | + |
| 115 | +Let's add a simple summation function before the main function `void app_main(void)`: |
| 116 | +```c |
| 117 | +// Function to calculate the summation of all integers from 1 to 'number' |
| 118 | +int summation(int number){ |
| 119 | + int sum = 0; |
| 120 | + for(int i=1;i<=number;i++){ |
| 121 | + sum += i; |
| 122 | + } |
| 123 | + return sum; |
| 124 | +} |
| 125 | +``` |
| 126 | +And change the `void app_main(void)` content to |
| 127 | +
|
| 128 | +```c |
| 129 | +void app_main(void) |
| 130 | +{ |
| 131 | + printf("Hello world!\n"); |
| 132 | + int final_number = 6; |
| 133 | + int sum = summation(final_number); |
| 134 | + printf("The summation up to %d is %d\n",final_number,sum); |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +Your `hello_world_main.c` should now look like [this gist](https://gist.github.com/FBEZ/cb3e5f940c220fbd6bdd2c1e432898c1). |
| 139 | + |
| 140 | +### Start a new debugging session |
| 141 | + |
| 142 | +Now, let's start a new debugging session: |
| 143 | +* Save the updated `hello_world_main.c` file. |
| 144 | +* Set a breakpoint at the line `int final_number=6`. |
| 145 | +* Press `F5` to begin the debugging session. Your screen should now resemble Scr.A, as shown in Fig.1. |
| 146 | + |
| 147 | +The debugger stops execution at the beginning of the `app_main` function and waits for commands. |
| 148 | + |
| 149 | + |
| 150 | +### `Continue` vs `Step Over` |
| 151 | + |
| 152 | +Let's look at the difference between `Continue` and `Step Over` with the help of Fig. 3. |
| 153 | + |
| 154 | +<!--  --> |
| 155 | +{{< figure |
| 156 | +default=true |
| 157 | +src="img/cont_vs_so.webp" |
| 158 | +height=50 |
| 159 | +caption="Fig.3 - Continue vs Step-over" |
| 160 | +>}} |
| 161 | +
|
| 162 | +1. `Continue` (`F5`) -- _Scr A → Scr B_ |
| 163 | + _Execution proceeds normally until it reaches the breakpoint we previously set._ |
| 164 | +2. `Step-Over` (`F10`) -- _Scr A → Scr C_ |
| 165 | + _The debugger executes the current line and then moves to the next line in the same function._ |
| 166 | + |
| 167 | + |
| 168 | +Before moving on, let's restart the debugging session. |
| 169 | + |
| 170 | +__Restarting the debugging session__ |
| 171 | + |
| 172 | +* Press ⇧+`F5` to exit the debugging session. |
| 173 | +* Press `F5` to enter a new debugging session |
| 174 | + _We are in Scr A (Fig. 3) again_ |
| 175 | +* Press `F5` again to reach the breakpoint |
| 176 | + _We're now in Scr. B (Fig. 1) which is the same as Scr. α (Fig.4)_ |
| 177 | + |
| 178 | +### `Step Over` vs `Step Into` |
| 179 | + |
| 180 | +Let's now look at the difference between `Step Over` and `Step Into` with the help of Fig. 4. |
| 181 | + |
| 182 | + <!--  --> |
| 183 | +{{< figure |
| 184 | +default=true |
| 185 | +src="img/stepin_vs_stepov.webp" |
| 186 | +height=50 |
| 187 | +caption="Fig. 4 - Step-over vs step-into" |
| 188 | +>}} |
| 189 | +
|
| 190 | +Since the current line in Scr. α involves a function call, it's the perfect case to highlight the difference between `Step Over` and `Step Into`. |
| 191 | + |
| 192 | +* `Step-over` (`F10`) -- Scr. α → Scr. β |
| 193 | + _The function is executed, and the debugger moves to the next line in the current function._ |
| 194 | +* `Step-into` (`F11`) -- Scr. α → Scr. γ |
| 195 | + _The debugger steps __into__ the function, allowing you to inspect it line by line._ |
| 196 | + |
| 197 | +To continue inspecting the function, you can keep using the `Step Over` command. When you're ready to exit the function call, you have two options: |
| 198 | + |
| 199 | +* __`Step Out`__ (⇧+`F11`) — Scr. β |
| 200 | + _Executes the remaining lines of the function and resumes at the line following the function call._ |
| 201 | + |
| 202 | +* __`Continue`__ (`F5`) — Scr. α |
| 203 | + _Resumes execution until the next breakpoint is hit._ |
| 204 | + |
| 205 | +These navigation commands help you move through your code with precision. Next, let’s look at how to inspect variables during debugging. |
| 206 | + |
| 207 | +## Inspecting variables |
| 208 | + |
| 209 | +So far, we've navigated the code flow, but without inspecting variables, it's hard to grasp what's really happening. |
| 210 | + |
| 211 | +In this section, we'll look into the inspecting variables tools with the help of Fig.5. |
| 212 | + |
| 213 | + <!--  --> |
| 214 | + |
| 215 | +{{< figure |
| 216 | +default=true |
| 217 | +src="img/vscode_panels.webp" |
| 218 | +height=50 |
| 219 | +caption="Fig.5 - Variable inspection" |
| 220 | +>}} |
| 221 | +
|
| 222 | +Let's start a new debugging session and reach the line `sum += i`. |
| 223 | + |
| 224 | +__Restarting the debugging session__ |
| 225 | + |
| 226 | +* Press ⇧+`F5` to exit the debugging session. |
| 227 | +* Set a breakpoint at line `sum += i` |
| 228 | +* Press `F5` to enter a new debugging session |
| 229 | +* Press `F5` again to reach the breakpoint |
| 230 | + |
| 231 | +{{< alert icon="lightbulb" iconColor="#179299" cardColor="#9cccce">}} |
| 232 | +As you know by now, we have a few options to get to the `sum +=1` line. The easiest one (followed above) is to set a breakpoint on the line and use `Continue`. |
| 233 | +{{< /alert >}} |
| 234 | + |
| 235 | +### Available inspection tools |
| 236 | + |
| 237 | +At this point of the code, three variables are defined : `number`, `i` and `sum`. The next step is to read their value. |
| 238 | + |
| 239 | +You can inspect the variable in three places: |
| 240 | +1. In the _VARIABLES_ pane under _Local_ on the left (see Fig 5) |
| 241 | +2. In the _DEBUG CONSOLE_, by typing the variable name and hitting `Enter` ⏎ |
| 242 | +3. By adding them to the _WATCH_ pane |
| 243 | + |
| 244 | +_VARIABLES_ displays all variables currently defined in the function scope, including local variables, global variables, and register values. |
| 245 | + |
| 246 | +_DEBUG CONSOLE_ is the most convenient tool for quick inspections. Use it to evaluate expressions and examine memory. |
| 247 | + |
| 248 | +_WATCH_ is useful for monitoring variables and expressions you want to track. Just click the + icon on the right side of the pane (see Fig. 5). |
| 249 | + |
| 250 | +#### VARIABLES pane |
| 251 | + |
| 252 | +When the breakpoint is first reached, _VARIABLES_ → _Local_ displays: |
| 253 | +```bash |
| 254 | +sum = 0 |
| 255 | +i = 1 |
| 256 | +``` |
| 257 | + |
| 258 | +Remember, the core hasn't executed the highlighted line yet—that's why `sum` is still 0. |
| 259 | + |
| 260 | +Pressing `Continue` (`F5`) again results in what we expect to see: |
| 261 | + |
| 262 | +```bash |
| 263 | +sum=1 |
| 264 | +i=2 |
| 265 | +``` |
| 266 | + |
| 267 | +#### DEBUG console |
| 268 | + |
| 269 | +Among the available methods to view variable values during execution, the _DEBUG CONSOLE_ --- highlighted at the bottom of Fig.5 - is the most flexible. |
| 270 | + |
| 271 | +At any moment during the debugging session, you can write an expression in the _DEBUG CONSOLE_ which is evaluated when hitting `Enter` ⏎. |
| 272 | + |
| 273 | +Type `(sum+1)*2` on the _DEBUG CONSOLE_ (next to the `>` sign, as shown in Fig. 5). As expected, you will get `4`. |
| 274 | + |
| 275 | +#### WATCH pane |
| 276 | + |
| 277 | +As a final step, let's explore the _WATCH_ tool. It allows you to enter full expressions, just like in the _DEBUG CONSOLE_. These expressions are saved and automatically evaluated in every debugging session, with their current values shown. |
| 278 | + |
| 279 | +To see the _WATCH_ pane in action: |
| 280 | +* Click `+` on the right side of _WATCH_ pane. A text input "Expression to watch" appears. |
| 281 | +* Type `sum*sum` inside of the text input |
| 282 | +* Start a new session (⇧+`F5` followed by `F5`) |
| 283 | +* Hit `F5` again to reach the breakpoint |
| 284 | + |
| 285 | +In the _WATCH_ pane a `sum*sum= 0` appeared. |
| 286 | + |
| 287 | +This field is updated according to the expression value. Press `F5` repeatedly to generate the sequence `0, 1, 9, 36, ...` which corresponds to the square of the sum. |
| 288 | + |
| 289 | +{{< alert icon="lightbulb" iconColor="#179299" cardColor="#9cccce">}} |
| 290 | +The calculation is performed in the IDE, not on the core. |
| 291 | +{{< /alert >}} |
| 292 | + |
| 293 | +## Conclusion |
| 294 | + |
| 295 | +In this second part, we explored the various functions of the debugger. We first examined different ways to navigate through the code and highlighted the differences between `Continue`, `Step-into`, and `Step-over`. Then, we focused on tools for inspecting variables, including the _DEBUG CONSOLE_, the _VARIABLES_ pane, and the _WATCH_ tool. With these tools at your disposal, you're now equipped to enhance your coding efficiency, identify issues more easily, and improve the overall quality of your code. |
0 commit comments