Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### Fixed

- Fix breakpoint filtering fallback in debugger logging ([#1989](https://github.com/swiftlang/vscode-swift/pull/1989))
Copy link
Member

Choose a reason for hiding this comment

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

chore: You'll have to rebase this since the CHANGELOG was recently updated for version 2.14.2. This may require moving this bullet to a new ### Fixed section.

Copy link
Author

Choose a reason for hiding this comment

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

Will do — I’ll rebase and move the entry to the current unreleased ### Fixed section.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! The location looks good now.

Though I just noticed that the wording here would only be meaningful to contributors and probably won't make much sense to users of the extension. Maybe we could go with something like Fixed breakpoints showing up as unresolved when using lldb-dap?


### Fixed

- Fix extension failing to activate when Swiftly was installed via Homebrew ([#1975](https://github.com/swiftlang/vscode-swift/pull/1975))
- Fix running `swift package` commands with `swift.disableSwiftPMIntegration` enabled ([#1969](https://github.com/swiftlang/vscode-swift/pull/1969))
- Fixed an issue where `lldb-dap` could not be found in Command Line Tools toolchains ([#1936](https://github.com/swiftlang/vscode-swift/pull/1936))
Expand Down
125 changes: 125 additions & 0 deletions src/debugger/logTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import { DebugProtocol } from "@vscode/debugprotocol";
import * as vscode from "vscode";

import { SwiftLogger } from "../logging/SwiftLogger";
Expand Down Expand Up @@ -72,6 +73,7 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
private exitHandler?: (exitCode: number) => void;
private output: string[] = [];
private exitCode: number | undefined;
private logger?: SwiftLogger;

constructor(public id: string) {
LoggingDebugAdapterTracker.debugSessionIdMap[id] = this;
Expand All @@ -90,6 +92,7 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
cb(o);
}
if (loggingDebugAdapter.exitCode) {
loggingDebugAdapter.logger = logger;
exitHandler(loggingDebugAdapter.exitCode);
}
loggingDebugAdapter.output = [];
Expand All @@ -114,6 +117,8 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
return;
}

this.handleBreakpointEvent(debugMessage);

if (debugMessage.event === "exited" && debugMessage.body.exitCode) {
this.exitCode = debugMessage.body.exitCode;
this.exitHandler?.(debugMessage.body.exitCode);
Expand All @@ -131,6 +136,126 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
}
}

private handleBreakpointEvent(rawMsg: unknown): void {
// Narrow-bodied shapes for just the fields we use (local to this function).
type StoppedBodyLike = {
reason?: string;
thread?: {
frames?: {
source?: { path?: string };
line?: number;
}[];
};
};

type BreakpointBodyLike = {
source?: { path?: string };
line?: number;
};

try {
const msg = rawMsg as DebugProtocol.ProtocolMessage;
const eventMsg = msg as DebugProtocol.Event;

if (!msg || msg.type !== "event") {
return;
}

const normalizePath = (p: string): string => {
try {
// If adapter sends a URI-like path, parse it
if (p.startsWith("file://") || p.includes("://")) {
return vscode.Uri.parse(p).fsPath;
}
// Otherwise treat as filesystem path
return vscode.Uri.file(p).fsPath;
} catch {
// Fallback: return raw
return p;
}
};

// Case A: stopped event with reason = "breakpoint"
if (
eventMsg.event === "stopped" &&
(eventMsg.body as StoppedBodyLike)?.reason === "breakpoint"
) {
const frames = (eventMsg.body as StoppedBodyLike)?.thread?.frames;
if (!Array.isArray(frames) || frames.length === 0) {
return;
}

const top = frames[0];
const sourcePath = top?.source?.path;
const line = top?.line;
if (!sourcePath || typeof line !== "number") {
return;
}

const bpLine0 = line - 1; // VS Code uses 0-based lines

const breakpoints = vscode.debug.breakpoints.filter(
b => !!(b as vscode.SourceBreakpoint).location
) as vscode.SourceBreakpoint[];

for (const bp of breakpoints) {
const loc = bp.location;
if (!loc) {
continue;
}
if (loc.uri.fsPath !== normalizePath(sourcePath)) {
continue;
}
if (loc.range.start.line !== bpLine0) {
continue;
}

// Force a UI refresh so the breakpoint shows as installed.
vscode.debug.removeBreakpoints([bp]);
Copy link
Member

Choose a reason for hiding this comment

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

question: Will this also trigger when the breakpoint is already resolved? It's probably not a huge issue, but I think we should be only doing this when the breakpoint that's being hit isn't marked as resolved.

vscode.debug.addBreakpoints([bp]);
break;
}
return;
}

// Case B: explicit "breakpoint" event that carries source+line info
if (eventMsg.event === "breakpoint" && eventMsg.body) {
const body = eventMsg.body as BreakpointBodyLike;
const sourcePath = body.source?.path;
const line = body.line;
if (!sourcePath || typeof line !== "number") {
return;
}

const bpLine0 = line - 1;
const breakpoints = vscode.debug.breakpoints.filter(
b => b instanceof vscode.SourceBreakpoint
) as vscode.SourceBreakpoint[];

for (const bp of breakpoints) {
const loc = bp.location;
if (!loc) {
continue;
}
if (loc.uri.fsPath !== normalizePath(sourcePath)) {
continue;
}
if (loc.range.start.line !== bpLine0) {
continue;
}

vscode.debug.removeBreakpoints([bp]);
vscode.debug.addBreakpoints([bp]);
break;
}
return;
}
} catch (err) {
// eslint-disable-next-line no-console
this.logger?.error(`Breakpoint fallback error: ${String(err)}`);
}
}

/**
* The debug adapter session is about to be stopped. Delete the session from
* the tracker
Expand Down