Skip to content

Commit 401c3a3

Browse files
Optionally add lldb-dap logs to Diagnostic Bundle (#1758)
* Optionally add lldb-dap logs to Diagnostic Bundle * Gracefully handle folder not existing * Consider new `lldb-dap.logFolder` setting * Add some tests * Update src/commands/captureDiagnostics.ts Co-authored-by: Paul LeMarquand <[email protected]> * Only include LLDB dap logs if it's being used as the debug adapter * Add changelog --------- Co-authored-by: Paul LeMarquand <[email protected]>
1 parent e4dec68 commit 401c3a3

File tree

8 files changed

+221
-43
lines changed

8 files changed

+221
-43
lines changed

.vscode-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ if (process.platform === "darwin" && process.arch === "x64") {
4242
}
4343

4444
const installExtensions = [];
45+
const extensionDependencies = [];
4546
let vsixPath = process.env["VSCODE_SWIFT_VSIX"];
4647
let versionStr = version;
4748
let extensionDevelopmentPath;
@@ -66,6 +67,8 @@ if (vsixPath) {
6667

6768
extensionDevelopmentPath = `${__dirname}/.vscode-test/extensions/${publisher}.${name}-${versionStr}`;
6869
console.log("Running tests against extension development path " + extensionDevelopmentPath);
70+
} else {
71+
extensionDependencies.push("vadimcn.vscode-lldb", "llvm-vs-code-extensions.lldb-dap");
6972
}
7073

7174
const vscodeVersion = process.env["VSCODE_VERSION"] ?? "stable";
@@ -117,6 +120,7 @@ module.exports = defineConfig({
117120
},
118121
},
119122
},
123+
installExtensions: extensionDependencies,
120124
skipExtensionDependencies: installConfigs.length > 0,
121125
reuseMachineInstall: !isCIBuild,
122126
},
@@ -152,6 +156,7 @@ module.exports = defineConfig({
152156
},
153157
},
154158
},
159+
installExtensions: extensionDependencies,
155160
skipExtensionDependencies: installConfigs.length > 0,
156161
reuseMachineInstall: !isCIBuild,
157162
},

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- New `swift.createTasksForLibraryProducts` setting that when enabled causes the extension to automatically create and provide tasks for library products ([#1741](https://github.com/swiftlang/vscode-swift/pull/1741))
88
- New `swift.outputChannelLogLevel` setting to control the verbosity of the `Swift` output channel ([#1746](https://github.com/swiftlang/vscode-swift/pull/1746))
9+
- Optionally include LLDB DAP logs in the Swift diagnostics bundle ([#1768](https://github.com/swiftlang/vscode-swift/pull/1758))
910

1011
### Changed
1112
- Added log levels and improved Swift extension logging so a logfile is produced in addition to logging messages to the existing `Swift` output channel. Deprecated the `swift.diagnostics` setting in favour of the new `swift.outputChannelLogLevel` setting ([#1746](https://github.com/swiftlang/vscode-swift/pull/1746))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Very important logging data

src/commands/captureDiagnostics.ts

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { Version } from "../utilities/version";
2525
import { destructuredPromise, execFileStreamOutput } from "../utilities/utilities";
2626
import configuration from "../configuration";
2727
import { FolderContext } from "../FolderContext";
28+
import { Extension } from "../utilities/extensions";
29+
import { DebugAdapter } from "../debugger/debugAdapter";
2830

2931
export async function captureDiagnostics(
3032
ctx: WorkspaceContext,
@@ -44,13 +46,26 @@ export async function captureDiagnostics(
4446
);
4547

4648
await fsPromises.mkdir(diagnosticsDir);
47-
await copyLogFile(diagnosticsDir, extensionLogFile(ctx));
4849

4950
const singleFolderWorkspace = ctx.folders.length === 1;
5051
const zipDir = await createDiagnosticsZipDir();
5152
const zipFilePath = path.join(zipDir, `${path.basename(diagnosticsDir)}.zip`);
5253
const { archive, done: archivingDone } = configureZipArchiver(zipFilePath);
5354

55+
const archivedLldbDapLogFolders = new Set<string>();
56+
const includeLldbDapLogs = DebugAdapter.getLaunchConfigType(
57+
ctx.globalToolchainSwiftVersion
58+
);
59+
if (captureMode === "Full" && includeLldbDapLogs) {
60+
for (const defaultLldbDapLogs of [defaultLldbDapLogFolder(ctx), lldbDapLogFolder()]) {
61+
if (!defaultLldbDapLogs || archivedLldbDapLogFolders.has(defaultLldbDapLogs)) {
62+
continue;
63+
}
64+
archivedLldbDapLogFolders.add(defaultLldbDapLogs);
65+
await copyLogFolder(ctx, diagnosticsDir, defaultLldbDapLogs);
66+
}
67+
}
68+
5469
for (const folder of ctx.folders) {
5570
const baseName = path.basename(folder.folder.fsPath);
5671
const guid = Math.random().toString(36).substring(2, 10);
@@ -70,14 +85,31 @@ export async function captureDiagnostics(
7085
// The `sourcekit-lsp diagnose` command is only available in 6.0 and higher.
7186
if (folder.toolchain.swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0))) {
7287
await sourcekitDiagnose(folder, outputDir);
73-
} else {
88+
} else if (
89+
vscode.workspace
90+
.getConfiguration("sourcekit-lsp")
91+
.get<string>("trace.server", "off") !== "off"
92+
) {
7493
const logFile = sourceKitLogFile(folder);
7594
if (logFile) {
7695
await copyLogFile(outputDir, logFile);
7796
}
7897
}
98+
99+
const includeLldbDapLogs = DebugAdapter.getLaunchConfigType(folder.swiftVersion);
100+
if (!includeLldbDapLogs) {
101+
continue;
102+
}
103+
// Copy lldb-dap logs
104+
const lldbDapLogs = lldbDapLogFolder(folder.workspaceFolder);
105+
if (lldbDapLogs && !archivedLldbDapLogFolders.has(lldbDapLogs)) {
106+
archivedLldbDapLogFolders.add(lldbDapLogs);
107+
await copyLogFolder(ctx, outputDir, lldbDapLogs);
108+
}
79109
}
80110
}
111+
// Leave at end in case log above
112+
await copyLogFile(diagnosticsDir, extensionLogFile(ctx));
81113

82114
archive.directory(diagnosticsDir, false);
83115
void archive.finalize();
@@ -137,24 +169,24 @@ async function captureDiagnosticsMode(
137169
ctx: WorkspaceContext,
138170
allowMinimalCapture: boolean
139171
): Promise<"Minimal" | "Full" | undefined> {
140-
if (
141-
ctx.globalToolchainSwiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) ||
142-
vscode.workspace.getConfiguration("sourcekit-lsp").get<string>("trace.server", "off") !==
143-
"off"
144-
) {
145-
const fullButton = allowMinimalCapture ? "Capture Full Diagnostics" : "Capture Diagnostics";
172+
if (ctx.globalToolchainSwiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0))) {
173+
const fullButton = "Capture Full Diagnostics";
146174
const minimalButton = "Capture Minimal Diagnostics";
147175
const buttons = allowMinimalCapture ? [fullButton, minimalButton] : [fullButton];
148176
const fullCaptureResult = await vscode.window.showInformationMessage(
149177
`A Diagnostic Bundle collects information that helps the developers of the Swift for VS Code extension diagnose and fix issues.
150178
151-
This information contains:
179+
This information includes:
152180
- Extension logs
181+
- Extension settings
153182
- Versions of Swift installed on your system
183+
184+
If you allow capturing a Full Diagnostic Bundle, the information will also include:
154185
- Crash logs from SourceKit
155186
- Log messages emitted by SourceKit
156187
- If possible, a minimized project that caused SourceKit to crash
157188
- If possible, a minimized project that caused the Swift compiler to crash
189+
- If available, log messages emitted by LLDB DAP
158190
159191
All information is collected locally and you can inspect the diagnose bundle before sharing it with developers of the Swift for VS Code extension.
160192
@@ -211,6 +243,19 @@ async function copyLogFile(dir: string, filePath: string) {
211243
await fsPromises.copyFile(filePath, path.join(dir, path.basename(filePath)));
212244
}
213245

246+
async function copyLogFolder(ctx: WorkspaceContext, dir: string, folderPath: string) {
247+
try {
248+
const lldbLogFiles = await fsPromises.readdir(folderPath);
249+
for (const log of lldbLogFiles) {
250+
await copyLogFile(dir, path.join(folderPath, log));
251+
}
252+
} catch (error) {
253+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
254+
ctx.logger.error(`Failed to read log files from ${folderPath}: ${error}`);
255+
}
256+
}
257+
}
258+
214259
/**
215260
* Creates a directory for diagnostics zip files, located in the system's temporary directory.
216261
*/
@@ -224,6 +269,31 @@ function extensionLogFile(ctx: WorkspaceContext): string {
224269
return ctx.logger.logFilePath;
225270
}
226271

272+
function defaultLldbDapLogFolder(ctx: WorkspaceContext): string {
273+
const rootLogFolder = path.dirname(ctx.loggerFactory.logFolderUri.fsPath);
274+
return path.join(rootLogFolder, Extension.LLDBDAP);
275+
}
276+
277+
function lldbDapLogFolder(workspaceFolder?: vscode.WorkspaceFolder): string | undefined {
278+
const config = vscode.workspace.workspaceFile
279+
? vscode.workspace.getConfiguration("lldb-dap")
280+
: vscode.workspace.getConfiguration("lldb-dap", workspaceFolder);
281+
let logFolder = config.get<string>("logFolder");
282+
if (!logFolder) {
283+
return;
284+
} else if (!path.isAbsolute(logFolder)) {
285+
const logFolderSettingInfo = config.inspect<string>("logFolder");
286+
if (logFolderSettingInfo?.workspaceFolderValue && workspaceFolder) {
287+
logFolder = path.join(workspaceFolder.uri.fsPath, logFolder);
288+
} else if (logFolderSettingInfo?.workspaceValue && vscode.workspace.workspaceFile) {
289+
logFolder = path.join(path.dirname(vscode.workspace.workspaceFile.fsPath), logFolder);
290+
} else if (vscode.workspace.workspaceFolders?.length) {
291+
logFolder = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, logFolder);
292+
}
293+
}
294+
return logFolder;
295+
}
296+
227297
function settingsLogs(ctx: FolderContext): string {
228298
const settings = JSON.stringify(vscode.workspace.getConfiguration("swift"), null, 2);
229299
return `${ctx.toolchain.diagnostics}\nSettings:\n${settings}`;

src/logging/SwiftLoggerFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { SwiftOutputChannel } from "./SwiftOutputChannel";
1919
import { SwiftLogger } from "./SwiftLogger";
2020

2121
export class SwiftLoggerFactory {
22-
constructor(private logFolderUri: vscode.Uri) {}
22+
constructor(public readonly logFolderUri: vscode.Uri) {}
2323

2424
create(name: string, logFilename: string): SwiftLogger;
2525
create(name: string, logFilename: string, options: { outputChannel: true }): SwiftOutputChannel;

src/utilities/extensions.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
export enum Extension {
16+
CODELLDB = "vadimcn.vscode-lldb",
17+
LLDBDAP = "llvm-vs-code-extensions.lldb-dap",
18+
}

0 commit comments

Comments
 (0)