ESP32/ESP32-C5_GDB_Debugging_Guid...

952 lines
32 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32-C5 GDB Debugging Guide</title>
<style>
:root {
--primary-color: #0e7490;
--secondary-color: #357edd;
--bg-color: #f9f9f9;
--code-bg: #f0f0f0;
--border-color: #e0e0e0;
--text-color: #1a1a1a;
--success-color: #19a974;
--warning-color: #ff6300;
}
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: var(--text-color);
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: var(--bg-color);
}
header {
background: linear-gradient(135deg, #0891b2, var(--secondary-color));
color: white;
padding: 40px 30px;
border-radius: 8px;
margin-bottom: 30px;
}
header h1 {
margin: 0 0 10px 0;
font-size: 2.5em;
}
header p {
margin: 5px 0;
opacity: 0.9;
}
nav {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
nav h2 {
margin-top: 0;
color: var(--secondary-color);
}
nav ul {
list-style: none;
padding: 0;
column-count: 2;
column-gap: 30px;
}
nav li {
margin: 8px 0;
}
nav a {
color: var(--secondary-color);
text-decoration: none;
transition: color 0.2s;
}
nav a:hover {
color: #0e7490;
text-decoration: underline;
}
main {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h2 {
color: var(--primary-color);
border-bottom: 2px solid var(--border-color);
padding-bottom: 10px;
margin-top: 40px;
}
h3 {
color: var(--secondary-color);
margin-top: 30px;
}
code {
background: var(--code-bg);
padding: 2px 6px;
border-radius: 3px;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.9em;
}
pre {
background: var(--code-bg);
padding: 20px;
border-radius: 6px;
overflow-x: auto;
border-left: 4px solid var(--secondary-color);
}
pre code {
background: none;
padding: 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
background: white;
}
th {
background: var(--secondary-color);
color: white;
padding: 12px;
text-align: left;
font-weight: 600;
}
td {
padding: 12px;
border-bottom: 1px solid var(--border-color);
}
tr:hover {
background: #f5f5f5;
}
.alert {
padding: 15px 20px;
border-radius: 6px;
margin: 20px 0;
border-left: 4px solid;
}
.alert-info {
background: #e7f5ff;
border-color: var(--secondary-color);
color: #004085;
}
.alert-success {
background: #e7f9f0;
border-color: var(--success-color);
color: #155724;
}
.alert-warning {
background: #fff4e5;
border-color: var(--warning-color);
color: #856404;
}
.checkmark::before {
content: '✅ ';
}
.crossmark::before {
content: '❌ ';
}
.warning::before {
content: '⚠️ ';
}
footer {
text-align: center;
margin-top: 50px;
padding: 30px;
color: #666;
border-top: 1px solid var(--border-color);
}
.button {
display: inline-block;
background: var(--secondary-color);
color: white;
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
margin: 5px;
transition: background 0.2s;
}
.button:hover {
background: #0e7490;
}
@media (max-width: 768px) {
body {
padding: 10px;
}
header {
padding: 30px 20px;
}
header h1 {
font-size: 2em;
}
main {
padding: 20px;
}
nav ul {
column-count: 1;
}
table {
font-size: 0.9em;
}
}
</style>
</head>
<body>
<header>
<h1>ESP32-C5 GDB Debugging Guide</h1>
<p><strong>Author:</strong> Bob McMahon</p>
<p><strong>Hardware:</strong> ESP32-C5 DevKit (RISC-V)</p>
<p><strong>ESP-IDF:</strong> v6.0 or later</p>
<p><strong>Last Updated:</strong> December 2025</p>
</header>
<nav>
<h2>Table of Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#why-gdb">Why GDB Debugging?</a></li>
<li><a href="#capabilities">ESP32-C5 Debug Capabilities</a></li>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#building">Building with Debug Symbols</a></li>
<li><a href="#starting">Starting a Debug Session</a></li>
<li><a href="#commands">Essential GDB Commands</a></li>
<li><a href="#strategies">Debugging Strategies</a></li>
<li><a href="#examples">Real-World Examples</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
<li><a href="#advanced">Advanced Techniques</a></li>
<li><a href="#resources">Resources</a></li>
</ul>
</nav>
<main>
<section id="introduction">
<h2>Introduction</h2>
<p>The ESP32-C5 is Espressif's first RISC-V microcontroller with dual-band WiFi 6 (802.11ax) support. Unlike its Xtensa predecessors (ESP32, ESP32-S3), the ESP32-C5's RISC-V architecture and built-in USB-JTAG interface make debugging significantly easier.</p>
<p>This guide demonstrates how to use GDB (GNU Debugger) to debug ESP32-C5 firmware, focusing on real-world scenarios like troubleshooting WiFi driver issues, CSI configuration problems, and memory corruption.</p>
</section>
<section id="why-gdb">
<h2>Why GDB Debugging?</h2>
<p>Traditional debugging with <code>ESP_LOGI()</code> statements has limitations:</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Limitations</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Printf Debugging</strong></td>
<td>
• Alters timing and behavior<br>
• Cannot inspect internal driver state<br>
• Requires recompilation for each change<br>
• Output floods serial console
</td>
</tr>
<tr>
<td><strong>LED Blink Debugging</strong></td>
<td>
• Very limited information<br>
• Time-consuming iteration<br>
• Cannot show complex state
</td>
</tr>
</tbody>
</table>
<div class="alert alert-success">
<strong>GDB debugging solves these problems:</strong>
<ul>
<li class="checkmark"><strong>Set breakpoints</strong> without modifying code</li>
<li class="checkmark"><strong>Inspect variables</strong> at any point in execution</li>
<li class="checkmark"><strong>Step through code</strong> line by line</li>
<li class="checkmark"><strong>Examine memory</strong> and registers</li>
<li class="checkmark"><strong>Watch variables</strong> for changes</li>
<li class="checkmark"><strong>View call stacks</strong> to understand program flow</li>
<li class="checkmark"><strong>Debug ESP-IDF internals</strong> (WiFi driver, FreeRTOS, etc.)</li>
</ul>
</div>
</section>
<section id="capabilities">
<h2>ESP32-C5 Debug Capabilities</h2>
<p>The ESP32-C5 has <strong>built-in USB-JTAG</strong> support, eliminating the need for external debug adapters:</p>
<h3>Hardware Features</h3>
<ul>
<li><strong>Built-in USB-JTAG</strong>: Debug over the same USB cable used for flashing</li>
<li><strong>4 Hardware Breakpoints</strong>: No speed penalty</li>
<li><strong>Unlimited Software Breakpoints</strong>: Via flash patching</li>
<li><strong>2 Watchpoints</strong>: Trigger on memory read/write</li>
<li><strong>Real-time Debugging</strong>: Debug live, running firmware</li>
</ul>
<h3>Comparison with Other ESP32 Chips</h3>
<table>
<thead>
<tr>
<th>Feature</th>
<th>ESP32 (Xtensa)</th>
<th>ESP32-S3 (Xtensa)</th>
<th>ESP32-C5 (RISC-V)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Debug Interface</strong></td>
<td>External JTAG required</td>
<td>Built-in USB-JTAG</td>
<td>Built-in USB-JTAG</td>
</tr>
<tr>
<td><strong>Debugger</strong></td>
<td>xt-gdb (Xtensa)</td>
<td>xt-gdb (Xtensa)</td>
<td>riscv32-esp-elf-gdb</td>
</tr>
<tr>
<td><strong>Setup Complexity</strong></td>
<td>High (extra hardware)</td>
<td>Medium</td>
<td><strong>Low</strong> (just USB)</td>
</tr>
<tr>
<td><strong>OpenOCD Support</strong></td>
<td>Mature</td>
<td>Mature</td>
<td>Good (ESP-IDF v6.0+)</td>
</tr>
</tbody>
</table>
</section>
<section id="prerequisites">
<h2>Prerequisites</h2>
<h3>Hardware</h3>
<ul>
<li><strong>ESP32-C5 DevKit</strong> with USB-C cable</li>
<li><strong>Host Computer</strong> running Linux, macOS, or Windows (WSL2)</li>
</ul>
<h3>Software</h3>
<ul>
<li><strong>ESP-IDF v6.0 or later</strong> (ESP32-C5 support)</li>
<li><strong>OpenOCD</strong> (included with ESP-IDF)</li>
<li><strong>GDB for RISC-V</strong> (riscv32-esp-elf-gdb, included with ESP-IDF)</li>
</ul>
<h3>Verify Installation</h3>
<pre><code># Check ESP-IDF version
idf.py --version
# Should show: ESP-IDF v6.0 or later
# Check GDB
riscv32-esp-elf-gdb --version
# Should show: GNU gdb (esp-gdb) 12.1 or later
# Check OpenOCD
openocd --version
# Should show: Open On-Chip Debugger 0.12.0-esp32 or later</code></pre>
</section>
<section id="building">
<h2>Building with Debug Symbols</h2>
<p>Debug symbols allow GDB to map machine code back to source code, showing variable names, function names, and line numbers.</p>
<h3>Method 1: Using menuconfig (Recommended)</h3>
<pre><code>cd ~/your-project
idf.py menuconfig</code></pre>
<p>Navigate to and configure:</p>
<pre><code>Component config
→ Compiler options
→ Optimization Level → Debug (-Og) ← Select this
→ [*] Generate debug symbols (-g) ← Enable
→ Debug information format → DWARF-4 ← Select</code></pre>
<p>Additional recommended settings:</p>
<pre><code>Component config
→ Compiler options
→ [*] Enable assertions (assert) ← Enable
→ [ ] Strip function/variable names ← DISABLE
Component config
→ FreeRTOS
→ [*] Enable stack overflow checks ← Enable
→ Check method → Canary bytes ← Select</code></pre>
<h3>Optimization Levels Explained</h3>
<table>
<thead>
<tr>
<th>Level</th>
<th>GCC Flag</th>
<th>Code Speed</th>
<th>Debug Quality</th>
<th>Use Case</th>
</tr>
</thead>
<tbody>
<tr style="background-color: #e7f9f0;">
<td><strong>Debug</strong></td>
<td><code>-Og</code></td>
<td>Medium</td>
<td><strong>Excellent</strong></td>
<td><strong>GDB debugging</strong></td>
</tr>
<tr>
<td>None</td>
<td><code>-O0</code></td>
<td>Slow</td>
<td>Excellent</td>
<td>Extreme debugging</td>
</tr>
<tr>
<td>Size</td>
<td><code>-Os</code></td>
<td>Medium</td>
<td>Poor</td>
<td>Production</td>
</tr>
<tr>
<td>Performance</td>
<td><code>-O2</code></td>
<td>Fast</td>
<td>Poor</td>
<td>Production</td>
</tr>
</tbody>
</table>
<div class="alert alert-warning">
<strong>For debugging, always use <code>-Og</code> (Debug level).</strong> It provides good performance while preserving all variable information for GDB.
</div>
<h3>Build Process</h3>
<pre><code>cd ~/your-project
# Clean previous build
idf.py fullclean
# Build with debug symbols
idf.py build
# Flash to device
idf.py -p /dev/ttyUSB0 flash</code></pre>
<h3>Verify Debug Symbols</h3>
<pre><code># Check if ELF file contains debug sections
riscv32-esp-elf-readelf -S build/your-project.elf | grep debug
# Expected output (debug sections present):
# [27] .debug_aranges PROGBITS 00000000 0f8a2c 004638 00 0 0 8
# [28] .debug_info PROGBITS 00000000 0fd064 19d4f4 00 0 0 1
# [29] .debug_abbrev PROGBITS 00000000 29a558 02b8f9 00 0 0 1
# [30] .debug_line PROGBITS 00000000 2c5e51 0e7a3c 00 0 0 1</code></pre>
</section>
<section id="starting">
<h2>Starting a Debug Session</h2>
<h3>Three-Step Debug Process</h3>
<ol>
<li><strong>Flash the firmware</strong> to the device</li>
<li><strong>Start OpenOCD</strong> to connect to the device</li>
<li><strong>Start GDB</strong> to control debugging</li>
</ol>
<h3>Step 1: Flash Firmware</h3>
<pre><code>cd ~/your-project
idf.py -p /dev/ttyUSB0 flash</code></pre>
<h3>Step 2: Start OpenOCD (Terminal 1)</h3>
<pre><code>cd ~/your-project
idf.py openocd</code></pre>
<div class="alert alert-info">
<strong>Leave this terminal running.</strong> OpenOCD acts as a bridge between GDB and the ESP32-C5.
</div>
<h3>Step 3: Start GDB (Terminal 2)</h3>
<pre><code>cd ~/your-project
idf.py gdb</code></pre>
<p>You're now in the GDB prompt and ready to debug!</p>
<h3>Quick Start Commands</h3>
<pre><code>(gdb) target remote :3333
(gdb) file build/your-project.elf
(gdb) monitor reset halt
(gdb) thbreak app_main
(gdb) continue</code></pre>
</section>
<section id="commands">
<h2>Essential GDB Commands</h2>
<h3>Navigation and Execution</h3>
<table>
<thead>
<tr>
<th>Command</th>
<th>Shortcut</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>break &lt;location&gt;</code></td>
<td><code>b</code></td>
<td>Set breakpoint</td>
</tr>
<tr>
<td><code>continue</code></td>
<td><code>c</code></td>
<td>Resume execution</td>
</tr>
<tr>
<td><code>next</code></td>
<td><code>n</code></td>
<td>Step over (skip function calls)</td>
</tr>
<tr>
<td><code>step</code></td>
<td><code>s</code></td>
<td>Step into (enter functions)</td>
</tr>
<tr>
<td><code>finish</code></td>
<td><code>fin</code></td>
<td>Run until function returns</td>
</tr>
</tbody>
</table>
<h3>Inspection</h3>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>print &lt;var&gt;</code></td>
<td>Print variable value</td>
<td><code>p my_variable</code></td>
</tr>
<tr>
<td><code>print *&lt;ptr&gt;</code></td>
<td>Dereference pointer</td>
<td><code>p *config</code></td>
</tr>
<tr>
<td><code>x/&lt;fmt&gt; &lt;addr&gt;</code></td>
<td>Examine memory</td>
<td><code>x/32xb 0x40000000</code></td>
</tr>
<tr>
<td><code>info locals</code></td>
<td>Show local variables</td>
<td><code>i lo</code></td>
</tr>
<tr>
<td><code>backtrace</code></td>
<td>Show call stack</td>
<td><code>bt</code></td>
</tr>
<tr>
<td><code>list</code></td>
<td>Show source code</td>
<td><code>l</code></td>
</tr>
</tbody>
</table>
<h3>Breakpoints & Watchpoints</h3>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>break &lt;func&gt;</code></td>
<td>Break on function entry</td>
<td><code>b esp_wifi_init</code></td>
</tr>
<tr>
<td><code>break &lt;func&gt; if &lt;cond&gt;</code></td>
<td>Conditional breakpoint</td>
<td><code>b send if len > 1000</code></td>
</tr>
<tr>
<td><code>watch &lt;var&gt;</code></td>
<td>Break when variable changes</td>
<td><code>watch my_counter</code></td>
</tr>
<tr>
<td><code>info breakpoints</code></td>
<td>List all breakpoints</td>
<td><code>i b</code></td>
</tr>
<tr>
<td><code>delete &lt;num&gt;</code></td>
<td>Delete breakpoint</td>
<td><code>d 1</code></td>
</tr>
</tbody>
</table>
</section>
<section id="strategies">
<h2>Debugging Strategies</h2>
<h3>Strategy 1: Breakpoint at Function Entry</h3>
<p><strong>Use case:</strong> Understand when and why a function is called.</p>
<pre><code>(gdb) break esp_wifi_set_csi_config
(gdb) continue
# When it breaks...
(gdb) info args
(gdb) print *config
(gdb) backtrace
(gdb) continue</code></pre>
<h3>Strategy 2: Conditional Breakpoints</h3>
<p><strong>Use case:</strong> Break only when specific conditions occur.</p>
<pre><code># Break only when error occurs
(gdb) break esp_wifi_set_csi_config if $a0 != 0
# Break only for specific SSID
(gdb) break wifi_connect if strcmp(ssid, "MyNetwork") == 0
# Break when buffer is full
(gdb) break send_packet if queue_size >= 100</code></pre>
<h3>Strategy 3: Step Through Algorithm</h3>
<p><strong>Use case:</strong> Understand complex logic step by step.</p>
<pre><code>(gdb) break process_csi_data
(gdb) continue
(gdb) next # Execute current line
(gdb) next # Next line
(gdb) step # Step into function call if any
(gdb) finish # Complete current function</code></pre>
<h3>Strategy 4: Watch for Variable Changes</h3>
<p><strong>Use case:</strong> Find where a variable gets corrupted.</p>
<pre><code>(gdb) watch connection_state
(gdb) continue
# GDB will break when variable changes
(gdb) backtrace
(gdb) print connection_state</code></pre>
</section>
<section id="examples">
<h2>Real-World Examples</h2>
<h3>Example 1: Debug CSI Configuration Failure</h3>
<p><strong>Problem:</strong> <code>esp_wifi_set_csi_config()</code> returns <code>ESP_FAIL</code> but we don't know why.</p>
<pre><code>(gdb) break esp_wifi_set_csi_config
Breakpoint 1 at 0x42012a4e
(gdb) continue
Breakpoint 1, esp_wifi_set_csi_config (config=0x3ffb0000)
# Examine the config structure
(gdb) print *config
$1 = {
enable = 1,
lltf_en = 1,
htltf_en = 1,
stbc_htltf2_en = 1,
ltf_merge_en = 1,
channel_filter_en = 1, ← Suspicious!
manu_scale = 0
}
# Step through to see where it fails
(gdb) step
(gdb) step
...
(gdb) print error_code
$2 = 259 ← ESP_FAIL (0x103)
# Found it! channel_filter_en must be 0 on ESP32-C5</code></pre>
<div class="alert alert-success">
<strong>Solution:</strong> Set <code>channel_filter_en = 0</code> in the code.
</div>
<h3>Example 2: Find Memory Corruption</h3>
<p><strong>Problem:</strong> A pointer is getting corrupted, causing crashes.</p>
<pre><code># Set watchpoint on the pointer
(gdb) watch *(void**)&my_buffer_ptr
Hardware watchpoint 2: *(void**)&my_buffer_ptr
# Run until it changes
(gdb) continue
Hardware watchpoint 2: *(void**)&my_buffer_ptr
Old value = (void *) 0x3ffb1000
New value = (void *) 0x00000000
# See what code changed it
(gdb) backtrace
#0 process_packet (data=0x3ffb0800) at network.c:142
#1 0x42008654 in network_task () at network.c:201
# Look at the source
(gdb) list
137 void process_packet(uint8_t *data) {
138 if (data == NULL) {
139 ESP_LOGE(TAG, "Null data!");
140 my_buffer_ptr = NULL; ← Found it!
141 return;
142 }</code></pre>
<div class="alert alert-success">
<strong>Solution:</strong> Fix the null-pointer handling logic.
</div>
<h3>Example 3: Understand WiFi Connection Failure</h3>
<p><strong>Problem:</strong> WiFi connects but immediately disconnects.</p>
<pre><code>(gdb) break event_handler
(gdb) condition 1 event_id == WIFI_EVENT_STA_DISCONNECTED
(gdb) continue
Breakpoint 1, event_handler (event_id=3, event_data=0x3ffb2000)
# Examine disconnect reason
(gdb) print *(wifi_event_sta_disconnected_t*)event_data
$1 = {
ssid = "ClubHouse",
ssid_len = 9,
bssid = {0xe0, 0x46, 0xee, 0x07, 0xdf, 0x01},
reason = 2, ← WIFI_REASON_AUTH_EXPIRE
rssi = -75
}
# Reason 2 = Authentication expired = weak signal or interference</code></pre>
<div class="alert alert-success">
<strong>Solution:</strong> Improve antenna placement or reduce distance to AP.
</div>
</section>
<section id="troubleshooting">
<h2>Troubleshooting</h2>
<h3>Problem: "No symbol table is loaded"</h3>
<div class="alert alert-warning">
<strong>Symptom:</strong>
<pre><code>(gdb) break app_main
Function "app_main" not defined.</code></pre>
</div>
<p><strong>Solutions:</strong></p>
<pre><code># 1. Rebuild with debug symbols
idf.py menuconfig # Set optimization to Debug (-Og)
idf.py fullclean build
# 2. Load correct ELF file in GDB
(gdb) file build/your-project.elf
# 3. Verify symbols exist
riscv32-esp-elf-nm build/your-project.elf | grep app_main</code></pre>
<h3>Problem: "Cannot access memory at address 0x..."</h3>
<p><strong>Causes:</strong> Variable optimized out, out of scope, or invalid pointer</p>
<p><strong>Solutions:</strong></p>
<pre><code># Check if variable exists
(gdb) info locals
(gdb) info args
# Examine raw memory
(gdb) print &my_variable
(gdb) x/4xw 0x3ffb0000</code></pre>
<h3>Problem: Breakpoint Not Hitting</h3>
<p><strong>Solutions:</strong></p>
<pre><code># Check breakpoint status
(gdb) info breakpoints
# Try software breakpoint
(gdb) delete 1
(gdb) break my_func</code></pre>
</section>
<section id="advanced">
<h2>Advanced Techniques</h2>
<h3>Technique 1: Scripting GDB</h3>
<p>Create a <code>.gdbinit</code> file to automate common tasks:</p>
<pre><code># ~/.gdbinit or project/.gdbinit
# Connect automatically
target remote :3333
file build/CSI.elf
# Define custom commands
define reset-and-break
monitor reset halt
thbreak app_main
continue
end
# Set common breakpoints
break esp_wifi_set_csi_config
break esp_wifi_connect</code></pre>
<h3>Technique 2: Debugging FreeRTOS Tasks</h3>
<pre><code># Show all tasks
(gdb) info threads
Id Target Id Frame
* 1 Remote target vTaskDelay ()
2 Remote target prvIdleTask ()
3 Remote target wifi_task ()
# Switch to different task
(gdb) thread 3
# See that task's stack
(gdb) backtrace</code></pre>
<h3>Technique 3: Live Variable Modification</h3>
<p>Change variables on-the-fly without recompiling:</p>
<pre><code>(gdb) break send_packet
(gdb) continue
# Change packet size before sending
(gdb) print packet_size
$1 = 1024
(gdb) set packet_size = 64
# Continue with modified value
(gdb) continue</code></pre>
</section>
<section id="resources">
<h2>Resources</h2>
<h3>Official Documentation</h3>
<ul>
<li><a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c5/api-guides/jtag-debugging/">ESP-IDF GDB Guide</a></li>
<li><a href="https://www.espressif.com/sites/default/files/documentation/esp32-c5_datasheet_en.pdf">ESP32-C5 Datasheet</a></li>
<li><a href="http://openocd.org/doc/html/index.html">OpenOCD Manual</a></li>
<li><a href="https://sourceware.org/gdb/current/onlinedocs/gdb/">GDB Manual</a></li>
</ul>
<h3>ESP32 Community</h3>
<ul>
<li><a href="https://esp32.com/">ESP32 Forum</a></li>
<li><a href="https://reddit.com/r/esp32">r/esp32 Subreddit</a></li>
<li><a href="https://github.com/espressif/esp-idf">Espressif GitHub</a></li>
</ul>
</section>
<section id="summary">
<h2>Summary</h2>
<p>GDB debugging on the ESP32-C5 provides powerful insights into firmware behavior:</p>
<div class="alert alert-success">
<ul>
<li class="checkmark"><strong>Built-in USB-JTAG</strong> eliminates external hardware requirements</li>
<li class="checkmark"><strong>Hardware and software breakpoints</strong> for flexible debugging</li>
<li class="checkmark"><strong>Real-time variable inspection</strong> without printf statements</li>
<li class="checkmark"><strong>Watchpoints</strong> to catch memory corruption</li>
<li class="checkmark"><strong>Call stack analysis</strong> to understand program flow</li>
<li class="checkmark"><strong>ESP-IDF driver debugging</strong> to troubleshoot library issues</li>
</ul>
</div>
<p><strong>Key takeaways:</strong></p>
<ol>
<li>Always build with <strong>Debug (-Og)</strong> optimization for best debug experience</li>
<li>Use <strong>conditional breakpoints</strong> to break only when needed</li>
<li>Combine <strong>watchpoints</strong> with breakpoints to find memory corruption</li>
<li><strong>Script common tasks</strong> in <code>.gdbinit</code> for faster debugging</li>
<li>The <strong>WiFi driver log</strong> is still the ground truth for connection status</li>
</ol>
<p>GDB debugging significantly reduces debug time compared to printf-based approaches, especially for complex issues like WiFi driver bugs, FreeRTOS task interactions, and memory corruption.</p>
</section>
</main>
<footer>
<p><strong>About this guide:</strong> Created based on real-world ESP32-C5 development experience, specifically debugging WiFi 6 CSI (Channel State Information) capture issues for the iperf WiFi Analyzer project.</p>
<p>
<a href="https://github.com/iperf2/iperf2" class="button">iperf2 GitHub</a>
<a href="https://sourceforge.net/projects/iperf2/" class="button">iperf2 SourceForge</a>
</p>
<p><strong>Hardware:</strong> ESP32-C5 DevKit<br>
<strong>Project:</strong> WiFi Collapse Detection using CSI</p>
<p>&copy; 2025 Bob McMahon. Last updated: December 4, 2025</p>
</footer>
</body>
</html>