[Lldb-commits] [lldb] [vscode-lldb] Support lldb-dap environment in debug configuration (PR #153536)
Roy Shi via lldb-commits
lldb-commits at lists.llvm.org
Tue Aug 26 13:56:35 PDT 2025
https://github.com/royitaqi updated https://github.com/llvm/llvm-project/pull/153536
>From a8b0d3ebb8e3b5519ecda14699346123781abc2b Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Wed, 13 Aug 2025 22:47:58 -0700
Subject: [PATCH 1/4] [vscode-lldb] Support lldb-dap environment in debug
configuration
---
lldb/tools/lldb-dap/package.json | 9 ++++
.../lldb-dap/src-ts/debug-adapter-factory.ts | 42 ++++++++++++++++++-
lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts | 26 ++++++++++--
3 files changed, 71 insertions(+), 6 deletions(-)
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index d677a81cc7974..3e6928cf4327f 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -370,6 +370,15 @@
},
"markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings."
},
+ "debugAdapterEnv": {
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `{ \"FOO\": \"1\" }`"
+ },
"program": {
"type": "string",
"description": "Path to the program to debug."
diff --git a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
index 157aa2ac76a1f..e7bdd835894c0 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
@@ -157,6 +157,42 @@ async function getDAPArguments(
.get<string[]>("arguments", []);
}
+/**
+ * Retrieves the environment that will be provided to lldb-dap either from settings or the provided
+ * {@link vscode.DebugConfiguration}.
+ *
+ * @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within
+ * @param configuration The {@link vscode.DebugConfiguration} that will be launched
+ * @throws An {@link ErrorWithNotification} if something went wrong
+ * @returns The environment that will be provided to lldb-dap
+ */
+async function getDAPEnvironment(
+ workspaceFolder: vscode.WorkspaceFolder | undefined,
+ configuration: vscode.DebugConfiguration,
+): Promise<string[]> {
+ const debugConfigEnv = configuration.debugAdapterEnv;
+ if (debugConfigEnv) {
+ if (
+ Array.isArray(debugConfigEnv) ||
+ typeof debugConfigEnv !== "object" ||
+ Object.values(debugConfigEnv).findIndex(
+ (entry) => typeof entry !== "string",
+ ) !== -1
+ ) {
+ throw new ErrorWithNotification(
+ "The debugAdapterEnv property must be a dictionary with string values. Please update your launch configuration",
+ new ConfigureButton(),
+ );
+ }
+ return debugConfigEnv;
+ }
+
+ const config = vscode.workspace.workspaceFile
+ ? vscode.workspace.getConfiguration("lldb-dap")
+ : vscode.workspace.getConfiguration("lldb-dap", workspaceFolder);
+ return config.get<{ [key: string]: string }>("environment") || {};
+}
+
/**
* Creates a new {@link vscode.DebugAdapterExecutable} based on the provided workspace folder and
* debug configuration. Assumes that the given debug configuration is for a local launch of lldb-dap.
@@ -186,8 +222,10 @@ export async function createDebugAdapterExecutable(
) {
env["LLDBDAP_LOG"] = logFilePath.get(LogType.DEBUG_SESSION);
}
- const configEnvironment =
- config.get<{ [key: string]: string }>("environment") || {};
+ const configEnvironment = await getDAPEnvironment(
+ workspaceFolder,
+ configuration,
+ );
const dapPath = await getDAPExecutable(workspaceFolder, configuration);
const dbgOptions = {
diff --git a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
index 5f9d8efdcb3a3..7d37c695c5a0a 100644
--- a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
+++ b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
@@ -11,6 +11,7 @@ import * as vscode from "vscode";
export class LLDBDapServer implements vscode.Disposable {
private serverProcess?: child_process.ChildProcessWithoutNullStreams;
private serverInfo?: Promise<{ host: string; port: number }>;
+ private serverSpawnInfo?: string[];
constructor() {
vscode.commands.registerCommand(
@@ -34,7 +35,7 @@ export class LLDBDapServer implements vscode.Disposable {
options?: child_process.SpawnOptionsWithoutStdio,
): Promise<{ host: string; port: number } | undefined> {
const dapArgs = [...args, "--connection", "listen://localhost:0"];
- if (!(await this.shouldContinueStartup(dapPath, dapArgs))) {
+ if (!(await this.shouldContinueStartup(dapPath, dapArgs, options?.env))) {
return undefined;
}
@@ -70,6 +71,7 @@ export class LLDBDapServer implements vscode.Disposable {
}
});
this.serverProcess = process;
+ this.serverSpawnInfo = this.getSpawnInfo(dapPath, dapArgs, options?.env);
});
return this.serverInfo;
}
@@ -85,12 +87,14 @@ export class LLDBDapServer implements vscode.Disposable {
private async shouldContinueStartup(
dapPath: string,
args: string[],
+ env: NodeJS.ProcessEnv | { [key: string]: string } | undefined,
): Promise<boolean> {
if (!this.serverProcess || !this.serverInfo) {
return true;
}
- if (isDeepStrictEqual(this.serverProcess.spawnargs, [dapPath, ...args])) {
+ const newSpawnInfo = this.getSpawnInfo(dapPath, args, env);
+ if (isDeepStrictEqual(this.serverSpawnInfo, newSpawnInfo)) {
return true;
}
@@ -102,11 +106,11 @@ export class LLDBDapServer implements vscode.Disposable {
The previous lldb-dap server was started with:
-${this.serverProcess.spawnargs.join(" ")}
+${this.serverSpawnInfo.join(" ")}
The new lldb-dap server will be started with:
-${dapPath} ${args.join(" ")}
+${newSpawnInfo.join(" ")}
Restarting the server will interrupt any existing debug sessions and start a new server.`,
},
@@ -143,4 +147,18 @@ Restarting the server will interrupt any existing debug sessions and start a new
this.serverInfo = undefined;
}
}
+
+ getSpawnInfo(
+ path: string,
+ args: string[],
+ env: NodeJS.ProcessEnv | { [key: string]: string } | undefined,
+ ): string[] {
+ return [
+ path,
+ ...args,
+ ...Object.entries(env ?? {}).map(
+ (entry) => String(entry[0]) + "=" + String(entry[1]),
+ ),
+ ];
+ }
}
>From f4678f26a7a756682edad7cd85100f59c1823f0a Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 14 Aug 2025 07:52:09 -0700
Subject: [PATCH 2/4] Support array format
---
lldb/tools/lldb-dap/package.json | 26 ++++++++++----
.../lldb-dap/src-ts/debug-adapter-factory.ts | 35 ++++++++++++++-----
2 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 3e6928cf4327f..63ea1620d16f5 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -371,13 +371,27 @@
"markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings."
},
"debugAdapterEnv": {
- "type": "object",
- "patternProperties": {
- ".*": {
- "type": "string"
+ "anyOf": [
+ {
+ "type": "object",
+ "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `{ \"FOO\": \"1\" }`",
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "default": {}
+ },
+ {
+ "type": "array",
+ "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `[\"FOO=1\", \"BAR\"]`",
+ "items": {
+ "type": "string",
+ "pattern": "^((\\w+=.*)|^\\w+)$"
+ },
+ "default": []
}
- },
- "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `{ \"FOO\": \"1\" }`"
+ ]
},
"program": {
"type": "string",
diff --git a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
index e7bdd835894c0..107771f7e5ccc 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
@@ -169,22 +169,41 @@ async function getDAPArguments(
async function getDAPEnvironment(
workspaceFolder: vscode.WorkspaceFolder | undefined,
configuration: vscode.DebugConfiguration,
-): Promise<string[]> {
+): Promise<{ [key: string]: string }> {
const debugConfigEnv = configuration.debugAdapterEnv;
if (debugConfigEnv) {
if (
- Array.isArray(debugConfigEnv) ||
- typeof debugConfigEnv !== "object" ||
- Object.values(debugConfigEnv).findIndex(
- (entry) => typeof entry !== "string",
- ) !== -1
+ (typeof debugConfigEnv !== "object" ||
+
+ Object.values(debugConfigEnv).findIndex(
+ (entry) => typeof entry !== "string",
+ ) !== -1) &&
+ (!Array.isArray(debugConfigEnv) ||
+ debugConfigEnv.findIndex(
+ (entry) =>
+ typeof entry !== "string" || !/^((\\w+=.*)|^\\w+)$/.test(entry),
+ ) !== -1)
) {
throw new ErrorWithNotification(
- "The debugAdapterEnv property must be a dictionary with string values. Please update your launch configuration",
+ "The debugAdapterEnv property must be a dictionary of string keys and values OR an array of string values. Please update your launch configuration",
new ConfigureButton(),
);
}
- return debugConfigEnv;
+ // Transform, so that the returned value is always a dictionary.
+ if (Array.isArray(debugConfigEnv)) {
+ const ret: { [key: string]: string } = {};
+ for (const envVar of debugConfigEnv as string[]) {
+ const equalSignPos = envVar.search("=");
+ if (equalSignPos >= 0) {
+ ret[envVar.substr(0, equalSignPos)] = envVar.substr(equalSignPos + 1);
+ } else {
+ ret[envVar] = "";
+ }
+ }
+ return ret;
+ } else {
+ return debugConfigEnv;
+ }
}
const config = vscode.workspace.workspaceFile
>From 840c9fcacf720b6e6533872d364a15761e57331a Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Sat, 16 Aug 2025 03:41:42 -0700
Subject: [PATCH 3/4] Fix build break
---
lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
index 7d37c695c5a0a..300b12d1cce1b 100644
--- a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
+++ b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
@@ -89,7 +89,7 @@ export class LLDBDapServer implements vscode.Disposable {
args: string[],
env: NodeJS.ProcessEnv | { [key: string]: string } | undefined,
): Promise<boolean> {
- if (!this.serverProcess || !this.serverInfo) {
+ if (!this.serverProcess || !this.serverInfo || !this.serverSpawnInfo) {
return true;
}
>From 0f91d37c87306d5b71a80ed8dc1a39110ae70a75 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Tue, 26 Aug 2025 13:55:11 -0700
Subject: [PATCH 4/4] Move DAP env validation logic into a separate util
function: `validateDAPEnv()`
---
.../lldb-dap/src-ts/debug-adapter-factory.ts | 54 ++++++++++++++-----
1 file changed, 41 insertions(+), 13 deletions(-)
diff --git a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
index 107771f7e5ccc..5f0a4653d43b8 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
@@ -68,6 +68,42 @@ async function findDAPExecutable(): Promise<string | undefined> {
return undefined;
}
+/**
+ * Validates the DAP environment provided in the debug configuration.
+ * It must be a dictionary of string keys and values OR an array of string values.
+ *
+ * @param debugConfigEnv The supposed DAP environment that will be validated
+ * @returns Whether or not the DAP environment is valid
+ */
+function validateDAPEnv(debugConfigEnv: any): boolean {
+ // If the env is an object, it should have string keys and values.
+ if (
+ typeof debugConfigEnv === "object" &&
+ Object.keys(debugConfigEnv).findIndex(
+ (entry) => typeof entry !== "string",
+ ) !== -1 &&
+ Object.values(debugConfigEnv).findIndex(
+ (entry) => typeof entry !== "string",
+ ) !== -1
+ ) {
+ return false;
+ }
+
+ // If the env is an array, it should have string values which match the regex.
+ if (
+ Array.isArray(debugConfigEnv) &&
+ debugConfigEnv.findIndex(
+ (entry) =>
+ typeof entry !== "string" || !/^((\\w+=.*)|^\\w+)$/.test(entry),
+ ) !== -1
+ ) {
+ return false;
+ }
+
+ // The env is valid.
+ return true;
+}
+
/**
* Retrieves the lldb-dap executable path either from settings or the provided
* {@link vscode.DebugConfiguration}.
@@ -172,23 +208,13 @@ async function getDAPEnvironment(
): Promise<{ [key: string]: string }> {
const debugConfigEnv = configuration.debugAdapterEnv;
if (debugConfigEnv) {
- if (
- (typeof debugConfigEnv !== "object" ||
-
- Object.values(debugConfigEnv).findIndex(
- (entry) => typeof entry !== "string",
- ) !== -1) &&
- (!Array.isArray(debugConfigEnv) ||
- debugConfigEnv.findIndex(
- (entry) =>
- typeof entry !== "string" || !/^((\\w+=.*)|^\\w+)$/.test(entry),
- ) !== -1)
- ) {
+ if (validateDAPEnv(debugConfigEnv) === false) {
throw new ErrorWithNotification(
"The debugAdapterEnv property must be a dictionary of string keys and values OR an array of string values. Please update your launch configuration",
new ConfigureButton(),
);
}
+
// Transform, so that the returned value is always a dictionary.
if (Array.isArray(debugConfigEnv)) {
const ret: { [key: string]: string } = {};
@@ -237,7 +263,9 @@ export async function createDebugAdapterExecutable(
if (log_path) {
env["LLDBDAP_LOG"] = log_path;
} else if (
- vscode.workspace.getConfiguration("lldb-dap").get("captureSessionLogs", false)
+ vscode.workspace
+ .getConfiguration("lldb-dap")
+ .get("captureSessionLogs", false)
) {
env["LLDBDAP_LOG"] = logFilePath.get(LogType.DEBUG_SESSION);
}
More information about the lldb-commits
mailing list