Author: award999
Date: 2025-08-05T08:14:55-07:00
New Revision: ae7be39601496aa8f712672844de82285a227646

URL: 
https://github.com/llvm/llvm-project/commit/ae7be39601496aa8f712672844de82285a227646
DIFF: 
https://github.com/llvm/llvm-project/commit/ae7be39601496aa8f712672844de82285a227646.diff

LOG: Logging setup for lldb-dap extension (#146884)

- ~Add `winston` dependency (MIT license) to handle logging setup~
- Have an `LogOutputChannel` to log user facing information, errors,
warnings
- Write a debug session logs under the provided `logUri` to capture
further diagnostics when the `lldb-dap.captureSessionLogs` setting is
enabled. *Note* the `lldb-dap.log-path` setting takes precedence when
set
Issue: #146880

---------

Co-authored-by: Jonas Devlieghere <jo...@devlieghere.com>

Added: 
    lldb/tools/lldb-dap/src-ts/logging.ts

Modified: 
    lldb/tools/lldb-dap/package-lock.json
    lldb/tools/lldb-dap/package.json
    lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
    lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
    lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
    lldb/tools/lldb-dap/src-ts/extension.ts

Removed: 
    


################################################################################
diff  --git a/lldb/tools/lldb-dap/package-lock.json 
b/lldb/tools/lldb-dap/package-lock.json
index af90a9573aee6..1969b196accc6 100644
--- a/lldb/tools/lldb-dap/package-lock.json
+++ b/lldb/tools/lldb-dap/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "lldb-dap",
-  "version": "0.2.14",
+  "version": "0.2.15",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "lldb-dap",
-      "version": "0.2.14",
+      "version": "0.2.15",
       "license": "Apache 2.0 License with LLVM exceptions",
       "devDependencies": {
         "@types/node": "^18.19.41",

diff  --git a/lldb/tools/lldb-dap/package.json 
b/lldb/tools/lldb-dap/package.json
index 801abe73edd7d..fa2d4daffaf27 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -81,10 +81,16 @@
             "description": "The path to the lldb-dap binary, e.g. 
/usr/local/bin/lldb-dap"
           },
           "lldb-dap.log-path": {
+            "scope": "machine-overridable",
+            "type": "string",
+            "description": "The log path for lldb-dap (if any)",
+            "markdownDeprecationMessage": "Use the `#lldb-dap.logFolder#` 
setting instead"
+          },
+          "lldb-dap.logFolder": {
             "order": 0,
             "scope": "machine-overridable",
             "type": "string",
-            "description": "The log path for lldb-dap (if any)"
+            "markdownDescription": "The folder to persist lldb-dap logs. If no 
value is provided, logs will be persisted in the [Extension Logs 
Folder](command:workbench.action.openExtensionLogsFolder)."
           },
           "lldb-dap.serverMode": {
             "order": 0,
@@ -110,6 +116,11 @@
             "additionalProperties": {
               "type": "string"
             }
+          },
+          "lldb-dap.captureSessionLogs": {
+            "type": "boolean",
+            "description": "When enabled, LLDB-DAP session logs will be 
written to the Extension's log folder if the `lldb-dap.log-path` setting is not 
explicitly set.",
+            "default": false
           }
         }
       },

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 015bcf77ddd29..6e94400b09155 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
@@ -5,6 +5,7 @@ import * as child_process from "child_process";
 import * as fs from "node:fs/promises";
 import { ConfigureButton, OpenSettingsButton } from "./ui/show-error-message";
 import { ErrorWithNotification } from "./ui/error-with-notification";
+import { LogFilePathProvider, LogType } from "./logging";
 
 const exec = util.promisify(child_process.execFile);
 
@@ -160,12 +161,16 @@ async function getDAPArguments(
  * 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.
  *
+ * @param logger The {@link vscode.LogOutputChannel} to log setup diagnostics
+ * @param logFilePath The {@link LogFilePathProvider} for determining where to 
put session logs
  * @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 {@link vscode.DebugAdapterExecutable} that can be used to 
launch lldb-dap
  */
 export async function createDebugAdapterExecutable(
+  logger: vscode.LogOutputChannel,
+  logFilePath: LogFilePathProvider,
   workspaceFolder: vscode.WorkspaceFolder | undefined,
   configuration: vscode.DebugConfiguration,
 ): Promise<vscode.DebugAdapterExecutable> {
@@ -176,6 +181,10 @@ export async function createDebugAdapterExecutable(
   let env: { [key: string]: string } = {};
   if (log_path) {
     env["LLDBDAP_LOG"] = log_path;
+  } else if (
+    vscode.workspace.getConfiguration("lldb-dap").get("captureSessionLogs", 
false)
+  ) {
+    env["LLDBDAP_LOG"] = logFilePath.get(LogType.DEBUG_SESSION);
   }
   const configEnvironment =
     config.get<{ [key: string]: string }>("environment") || {};
@@ -190,6 +199,11 @@ export async function createDebugAdapterExecutable(
   };
   const dbgArgs = await getDAPArguments(workspaceFolder, configuration);
 
+  logger.info(`lldb-dap path: ${dapPath}`);
+  logger.info(`lldb-dap args: ${dbgArgs}`);
+  logger.info(`cwd: ${dbgOptions.cwd}`);
+  logger.info(`env: ${JSON.stringify(dbgOptions.env)}`);
+
   return new vscode.DebugAdapterExecutable(dapPath, dbgArgs, dbgOptions);
 }
 
@@ -200,18 +214,33 @@ export async function createDebugAdapterExecutable(
 export class LLDBDapDescriptorFactory
   implements vscode.DebugAdapterDescriptorFactory
 {
+  constructor(
+    private readonly logger: vscode.LogOutputChannel,
+    private logFilePath: LogFilePathProvider,
+  ) {}
+
   async createDebugAdapterDescriptor(
     session: vscode.DebugSession,
     executable: vscode.DebugAdapterExecutable | undefined,
   ): Promise<vscode.DebugAdapterDescriptor | undefined> {
+    this.logger.info(`Creating debug adapter for session "${session.name}"`);
+    this.logger.info(
+      `Session "${session.name}" debug configuration:\n` +
+        JSON.stringify(session.configuration, undefined, 2),
+    );
     if (executable) {
-      throw new Error(
+      const error = new Error(
         "Setting the debug adapter executable in the package.json is not 
supported.",
       );
+      this.logger.error(error);
+      throw error;
     }
 
     // Use a server connection if the debugAdapterPort is provided
     if (session.configuration.debugAdapterPort) {
+      this.logger.info(
+        `Spawning debug adapter server on port 
${session.configuration.debugAdapterPort}`,
+      );
       return new vscode.DebugAdapterServer(
         session.configuration.debugAdapterPort,
         session.configuration.debugAdapterHostname,
@@ -219,6 +248,8 @@ export class LLDBDapDescriptorFactory
     }
 
     return createDebugAdapterExecutable(
+      this.logger,
+      this.logFilePath,
       session.workspaceFolder,
       session.configuration,
     );

diff  --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts 
b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
index 316ffaf47c3d2..8c04ec2bdc9d3 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
@@ -5,6 +5,7 @@ import { LLDBDapServer } from "./lldb-dap-server";
 import { createDebugAdapterExecutable } from "./debug-adapter-factory";
 import { ConfigureButton, showErrorMessage } from "./ui/show-error-message";
 import { ErrorWithNotification } from "./ui/error-with-notification";
+import { LogFilePathProvider } from "./logging";
 
 const exec = util.promisify(child_process.execFile);
 
@@ -71,13 +72,24 @@ const configurations: Record<string, DefaultConfig> = {
 export class LLDBDapConfigurationProvider
   implements vscode.DebugConfigurationProvider
 {
-  constructor(private readonly server: LLDBDapServer) {}
+  constructor(
+    private readonly server: LLDBDapServer,
+    private readonly logger: vscode.LogOutputChannel,
+    private readonly logFilePath: LogFilePathProvider,
+  ) {}
 
   async resolveDebugConfiguration(
     folder: vscode.WorkspaceFolder | undefined,
     debugConfiguration: vscode.DebugConfiguration,
     token?: vscode.CancellationToken,
   ): Promise<vscode.DebugConfiguration> {
+    this.logger.info(
+      `Resolving debug configuration for "${debugConfiguration.name}"`,
+    );
+    this.logger.debug(
+      "Initial debug configuration:\n" +
+        JSON.stringify(debugConfiguration, undefined, 2),
+    );
     let config = vscode.workspace.getConfiguration("lldb-dap");
     for (const [key, cfg] of Object.entries(configurations)) {
       if (Reflect.has(debugConfiguration, key)) {
@@ -152,6 +164,8 @@ export class LLDBDapConfigurationProvider
         // Always try to create the debug adapter executable as this will show 
the user errors
         // if there are any.
         const executable = await createDebugAdapterExecutable(
+          this.logger,
+          this.logFilePath,
           folder,
           debugConfiguration,
         );
@@ -184,8 +198,14 @@ export class LLDBDapConfigurationProvider
         }
       }
 
+      this.logger.info(
+        "Resolved debug configuration:\n" +
+          JSON.stringify(debugConfiguration, undefined, 2),
+      );
+
       return debugConfiguration;
     } catch (error) {
+      this.logger.error(error as Error);
       // Show a better error message to the user if possible
       if (!(error instanceof ErrorWithNotification)) {
         throw error;

diff  --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts 
b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
index 50db1e1c3a7b0..7d7f73dbff92d 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
@@ -5,6 +5,7 @@ import * as vscode from "vscode";
 // prettier-ignore
 interface EventMap {
   "module": DebugProtocol.ModuleEvent;
+  "exited": DebugProtocol.ExitedEvent;
 }
 
 /** A type assertion to check if a ProtocolMessage is an event or if it is a 
specific event. */
@@ -47,7 +48,7 @@ export class DebugSessionTracker
   onDidChangeModules: vscode.Event<vscode.DebugSession | undefined> =
     this.modulesChanged.event;
 
-  constructor() {
+  constructor(private logger: vscode.LogOutputChannel) {
     this.onDidChangeModules(this.moduleChangedListener, this);
     vscode.debug.onDidChangeActiveDebugSession((session) =>
       this.modulesChanged.fire(session),
@@ -62,8 +63,12 @@ export class DebugSessionTracker
   createDebugAdapterTracker(
     session: vscode.DebugSession,
   ): vscode.ProviderResult<vscode.DebugAdapterTracker> {
+    this.logger.info(`Starting debug session "${session.name}"`);
+    let stopping = false;
     return {
+      onError: (error) => !stopping && this.logger.error(error), // Can throw 
benign read errors when shutting down.
       onDidSendMessage: (message) => this.onDidSendMessage(session, message),
+      onWillStopSession: () => (stopping = true),
       onExit: () => this.onExit(session),
     };
   }
@@ -134,6 +139,13 @@ export class DebugSessionTracker
       }
       this.modules.set(session, modules);
       this.modulesChanged.fire(session);
+    } else if (isEvent(message, "exited")) {
+      // The vscode.DebugAdapterTracker#onExit event is sometimes called with
+      // exitCode = undefined but the exit event from LLDB-DAP always has the 
"exitCode"
+      const { exitCode } = message.body;
+      this.logger.info(
+        `Session "${session.name}" exited with code ${exitCode}`,
+      );
     }
   }
 }

diff  --git a/lldb/tools/lldb-dap/src-ts/extension.ts 
b/lldb/tools/lldb-dap/src-ts/extension.ts
index c8e5146e29cea..4b7a35e6944c6 100644
--- a/lldb/tools/lldb-dap/src-ts/extension.ts
+++ b/lldb/tools/lldb-dap/src-ts/extension.ts
@@ -1,3 +1,4 @@
+import * as path from "path";
 import * as vscode from "vscode";
 
 import { LLDBDapDescriptorFactory } from "./debug-adapter-factory";
@@ -10,28 +11,35 @@ import {
   ModulesDataProvider,
   ModuleProperty,
 } from "./ui/modules-data-provider";
+import { LogFilePathProvider } from "./logging";
 
 /**
  * This class represents the extension and manages its life cycle. Other 
extensions
  * using it as as library should use this class as the main entry point.
  */
 export class LLDBDapExtension extends DisposableContext {
-  constructor() {
+  constructor(
+    logger: vscode.LogOutputChannel,
+    logFilePath: LogFilePathProvider,
+    outputChannel: vscode.OutputChannel,
+  ) {
     super();
 
     const lldbDapServer = new LLDBDapServer();
-    const sessionTracker = new DebugSessionTracker();
+    const sessionTracker = new DebugSessionTracker(logger);
 
     this.pushSubscription(
+      logger,
+      outputChannel,
       lldbDapServer,
       sessionTracker,
       vscode.debug.registerDebugConfigurationProvider(
         "lldb-dap",
-        new LLDBDapConfigurationProvider(lldbDapServer),
+        new LLDBDapConfigurationProvider(lldbDapServer, logger, logFilePath),
       ),
       vscode.debug.registerDebugAdapterDescriptorFactory(
         "lldb-dap",
-        new LLDBDapDescriptorFactory(),
+        new LLDBDapDescriptorFactory(logger, logFilePath),
       ),
       vscode.debug.registerDebugAdapterTrackerFactory(
         "lldb-dap",
@@ -54,6 +62,12 @@ export class LLDBDapExtension extends DisposableContext {
 /**
  * This is the entry point when initialized by VS Code.
  */
-export function activate(context: vscode.ExtensionContext) {
-  context.subscriptions.push(new LLDBDapExtension());
+export async function activate(context: vscode.ExtensionContext) {
+  const outputChannel = vscode.window.createOutputChannel("LLDB-DAP", { log: 
true });
+  outputChannel.info("LLDB-DAP extension activating...");
+  const logFilePath = new LogFilePathProvider(context, outputChannel);
+  context.subscriptions.push(
+    new LLDBDapExtension(outputChannel, logFilePath, outputChannel),
+  );
+  outputChannel.info("LLDB-DAP extension activated");
 }

diff  --git a/lldb/tools/lldb-dap/src-ts/logging.ts 
b/lldb/tools/lldb-dap/src-ts/logging.ts
new file mode 100644
index 0000000000000..3b1c3c37ce1ce
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/logging.ts
@@ -0,0 +1,67 @@
+import * as path from "path";
+import * as vscode from "vscode";
+
+/**
+ * Formats the given date as a string in the form "YYYYMMddTHHMMSS".
+ *
+ * @param date The date to format as a string.
+ * @returns The formatted date.
+ */
+function formatDate(date: Date): string {
+  const year = date.getFullYear().toString().padStart(4, "0");
+  const month = (date.getMonth() + 1).toString().padStart(2, "0");
+  const day = date.getDate().toString().padStart(2, "0");
+  const hour = date.getHours().toString().padStart(2, "0");
+  const minute = date.getMinutes().toString().padStart(2, "0");
+  const seconds = date.getSeconds().toString().padStart(2, "0");
+  return `${year}${month}${day}T${hour}${minute}${seconds}`;
+}
+
+export enum LogType {
+  DEBUG_SESSION,
+}
+
+export class LogFilePathProvider {
+  private logFolder: string = "";
+
+  constructor(
+    private context: vscode.ExtensionContext,
+    private logger: vscode.LogOutputChannel,
+  ) {
+    this.updateLogFolder();
+    context.subscriptions.push(
+        vscode.workspace.onDidChangeConfiguration(e => {
+            if (
+                e.affectsConfiguration("lldb-dap.logFolder")
+            ) {
+                this.updateLogFolder();
+            }
+        })
+    );
+  }
+
+  get(type: LogType): string {
+    const logFolder = this.logFolder || this.context.logUri.fsPath;
+    switch(type) {
+    case LogType.DEBUG_SESSION:
+        return path.join(logFolder, `lldb-dap-session-${formatDate(new 
Date())}.log`);
+        break;
+    }
+  }
+
+  private updateLogFolder() {
+    const config = vscode.workspace.getConfiguration("lldb-dap");
+    let logFolder =
+      config.get<string>("logFolder") || this.context.logUri.fsPath;
+    vscode.workspace.fs
+      .createDirectory(vscode.Uri.file(logFolder))
+      .then(undefined, (error) => {
+        this.logger.error(`Failed to create log folder ${logFolder}: 
${error}`);
+        logFolder = this.context.logUri.fsPath;
+      })
+      .then(() => {
+        this.logFolder = logFolder;
+        this.logger.info(`Persisting lldb-dap logs to ${logFolder}`);
+      });
+  }
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to