Skip to content

GDB debugging

Live debugging, flashing, and tracing on Cortex-M targets over ARM GDB.

This comes from the Cortex M Debugger plugin. Install it from the Marketplace, or from a session:

hydron plugin add cortex-m-debugger

See Plugins.

What it covers

Cores Cortex-M0, M0+, M3, M4, M7, M23, M33, and beyond
Vendors STM32, NXP, Nordic, Microchip SAM, TI, Silicon Labs, and others
Probes and servers ST-LINK, J-Link, CMSIS-DAP, OpenOCD, pyOCD, vendor tools
Host Windows, macOS, Linux
Protocol ARM GDB with the GDB MI interface

Nothing is hardcoded to a chip, board, probe, or OS. Commands either detect their target from the environment or read it from your target profile.

Set up a target profile

Fill this in once per project. It holds the parameters every other operation treats as inputs: core type, memory map, GDB port, clock speed, pinout, and which probe backend you use.

Set up a target profile for this board. It is an STM32G4 on ST-LINK.
Read the memory map from the linker script.

Without a profile the agent has to ask for a port or a flash base address every time, or guess at one.

Toolchain resolution

Before anything runs, the plugin resolves arm-none-eabi-gdb and your backend tools for the current OS. It checks PATH, then per-OS install locations, and caches the result for 24 hours.

It never hardcodes an absolute path, because install locations differ by OS, IDE version, and user.

Resolve the ARM toolchain and tell me which GDB and programmer you found.

Attach and inspect

Attach to the target and tell me where it is stopped.
Set a breakpoint at main.c:142, continue, and when it hits show me r0
through r3 and the call stack.

Available once attached: breakpoints and watchpoints, step, next, step-instruction, finish, and reads of registers, memory, locals, globals, stack frames, threads, and disassembly.

Register reads are batched rather than looped one per round trip, which is what makes an interactive session usable.

Diagnose a fault

The board is sitting in HardFault_Handler. Decode the fault registers
and tell me what caused it.

The plugin reads the six contiguous SCB fault registers in a single batched read and decodes CFSR, HFSR, BFAR, and MMFAR, covering HardFault, BusFault, MemManage, stack overflow, and TrustZone secure faults.

Flash and iterate

Flash the build and confirm the board comes up.

For tight edit-build-test loops there is a strategy ladder rather than a full flash every time: SRAM execute, GDB load, differential update, dual-bank live update, hash-skip when nothing changed, and an auto-watcher that reflashes on rebuild. The agent picks the cheapest strategy that is still correct for what changed.

This flash cycle is too slow. Use the fastest strategy that is still
correct for a change confined to one .c file.

Trace

SWO and ITM give printf-style output without stopping the target.

Set up SWO trace and stream the log output while it runs.

SWO pin, baud, and CPU frequency come from the target profile.

Advanced

DWT profiling, conditional and hardware breakpoints with ignore counts, a variable logger, core-dump capture on crash, a peripheral monitor, and an interactive debug REPL.

Register-level peripheral inspection covers GPIO, USART, SPI, I2C, Timer, RCC, NVIC, MPU, and DWT.

Core differences that bite

Check the core before assuming a debug feature exists. M0 and M0+ have no DWT comparators, so hardware watchpoints are unavailable, no fault status registers, so faults escalate straight to HardFault_Handler with no further classification, and no ITM or SWO, so trace does not apply.

The plugin confirms the core at runtime from CPUID at 0xE000ED00 rather than inferring it from the part number.

Server safety

Starting or stopping a GDB server is deliberately not automatic. It needs your explicit confirmation, and the plugin tracks which server process it started.

It will not kill a server it did not launch. On a shared bench that is the difference between restarting your own session and killing a colleague's.

Checking whether a server is already listening is a read-only TCP probe and runs without confirmation.

Sessions are reused

The plugin will not restart GDB or reconnect a live session unless that session is proven dead. A debug session holds state that is expensive to rebuild, so continuity is the default.

Troubleshoot

  • Connection refused, or an attach that times out. Check whether a server is already listening before assuming the port in your profile is wrong.
  • Command not found, or GDB not recognised. Resolve the toolchain rather than hardcoding a path.
  • A watchpoint silently does nothing. The core has no DWT comparators. Confirm the core first.
  • Fault registers read as garbage. Same cause. M0 and M0+ have none.
  • The build blocks debugging. Compile and link flags, linker script symbols, and project structure are covered. If a CMakeLists.txt exists, build through it rather than calling arm-none-eabi-gcc directly.

Next