Skip to content

Feat: Setting Hardware/Software Breakpoint Types via Editor#211

Open
asimgunes wants to merge 2 commits intoeclipse-cdt-cloud:mainfrom
asimgunes:feat/breakpoint-types
Open

Feat: Setting Hardware/Software Breakpoint Types via Editor#211
asimgunes wants to merge 2 commits intoeclipse-cdt-cloud:mainfrom
asimgunes:feat/breakpoint-types

Conversation

@asimgunes
Copy link
Copy Markdown

@asimgunes asimgunes commented Mar 19, 2026

Summary

This PR exposes hardware and software breakpoint type selection to end users directly from the editor context menu, making a capability that has long existed at the adapter level easy to discover and use.

The underlying DAP support for breakpoint modes was already implemented in cdt-gdb-adapter. This change wires that adapter capability into the VS Code extension UI so users no longer need to manually configure breakpoint types through launch configuration or GDB console commands.

This feature was discussed and tracked as an initiative in CDT Cloud community meetings (starting February 2024), with evaluation of the GDB DAP adapter proof-of-concept carried out earlier. This PR represents the VS Code extension layer of that initiative.

Changes

  • src/BreakpointModesController.ts — New controller that registers two commands:

    • cdt.debug.breakpoints.addHardwareBreakpoint
    • cdt.debug.breakpoints.addSoftwareBreakpoint

    Each command removes any existing breakpoint on the target line and re-adds it with the desired mode injected via a workaround on the SourceBreakpoint object (necessary due to a current VS Code API limitation — the mode is passed correctly through DAP but is not reflected in the breakpoints panel UI).

    The controller also exports arePathsEqual, a path comparison utility that handles normalisation and Windows case/slash insensitivity.

  • src/extension.ts — Activates the BreakpointModesController.

  • package.json — Contributes the two commands and surfaces them in the editor line number context menu (editor/lineNumber/context), with inDebugMode guards in the Command Palette.

How It Works

Right-clicking on a line number in the editor while a debug session is active shows:

  • CDT-GDB: Set Hardware Breakpoint — sets a hardware breakpoint on that line
  • CDT-GDB: Set Software Breakpoint — sets a software breakpoint on that line

If a breakpoint already exists on the line with the requested mode, the command is a no-op. Otherwise the existing breakpoint is replaced.

image

Known Limitation

VS Code does not currently expose a public API to set mode on a SourceBreakpoint. The mode is injected via (sp as any).mode = mode, which works functionally and is passed correctly in DAP setBreakpoints requests — but the breakpoint mode is not visible in the VS Code Breakpoints panel.

For more information: microsoft/vscode#304764

Comment thread package.json
@asimgunes asimgunes marked this pull request as ready for review March 19, 2026 14:01
@asimgunes
Copy link
Copy Markdown
Author

Hi @jreineckearm , @jonahgraham ,

I completed my checks and I would like to hear your feedback on this update when you have suitable time.

Kind regards.

Asim

Copy link
Copy Markdown
Contributor

@jreineckearm jreineckearm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thanks for the PR. Good to see this initiative reaching completion. 🙂

See a couple of questions/nits: most are not blocking.

We need to see how the CDT-GDB: prefix in the context menu is perceived by users. I personally don't mind it for now. But I think it would be equally fine to not have it in the context menu but rather document it well once we add some better documentation about non-standard GUI features coming from the extension (similar needed for the radix stuff).

During testing I found a few things that might need attention:

  • 1.) set a breakpoint through the column/margin, 2.) use one of the two new commands => I always see the log output No breakpoint number <n>. Is there anything we can do to suppress this? It's not super-blocking and only happens when explicitly using one of the new commands. But made me look twice to ensure this isn't a result of something going wrong. I expect similar confusion at users' end.
  • 1.) set a breakpoint through the column/margin, 2.) use "set software breakpoint", 3.) call a > info b from the debug console => all breakpoints are gone in GDB (still visible in GUI). I am afraid this needs another look. This doesn't happen when you use "set hardware breakpoint" first.
  • We probably should disable the commands for the command palette (will add a comment to the package.json). If I understand correctly, you can't do anything without providing parameters. I even get an error pop up when using it.
Image

Comment thread src/BreakpointModesController.ts Outdated
Comment thread src/BreakpointModesController.ts
Comment thread src/BreakpointModesController.ts Outdated
Comment thread package.json Outdated
@jreineckearm
Copy link
Copy Markdown
Contributor

I am away now until Monday. Happy to review again then unless you got it completed until then with Jonah.

@cwalther
Copy link
Copy Markdown
Contributor

I am a bit confused – you seem to imply that there previously was no UI to choose between hardware and software breakpoints, but I do seem to see such UI: When I right-click on a breakpoint in the Breakpoints view, there is a context menu item Edit Mode that lets me select Hardware Breakpoint or Software Breakpoint. When I right-click on a breakpoint in the editor margin, I can choose Edit Breakpoint and get a Mode popup menu with the same choice. I have not tried if these work (our stub does not provide hardware breakpoints).

So, it seems like the advantage of your addition is only that, during a running debug session, it allows me to set a breakpoint with the desired mode from the start, instead of having to set it with the default mode first and then change the mode?

(Despite not needing hardware breakpoints, I am watching this with some interest because we have other breakpoint-like things that we will probably want to present as breakpoints of a separate type in the future.)

@asimgunes
Copy link
Copy Markdown
Author

I am a bit confused – you seem to imply that there previously was no UI to choose between hardware and software breakpoints, but I do seem to see such UI: When I right-click on a breakpoint in the Breakpoints view, there is a context menu item Edit Mode that lets me select Hardware Breakpoint or Software Breakpoint. When I right-click on a breakpoint in the editor margin, I can choose Edit Breakpoint and get a Mode popup menu with the same choice. I have not tried if these work (our stub does not provide hardware breakpoints).

Hi @cwalther , you're right, I might misinterpret this during the PR description. The VS Code does expose the mode selection through "Edit Breakpoint" and "Edit Mode" in the Breakpoints view. This PR adds the ability to set the desired mode directly from the line number context menu in a single step. This was a UX improvement we had already applied in practice and are now contributing upstream to the community.

@asimgunes asimgunes requested a review from jreineckearm March 25, 2026 14:29
@asimgunes
Copy link
Copy Markdown
Author

Hi @jreineckearm , @jonahgraham ,

I made some improvements depending on your feedback, is it possible to re-check the implemtation and the comments please?

Kind regards

Asim

@asimgunes asimgunes force-pushed the feat/breakpoint-types branch from 64c468b to 81b9b50 Compare March 25, 2026 17:39
Copy link
Copy Markdown
Contributor

@jreineckearm jreineckearm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, had forgotten about this one. The code changes look good, thanks for addressing the feedback.

I'll see if I can retry the two scenarios where I saw unexpected behavior (No breakpoint number <n>. messages, and losing breakpoints in GDB) tomorrow morning. I don't have a board at hand right now.

I think losing the breakpoints should at least be captured as a defect for later if still present. At the same time, I don't see it as a strong blocker. So, feel free to merge and prepare a release without me having tested the latest changes. Especially, since I am on Easter break from tomorrow night on. And I don't want to hold up releases you require for your product release.

@jreineckearm
Copy link
Copy Markdown
Contributor

These two problems haven't been addressed or commented

  • 1.) set a breakpoint through the column/margin, 2.) use one of the two new commands => I always see the log output No breakpoint number . Is there anything we can do to suppress this? It's not super-blocking and only happens when explicitly using one of the new commands. But made me look twice to ensure this isn't a result of something going wrong. I expect similar confusion at users' end.
  • 1.) set a breakpoint through the column/margin, 2.) use "set software breakpoint", 3.) call a > info b from the debug console => all breakpoints are gone in GDB (still visible in GUI). I am afraid this needs another look. This doesn't happen when you use "set hardware breakpoint" first.

The second IMO at least needs an issue in the backlog to follow up on. It leaves behind a zombie-breakpoint in the VS Code Breakpoint dialog.

As I wrote in my previous comment, I am away now and don't want to hold up the release. So, I am fine to go with this defect.

@asimgunes
Copy link
Copy Markdown
Author

Hi @jreineckearm ,
cc @jonahgraham ,

I investigate on your feedback about "No breakpoint number ..." and the second concern. I found an interesting behaviour which seems out of context for this update but we may need to check this in the breakpoint handling logic in cdt-gdb-adapter.

In this new implementation, we are checking the current breakpoints and if the breakpoint mode is changed between hardware and software breakpoint types we are deleting the current one and creating a new breakpoint through deleteBreakpoints and the addBreakpoints functions of the vscode.debug namespace.

In the logs below you can see both functions triggered setBreakpoints DAP message without waiting any feedback from the adapter. This seems like creating a race condition in the setBreakpointHandler inside the cdt-gdb-adapter where both of the calls started to perform the change. In some conditions this can also lead to zombie breakpoints in the VSCode as you observed during your checks.

I believe we need to include a mutex guard in the adapter layer to avoid such conditions since the protocol seems open for such invocations.

I like to hear your thoughs. If we agree, I can arrange the adapter update.

[14:25:18.507 UTC] From client: setBreakpoints({"source":{"name":"stepping.c","path":"c:\\test-c\\stepping.c"},"lines":[9],"breakpoints":[{"line":9}],"sourceModified":false})
[14:25:18.508 UTC] GDB command: 19 -break-list
[14:25:18.508 UTC] GDB write command: 19 -break-list
[14:25:18.510 UTC] From client: setBreakpoints({"source":{"name":"stepping.c","path":"c:\\test-c\\stepping.c"},"lines":[9,11],"breakpoints":[{"line":9},{"line":11,"mode":"software"}],"sourceModified":false})
[14:25:18.510 UTC] GDB command: 20 -break-list
[14:25:18.510 UTC] GDB write command: 20 -break-list
[14:25:18.522 UTC] GDB result: 19 done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[{width="7",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="18",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00000001004010a1",func="main",file="C:/test-c/stepping.c",fullname="C:\\test-c\\stepping.c",line="9",thread-groups=["i1"],times="0",original-location="-source c:\\test-c\\stepping.c -line 9"},bkpt={number="3",type="hw breakpoint",disp="keep",enabled="y",addr="0x00000001004010b9",func="main",file="C:/test-c/stepping.c",fullname="
[14:25:18.522 UTC] GDB -cont-: 19 test-c\\stepping.c",line="11",thread-groups=["i1"],times="0",original-location="-source c:\\test-c\\stepping.c -line 11"}]}
[14:25:18.522 UTC] GDB command: 19 -break-list completed with data
[14:25:18.522 UTC] GDB command: 21 -break-delete 3
[14:25:18.522 UTC] GDB write command: 21 -break-delete 3
[14:25:18.522 UTC] GDB result: 20 done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[{width="7",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="18",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00000001004010a1",func="main",file="C:/test-c/stepping.c",fullname="C:\\test-c\\stepping.c",line="9",thread-groups=["i1"],times="0",original-location="-source c:\\test-c\\stepping.c -line 9"},bkpt={number="3",type="hw breakpoint",disp="keep",enabled="y",addr="0x00000001004010b9",func="main",file="C:/test-c/stepping.c",fullname="
[14:25:18.522 UTC] GDB -cont-: 20 test-c\\stepping.c",line="11",thread-groups=["i1"],times="0",original-location="-source c:\\test-c\\stepping.c -line 11"}]}
[14:25:18.522 UTC] GDB command: 20 -break-list completed with data
[14:25:18.522 UTC] GDB command: 22 -break-delete 3
[14:25:18.522 UTC] GDB write command: 22 -break-delete 3
[14:25:18.535 UTC] GDB result: 21 done
[14:25:18.535 UTC] GDB command: 21 -break-delete 3 completed with data
[14:25:18.535 UTC] To client: {"seq":0,"type":"response","request_seq":13,"command":"setBreakpoints","success":true,"body":{"breakpoints":[{"id":1,"line":9,"verified":true}]}}
[14:25:18.535 UTC] To client: {"seq":0,"type":"event","event":"output","body":{"category":"stdout","output":"No breakpoint number 3.\n"}}
[14:25:18.535 UTC] GDB result: 22 done
[14:25:18.535 UTC] GDB command: 22 -break-delete 3 completed with data
[14:25:18.535 UTC] GDB command: 23 -break-insert --source "c:\\test-c\\stepping.c" --line 11
[14:25:18.535 UTC] GDB write command: 23 -break-insert --source "c:\\test-c\\stepping.c" --line 11
[14:25:18.554 UTC] GDB result: 23 done,bkpt={number="4",type="breakpoint",disp="keep",enabled="y",addr="0x00000001004010b9",func="main",file="C:/test-c/stepping.c",fullname="C:\\test-c\\stepping.c",line="11",thread-groups=["i1"],times="0",original-location="-source c:\\test-c\\stepping.c -line 11"}
[14:25:18.555 UTC] GDB command: 23 -break-insert --source "c:\\test-c\\stepping.c" --line 11 completed with data
[14:25:18.555 UTC] To client: {"seq":0,"type":"response","request_seq":14,"command":"setBreakpoints","success":true,"body":{"breakpoints":[{"id":1,"line":9,"verified":true},{"id":4,"line":11,"verified":true}]}}

@jreineckearm
Copy link
Copy Markdown
Contributor

@asimgunes , would breakpoint changed events help if updating an existing one?
https://microsoft.github.io/debug-adapter-protocol/specification#Events_Breakpoint

I believe, you can send an updated mode. But might need some experimenting first:
https://github.com/microsoft/vscode/blob/b0ee7a156ad4a2ded0a358b092d39a426dc95fbc/src/vs/workbench/contrib/debug/browser/debugSession.ts#L1280

That way, you might be able to avoid the race condition by add/remove operations through the API.
(Note that I am aware that programmatic breakpoint handling still has some inefficiencies. Hence, I cannot guarantee it works).

Otherwise, I agree we'd need some way of synchronization....

@cwalther
Copy link
Copy Markdown
Contributor

cwalther commented Apr 8, 2026

I don’t think that you can update a breakpoint mode using a DAP event (at least not officially). The DAP Breakpoint type that goes from adapter to client has fewer properties than the SourceBreakpoint type that goes from client to adapter. They are not subclasses of each other. That distinction had me confused for a while when I was recently examining a similar problem (trying to update a breakpoint condition from the adapter).

Regardless, I agree that the adapter should be able to deal with breakpoint requests that come in before previous ones are completed.

@jreineckearm
Copy link
Copy Markdown
Contributor

Another thought is whether vscode.debug.onDidChangeBreakpoints would help to guarantee that the remove operation is completed before the add operation (wait for a subsequent event). VS Code source code suggests it's always fired when the breakpoint list changed through the corresponding API calls.
Still a theoretical chance of a race condition if interrupted by a breakpoint change from the outside. But probably less of a real-world problem?

If that neither helps, then yes. We'd need to guard sequences of MI breakpoint commands by a mutex or install a proper breakpoint command queue. And apply consistently wherever we use MI breakpoint commands.
Sounds like a more fundamental change though deserving its own issue and PR as you stated earlier, @asimgunes .

I am still fine to merge this PR here as long as we capture your findings in a GH issue to address separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants