bkpt_gdbserver
A GDB remote-serial-protocol server for Cortex-M targets behind an ST-LINK, a J-Link, or the BKPT#1 probe. It is what BKPT Debug and BKPT Studio run for a debug session; it can also be used by hand with any arm-none-eabi-gdb.
bkpt_gdbserver [--probe vacore|stlink|jlink] [--serial S] [--speed KHZ] [--device NAME] [--jlink-dir DIR]
[--tcp PORT] [--bind 127.0.0.1|0.0.0.0] [--no-reset] [--maskisr off|auto|on]
[--live-tcp PORT] [--trace-tcp PORT] [--profile-tcp PORT] [--swo-tcp PORT]
[--flm CHIP.FLM [--ram-base 0xADDR] [--ram-size 0xSIZE]] [--trace-support FILE]
$ bkpt_gdbserver --probe stlink --serial 0033004B3033510735393935 --tcp 2331 --live-tcp 2332 --profile-tcp 2334 --swo-tcp 2335
>> probe stlink 0033004B3033510735393935 (STLINK-V3 fw J17 @ 24000 kHz ap0)
>> GDB server listening on 127.0.0.1:2331 -- (gdb) target remote :2331
>> live-read channel on 127.0.0.1:2332 (r ADDR LEN -> ok HEXBYTES)
>> live-profile channel on 127.0.0.1:2334 (s MILLISECONDS -> ok N sleep=M rate=R PC:COUNT ...; q -> ok pcsr=0|1; h -> ok <hwtrace status json>; w -> ok swo=...)
>> raw SWO channel on 127.0.0.1:2335 (push: probe SWV bytes while 'monitor swo start' runs)
(gdb) target remote :2331
(gdb) load # with --flm
(gdb) monitor reset halt
(gdb) break main
(gdb) continue
Options
| OPTION | MEANING |
|---|---|
--probe | vacore (default, the BKPT#1 FPGA probe over --port /dev/ttyUSBn, --swclk MHZ), stlink, jlink |
--serial, --speed, --device, --jlink-dir | Probe selection, SWD clock in kHz, SEGGER device name, SEGGER install directory |
--tcp PORT | The GDB port (default 2331) |
--bind | 127.0.0.1 (default) or 0.0.0.0 for a gdb on another machine |
--no-reset | Attach to the running program instead of resetting it at connect |
--maskisr | Interrupt masking while debugging (DHCSR.C_MASKINTS): off, auto (mask during single-step, default), on (hold across continue); also monitor maskisr live |
--flm CHIP.FLM | A CMSIS-Pack flash algorithm: required for load and any flash write, on any MCU or vendor. Without it the server is debug-only. --ram-base, --ram-size place the algorithm when the defaults do not fit |
--trace-support FILE | Override the bundled vendor trace bring-up catalog (trace-support.json) used by monitor swo start and hwtrace apply |
--live-tcp, --trace-tcp, --profile-tcp, --swo-tcp | The side channels, below; each off unless given |
--dry-run --arch v7m|v8m|v6m [--caps JSON] --cpu-hz N --swo-hz N --itm-port P --hwtrace JSON|@FILE | Print the hardware-trace register image for those inputs and exit; no probe, no USB. The same image as viewalyzer-cli hwtrace --dry-run, pinned by a shared conformance table |
Session behaviour
- Connecting resets the target and halts at the reset vector (or attaches running with
--no-reset); GDB'sdetachresumes it with the interrupt mask dropped. - Breakpoints are hardware (FPB) up to the core's count; watchpoints use the DWT comparators (
watch,rwatch,awatch), and a hit reports which comparator matched. - Memory reads and writes go through the MEM-AP while the core runs, so a
printin a running program works and the live channel exists. - The server ends the session by itself when its stdin closes or on SIGTERM/SIGINT: it disarms hardware trace and the fault catch, stops the SWO receiver, releases the probe (an ST-LINK gets
DBG_EXIT), and exits. A host that spawns it should close stdin and wait rather than kill it; a process killed mid-transfer leaves an ST-LINK needingbkpt_probe reset-usbor a power cycle. - Flash programming (
load,restore) needs--flm; the algorithm runs from RAM and the server verifies what it wrote.
monitor commands
(gdb) monitor help
supported monitor commands: reset [halt|run], halt, maskisr [off|auto|on], caps,
faultcatch [all|off|hard bus mem usage state chk nocp int sf], fault [fresh],
etm arm|drain out=<path>|disarm, hwtrace apply <json>|status|clear, swo start <swo_hz> <cpu_hz>|stop
| COMMAND | WHAT IT DOES |
|---|---|
reset [halt|run] | Reset the core, then halt at the vector (default) or run |
halt | Halt the core |
maskisr off|auto|on | The interrupt mask policy, live |
caps | The core's debug capabilities (breakpoints, comparators, trace blocks) as JSON |
faultcatch all|off|hard bus mem usage state chk nocp int sf | Vector catch: halt on the named fault classes before the handler runs (DEMCR.VC_*), so a fault stops at the faulting instruction with the registers intact |
fault [fresh] | The decoded fault report for the current halt (the bkpt_probe fault report), fresh re-reads the registers |
hwtrace apply <json> | Program the hardware-trace image (ITM, TPIU, DWT: PC sampling, exception trace, data watches, counters) from a hardware-trace JSON block, at a halt; status re-reads and reports the image; clear disarms everything the server armed |
swo start <swo_hz> <cpu_hz> | Start the probe's SWO receiver at that rate (rounded to the rate the TPIU's integer prescaler actually produces), bring up the vendor trace pins from the catalog, push the bytes on --swo-tcp; stop ends it |
etm arm|drain out=<path>|disarm | BKPT#1 only: arm the ETM and the FPGA ring, drain a capture to a file, disarm |
monitor runs only while the core is halted (a GDB rule): a host arms hardware trace at the halt after attach, and re-arms comparators at the next halt when rows change while running.
Side channels
All on 127.0.0.1 (or --bind), one client each, line protocols, alive for the length of a GDB session. They exist so an IDE can watch a running program without halting it.
| CHANNEL | PROTOCOL | PURPOSE |
|---|---|---|
--live-tcp | r ADDR LEN → ok HEXBYTES (err ...) | Live memory reads while the core runs: the BKPT Debug live watch polls variables here at up to hundreds of hertz |
--trace-tcp | The same read/write memory protocol, a separate client | Recorder and trace drains (a ViewAlyzer RAM-buffer capture during a debug session) alongside the live watch |
--profile-tcp | s MILLISECONDS → ok N sleep=M rate=R PC:COUNT ...; q → ok pcsr=0|1; h → ok <hwtrace status JSON>; w → ok swo=on|off hz=N bytes=N dropped=N | Non-halting statistical PC samples through DWT_PCSR (sleep = reads that returned 0xFFFFFFFF, the core in WFI/WFE; rate = reads per second achieved), the hardware-trace image and the SWO receiver state, all usable while the core runs |
--swo-tcp | Push: the raw SWO bytes | The probe's SWV receiver output while monitor swo start runs; viewalyzer-cli capture --transport swo-tcp --swo-tcp-port N decodes it (recorder stream, ITM console, PC samples, exception trace, data watches) |
A client that cannot keep up is dropped rather than allowed to stall the GDB loop; the SWO channel keeps a 64 KB pending buffer.
Hardware trace, end to end
With an ST-LINK or J-Link and the target's SWO pin wired:
(gdb) monitor swo start 10000000 170000000
swo: receiver on at 10000000 Hz (cpu 170000000 Hz), bytes on the --swo-tcp channel
(gdb) monitor hwtrace apply {"dwt":{"enable":true,"exception-trace":true,"pc-sample-cyc":16384,"watch":[{"addr":"0x200000E4","size":4,"function":"data-w","name":"sig_noise"}]}}
hwtrace: applied
(gdb) continue
$ viewalyzer-cli capture --transport swo-tcp --swo-tcp-port 2335 --cpu-clock-hz 170000000 --swo-freq-hz 10000000 --itm-port 1 \
--hardware-trace '{"dwt":{"enable":true,"exception-trace":true,"pc-sample-cyc":16384}}' --elf firmware.elf --output session.vadb --stream
monitor hwtrace status prints the applied image with the registers read back, and the refusals with their reasons when the core lacks a feature (the same verdicts as bkpt_probe detect-trace).
Fault catching
(gdb) monitor faultcatch all
(gdb) continue
Program received signal SIGSEGV, Segmentation fault. # halted before the handler ran
(gdb) monitor fault
{ "event": "fault", "caught": "vector-catch", "exception": {"number": 6, "name": "UsageFault"}, "root_cause": "INVSTATE", ... }
faultcatch sets the vector-catch bits so the core halts at the faulting instruction with the registers and the stack untouched; fault decodes the state. Both survive across continue; faultcatch off clears them, and detach clears whatever the server armed.
Logging
The server prints its log on stderr (>> lines; !! for probe errors), which the IDE shows in its Output pane. >> slow dispatch 'vCont;c' held the loop N ms (live channel starved) is informational: a long continue held the packet loop, which is normal for a running program.