Repository navigation
gdbserver: commit breakpoint removals before replying OK - #2048
Open
j4rvisstant wants to merge 1 commit into
Open
j4rvisstant wants to merge 1 commit into
j4rvisstant wants to merge 1 commit into
Conversation
A z0/z1 removal was only recorded and applied on the next resume or step, so a session that ended first left the breakpoint armed on the target. When the core is halted, the handlers now commit pending removals through the new BreakpointManager.flush_removals() before replying, and reply E01 if that fails. Additions, and removals while the core runs, stay deferred. Assisted-by: Claude Code Assisted-by: OpenAI Codex
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2014.
Problem
When GDB removes a breakpoint with
z0/z1while the core is halted, the gdbserver repliesOKonceBreakpointManager.remove_breakpoint()has recorded the removal, but nothing has been written to the target yet. The FPB comparator, or the original instruction under a software breakpoint, is restored on the next resume or step. A session that ends without one, as in #2014 (GDB disconnects and the frontend terminates the server), leaves the deleted breakpoint armed, and the target stops on it again later.Change
BreakpointManager.flush_removals()commits pending removals, and only removals, while the core is halted, then drains the probe's queued transfers. Pending additions stay deferred untilflush(), as before. If a provider keeps a breakpoint it was asked to remove,DebugErroris raised and the breakpoint stays pending for the nextflush().z0/z1handlers call it before replying. If the removal cannot be committed, they log the error and replyE01instead ofOK.Observed on hardware
Agentic Hardware-in-the-Loop (Agentic HIL) is an open-source toolchain, maintained by us, that puts real embedded boards into an AI agent's build, flash and debug loop. On pyOCD it runs its debug sessions through
pyocd gdbserver. A GDB detach resumes the core, so it ends the server while GDB is still connected, which leaves the core halted (backend notes).Its test suite includes a hardware recorder that drives
pyocd gdbserverand GDB directly against a NUCLEO-F446RE (Cortex-M4) to record the behaviour that backend is built against (recorder). With pyOCD 0.45.1 and GDB 14.2.90 it ran this sequence: breakpoint onSysTick_Handler, continue, stop on it,-break-delete, then SIGTERM to the server while GDB was still connected. Three freshpyocd gdbserverconnects followed, two seconds apart, each reading the PC and the firmware's millisecond counter (recording).DebugCoreStartsequence runs. In the control, where the session halted the core with an interrupt instead of a breakpoint, the counter moved from 3615 to 3641 at the first connect.SysTick_Handler+4with the counter unchanged at 3717. The deleted breakpoint was still armed and stopped the core as soon as it ran. The counter moved again only at the following connects (3738, 3765).The breakpoint manager and the
z0/z1handler are unchanged between 0.45.1 and develop, and the unit tests below fail on develop in the same way. This change itself has not been run on hardware yet.Trade-off
With GDB's default
set breakpoint always-inserted off, GDB removes its breakpoints at every stop and inserts them again before resuming. Before this change such a remove/insert pair cancelled out inside the manager. Now each stop writes the removals to the target, and the next resume writes the breakpoints back. The cost has not been measured.An alternative is to keep removals deferred and commit them on detach, disconnect and shutdown, including a SIGTERM handler. That leaves the stop/resume path as it is. It does not help a server that is killed without running a handler, though, and the
OKwould still not mean that the target was updated.Not addressed
GDBServercreated without a core number targets the SoC object, which has nobp_manager, so removals there stay deferred as before. The command-line gdbserver always passes a core.FPB.remove_breakpoint()marks a comparator unused before writing it. If that write fails, a retry does not rewrite the comparator. This is pre-existing and unchanged here.Tests
New
test/unit/test_gdbserver_breakpoints.pydrivesz0/z1packets throughGDBServer.breakpoint(). It runs against the realBreakpointManager,FPBand software breakpoint provider, over a fake core and a dict-backed fake access port (test/unit/mock_breakpoints.py). On develop without this change, 7 of its 10 tests fail:z1, the modelledFP_COMP0is still enabled.z0in RAM, theBKPTinstruction is still in memory.OK.The other 3 tests cover behaviour that does not change.
New
test/unit/test_breakpoint_manager.py(12 tests) coversflush_removals()itself.Full unit suite (
pytest): 1122 passed, 41 skipped. flake8 with the CI selection: 0 findings.test/import_all.py: 376 modules imported. These ran on Windows 11 with Python 3.13, on develop f8ca7e4.Disclosure
We maintain Agentic HIL. This change and its tests were written by AI coding agents (Claude Code and OpenAI Codex) as part of our upstream work on the debug tools Agentic HIL uses, and reviewed by AI agents. No human has reviewed the code yet. Agentic HIL's part is the hardware recording above, made with pyOCD 0.45.1 before this change; the change itself has not been run on hardware. The commit carries
Assisted-by:trailers.