From 858ba8192329299880153a776bb7f6d0136cfd3f Mon Sep 17 00:00:00 2001 From: Bob Date: Thu, 4 Dec 2025 16:33:41 -0800 Subject: [PATCH] add gdb html --- ESP32-C5_GDB_Debugging_Guide.html | 951 ++++++++++++++++++++++++++++++ 1 file changed, 951 insertions(+) create mode 100644 ESP32-C5_GDB_Debugging_Guide.html diff --git a/ESP32-C5_GDB_Debugging_Guide.html b/ESP32-C5_GDB_Debugging_Guide.html new file mode 100644 index 0000000..d503970 --- /dev/null +++ b/ESP32-C5_GDB_Debugging_Guide.html @@ -0,0 +1,951 @@ + + + + + + ESP32-C5 GDB Debugging Guide + + + +
+

ESP32-C5 GDB Debugging Guide

+

Author: Bob McMahon

+

Hardware: ESP32-C5 DevKit (RISC-V)

+

ESP-IDF: v6.0 or later

+

Last Updated: December 2025

+
+ + + +
+
+

Introduction

+

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.

+ +

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.

+
+ +
+

Why GDB Debugging?

+ +

Traditional debugging with ESP_LOGI() statements has limitations:

+ + + + + + + + + + + + + + + + + + +
MethodLimitations
Printf Debugging + • Alters timing and behavior
+ • Cannot inspect internal driver state
+ • Requires recompilation for each change
+ • Output floods serial console +
LED Blink Debugging + • Very limited information
+ • Time-consuming iteration
+ • Cannot show complex state +
+ +
+ GDB debugging solves these problems: +
    +
  • Set breakpoints without modifying code
  • +
  • Inspect variables at any point in execution
  • +
  • Step through code line by line
  • +
  • Examine memory and registers
  • +
  • Watch variables for changes
  • +
  • View call stacks to understand program flow
  • +
  • Debug ESP-IDF internals (WiFi driver, FreeRTOS, etc.)
  • +
+
+
+ +
+

ESP32-C5 Debug Capabilities

+ +

The ESP32-C5 has built-in USB-JTAG support, eliminating the need for external debug adapters:

+ +

Hardware Features

+
    +
  • Built-in USB-JTAG: Debug over the same USB cable used for flashing
  • +
  • 4 Hardware Breakpoints: No speed penalty
  • +
  • Unlimited Software Breakpoints: Via flash patching
  • +
  • 2 Watchpoints: Trigger on memory read/write
  • +
  • Real-time Debugging: Debug live, running firmware
  • +
+ +

Comparison with Other ESP32 Chips

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureESP32 (Xtensa)ESP32-S3 (Xtensa)ESP32-C5 (RISC-V)
Debug InterfaceExternal JTAG requiredBuilt-in USB-JTAGBuilt-in USB-JTAG
Debuggerxt-gdb (Xtensa)xt-gdb (Xtensa)riscv32-esp-elf-gdb
Setup ComplexityHigh (extra hardware)MediumLow (just USB)
OpenOCD SupportMatureMatureGood (ESP-IDF v6.0+)
+
+ +
+

Prerequisites

+ +

Hardware

+
    +
  • ESP32-C5 DevKit with USB-C cable
  • +
  • Host Computer running Linux, macOS, or Windows (WSL2)
  • +
+ +

Software

+
    +
  • ESP-IDF v6.0 or later (ESP32-C5 support)
  • +
  • OpenOCD (included with ESP-IDF)
  • +
  • GDB for RISC-V (riscv32-esp-elf-gdb, included with ESP-IDF)
  • +
+ +

Verify Installation

+
# 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
+
+ +
+

Building with Debug Symbols

+ +

Debug symbols allow GDB to map machine code back to source code, showing variable names, function names, and line numbers.

+ +

Method 1: Using menuconfig (Recommended)

+
cd ~/your-project
+idf.py menuconfig
+ +

Navigate to and configure:

+
Component config
+  → Compiler options
+    → Optimization Level → Debug (-Og)       ← Select this
+    → [*] Generate debug symbols (-g)        ← Enable
+    → Debug information format → DWARF-4     ← Select
+ +

Additional recommended settings:

+
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
+ +

Optimization Levels Explained

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LevelGCC FlagCode SpeedDebug QualityUse Case
Debug-OgMediumExcellentGDB debugging
None-O0SlowExcellentExtreme debugging
Size-OsMediumPoorProduction
Performance-O2FastPoorProduction
+ +
+ For debugging, always use -Og (Debug level). It provides good performance while preserving all variable information for GDB. +
+ +

Build Process

+
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
+ +

Verify Debug Symbols

+
# 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
+
+ +
+

Starting a Debug Session

+ +

Three-Step Debug Process

+
    +
  1. Flash the firmware to the device
  2. +
  3. Start OpenOCD to connect to the device
  4. +
  5. Start GDB to control debugging
  6. +
+ +

Step 1: Flash Firmware

+
cd ~/your-project
+idf.py -p /dev/ttyUSB0 flash
+ +

Step 2: Start OpenOCD (Terminal 1)

+
cd ~/your-project
+idf.py openocd
+ +
+ Leave this terminal running. OpenOCD acts as a bridge between GDB and the ESP32-C5. +
+ +

Step 3: Start GDB (Terminal 2)

+
cd ~/your-project
+idf.py gdb
+ +

You're now in the GDB prompt and ready to debug!

+ +

Quick Start Commands

+
(gdb) target remote :3333
+(gdb) file build/your-project.elf
+(gdb) monitor reset halt
+(gdb) thbreak app_main
+(gdb) continue
+
+ +
+

Essential GDB Commands

+ +

Navigation and Execution

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandShortcutDescription
break <location>bSet breakpoint
continuecResume execution
nextnStep over (skip function calls)
stepsStep into (enter functions)
finishfinRun until function returns
+ +

Inspection

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescriptionExample
print <var>Print variable valuep my_variable
print *<ptr>Dereference pointerp *config
x/<fmt> <addr>Examine memoryx/32xb 0x40000000
info localsShow local variablesi lo
backtraceShow call stackbt
listShow source codel
+ +

Breakpoints & Watchpoints

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescriptionExample
break <func>Break on function entryb esp_wifi_init
break <func> if <cond>Conditional breakpointb send if len > 1000
watch <var>Break when variable changeswatch my_counter
info breakpointsList all breakpointsi b
delete <num>Delete breakpointd 1
+
+ +
+

Debugging Strategies

+ +

Strategy 1: Breakpoint at Function Entry

+

Use case: Understand when and why a function is called.

+
(gdb) break esp_wifi_set_csi_config
+(gdb) continue
+# When it breaks...
+(gdb) info args
+(gdb) print *config
+(gdb) backtrace
+(gdb) continue
+ +

Strategy 2: Conditional Breakpoints

+

Use case: Break only when specific conditions occur.

+
# 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
+ +

Strategy 3: Step Through Algorithm

+

Use case: Understand complex logic step by step.

+
(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
+ +

Strategy 4: Watch for Variable Changes

+

Use case: Find where a variable gets corrupted.

+
(gdb) watch connection_state
+(gdb) continue
+# GDB will break when variable changes
+(gdb) backtrace
+(gdb) print connection_state
+
+ +
+

Real-World Examples

+ +

Example 1: Debug CSI Configuration Failure

+

Problem: esp_wifi_set_csi_config() returns ESP_FAIL but we don't know why.

+ +
(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
+ +
+ Solution: Set channel_filter_en = 0 in the code. +
+ +

Example 2: Find Memory Corruption

+

Problem: A pointer is getting corrupted, causing crashes.

+ +
# 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         }
+ +
+ Solution: Fix the null-pointer handling logic. +
+ +

Example 3: Understand WiFi Connection Failure

+

Problem: WiFi connects but immediately disconnects.

+ +
(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
+ +
+ Solution: Improve antenna placement or reduce distance to AP. +
+
+ +
+

Troubleshooting

+ +

Problem: "No symbol table is loaded"

+
+ Symptom: +
(gdb) break app_main
+Function "app_main" not defined.
+
+ +

Solutions:

+
# 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
+ +

Problem: "Cannot access memory at address 0x..."

+

Causes: Variable optimized out, out of scope, or invalid pointer

+ +

Solutions:

+
# Check if variable exists
+(gdb) info locals
+(gdb) info args
+
+# Examine raw memory
+(gdb) print &my_variable
+(gdb) x/4xw 0x3ffb0000
+ +

Problem: Breakpoint Not Hitting

+

Solutions:

+
# Check breakpoint status
+(gdb) info breakpoints
+
+# Try software breakpoint
+(gdb) delete 1
+(gdb) break my_func
+
+ +
+

Advanced Techniques

+ +

Technique 1: Scripting GDB

+

Create a .gdbinit file to automate common tasks:

+ +
# ~/.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
+ +

Technique 2: Debugging FreeRTOS Tasks

+
# 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
+ +

Technique 3: Live Variable Modification

+

Change variables on-the-fly without recompiling:

+ +
(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
+
+ +
+

Resources

+ +

Official Documentation

+ + +

ESP32 Community

+ +
+ +
+

Summary

+ +

GDB debugging on the ESP32-C5 provides powerful insights into firmware behavior:

+ +
+
    +
  • Built-in USB-JTAG eliminates external hardware requirements
  • +
  • Hardware and software breakpoints for flexible debugging
  • +
  • Real-time variable inspection without printf statements
  • +
  • Watchpoints to catch memory corruption
  • +
  • Call stack analysis to understand program flow
  • +
  • ESP-IDF driver debugging to troubleshoot library issues
  • +
+
+ +

Key takeaways:

+
    +
  1. Always build with Debug (-Og) optimization for best debug experience
  2. +
  3. Use conditional breakpoints to break only when needed
  4. +
  5. Combine watchpoints with breakpoints to find memory corruption
  6. +
  7. Script common tasks in .gdbinit for faster debugging
  8. +
  9. The WiFi driver log is still the ground truth for connection status
  10. +
+ +

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.

+
+
+ + + +