Author: John Harrison
Date: 2025-05-08T15:25:28-07:00
New Revision: 611d81bd9304768f3cdb101d37c81d36b9762723

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

LOG: [lldb-dap] Adding a modules explorer to lldb-dap ext. (#138977)

This creates a very basic module explorer for tracking and displaying
loaded modules, reported by lldb-dap for the active debug session.

This includes a basic session tracker that we can use to observe the
debug session and collect specific information for additional
visualizations in the lldb-dap ext.

Here is a screenshot of the current visualization in the tree view.
There is some unfortunate wrapping of the path, but it shows the basic
support that could be extended in the future.

<img width="1759" alt="Screenshot 2025-05-07 at 2 52 50 PM"
src="https://github.com/user-attachments/assets/588baa2f-61d5-4434-8692-b1d0cce42875";
/>

Added: 
    lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
    lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts

Modified: 
    lldb/tools/lldb-dap/package-lock.json
    lldb/tools/lldb-dap/package.json
    lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
    lldb/tools/lldb-dap/src-ts/disposable-context.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 ab5c7dc33a8e5..0a2b9e764067e 100644
--- a/lldb/tools/lldb-dap/package-lock.json
+++ b/lldb/tools/lldb-dap/package-lock.json
@@ -1,16 +1,17 @@
 {
   "name": "lldb-dap",
-  "version": "0.2.10",
+  "version": "0.2.13",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "lldb-dap",
-      "version": "0.2.10",
+      "version": "0.2.13",
       "license": "Apache 2.0 License with LLVM exceptions",
       "devDependencies": {
         "@types/node": "^18.19.41",
         "@types/vscode": "1.75.0",
+        "@vscode/debugprotocol": "^1.68.0",
         "@vscode/vsce": "^3.2.2",
         "prettier": "^3.4.2",
         "prettier-plugin-curly": "^0.3.1",
@@ -405,6 +406,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@vscode/debugprotocol": {
+      "version": "1.68.0",
+      "resolved": 
"https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz";,
+      "integrity": 
"sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@vscode/vsce": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz";,

diff  --git a/lldb/tools/lldb-dap/package.json 
b/lldb/tools/lldb-dap/package.json
index a7631464d236a..e3e46526f379f 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -30,9 +30,10 @@
   "devDependencies": {
     "@types/node": "^18.19.41",
     "@types/vscode": "1.75.0",
+    "@vscode/debugprotocol": "^1.68.0",
     "@vscode/vsce": "^3.2.2",
-    "prettier-plugin-curly": "^0.3.1",
     "prettier": "^3.4.2",
+    "prettier-plugin-curly": "^0.3.1",
     "typescript": "^5.7.3"
   },
   "activationEvents": [
@@ -760,6 +761,16 @@
           }
         ]
       }
-    ]
+    ],
+    "views": {
+      "debug": [
+        {
+          "id": "lldb-dap.modules",
+          "name": "Modules",
+          "when": "inDebugMode && debugType == 'lldb-dap'",
+          "icon": "$(symbol-module)"
+        }
+      ]
+    }
   }
 }

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 c91b101f4a9ba..957bc5e1eb956 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
@@ -78,7 +78,7 @@ export class LLDBDapConfigurationProvider
     debugConfiguration: vscode.DebugConfiguration,
     token?: vscode.CancellationToken,
   ): Promise<vscode.DebugConfiguration> {
-    let config = vscode.workspace.getConfiguration("lldb-dap.defaults");
+    let config = vscode.workspace.getConfiguration("lldb-dap");
     for (const [key, cfg] of Object.entries(configurations)) {
       if (Reflect.has(debugConfiguration, key)) {
         continue;

diff  --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts 
b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
new file mode 100644
index 0000000000000..1ce190938d9c7
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
@@ -0,0 +1,109 @@
+import { DebugProtocol } from "@vscode/debugprotocol";
+import * as vscode from "vscode";
+
+/** A helper type for mapping event types to their corresponding data type. */
+// prettier-ignore
+interface EventMap {
+  "module": DebugProtocol.ModuleEvent;
+}
+
+/** A type assertion to check if a ProtocolMessage is an event or if it is a 
specific event. */
+function isEvent(
+  message: DebugProtocol.ProtocolMessage,
+): message is DebugProtocol.Event;
+function isEvent<K extends keyof EventMap>(
+  message: DebugProtocol.ProtocolMessage,
+  event: K,
+): message is EventMap[K];
+function isEvent(
+  message: DebugProtocol.ProtocolMessage,
+  event?: string,
+): boolean {
+  return (
+    message.type === "event" &&
+    (!event || (message as DebugProtocol.Event).event === event)
+  );
+}
+
+/** Tracks lldb-dap sessions for data visualizers. */
+export class DebugSessionTracker
+  implements vscode.DebugAdapterTrackerFactory, vscode.Disposable
+{
+  /**
+   * Tracks active modules for each debug sessions.
+   *
+   * The modules are kept in an array to maintain the load order of the 
modules.
+   */
+  private modules = new Map<vscode.DebugSession, DebugProtocol.Module[]>();
+  private modulesChanged = new vscode.EventEmitter<void>();
+
+  /**
+   * Fired when modules are changed for any active debug session.
+   *
+   * Use `debugSessionModules` to retieve the active modules for a given debug 
session.
+   */
+  onDidChangeModules: vscode.Event<void> = this.modulesChanged.event;
+
+  dispose() {
+    this.modules.clear();
+    this.modulesChanged.dispose();
+  }
+
+  createDebugAdapterTracker(
+    session: vscode.DebugSession,
+  ): vscode.ProviderResult<vscode.DebugAdapterTracker> {
+    return {
+      onDidSendMessage: (message) => this.onDidSendMessage(session, message),
+      onExit: () => this.onExit(session),
+    };
+  }
+
+  /**
+   * Retrieves the modules for the given debug session.
+   *
+   * Modules are returned in load order.
+   */
+  debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] {
+    return this.modules.get(session) ?? [];
+  }
+
+  /** Clear information from the active session. */
+  private onExit(session: vscode.DebugSession) {
+    this.modules.delete(session);
+    this.modulesChanged.fire();
+  }
+
+  private onDidSendMessage(
+    session: vscode.DebugSession,
+    message: DebugProtocol.ProtocolMessage,
+  ) {
+    if (isEvent(message, "module")) {
+      const { module, reason } = message.body;
+      const modules = this.modules.get(session) ?? [];
+      switch (reason) {
+        case "new":
+        case "changed": {
+          const index = modules.findIndex((m) => m.id === module.id);
+          if (index !== -1) {
+            modules[index] = module;
+          } else {
+            modules.push(module);
+          }
+          break;
+        }
+        case "removed": {
+          const index = modules.findIndex((m) => m.id === module.id);
+          if (index !== -1) {
+            modules.splice(index, 1);
+          }
+          break;
+        }
+        default:
+          console.error("unexpected module event reason");
+          break;
+      }
+      this.modules.set(session, modules);
+      this.modulesChanged.fire();
+    }
+  }
+}

diff  --git a/lldb/tools/lldb-dap/src-ts/disposable-context.ts 
b/lldb/tools/lldb-dap/src-ts/disposable-context.ts
index 39d9f18d2d85f..42ece763d247f 100644
--- a/lldb/tools/lldb-dap/src-ts/disposable-context.ts
+++ b/lldb/tools/lldb-dap/src-ts/disposable-context.ts
@@ -21,7 +21,7 @@ export class DisposableContext implements vscode.Disposable {
    *
    * @param disposable The disposable to register.
    */
-  public pushSubscription(disposable: vscode.Disposable) {
-    this._disposables.push(disposable);
+  public pushSubscription(...disposable: vscode.Disposable[]) {
+    this._disposables.push(...disposable);
   }
 }

diff  --git a/lldb/tools/lldb-dap/src-ts/extension.ts 
b/lldb/tools/lldb-dap/src-ts/extension.ts
index 0b014f953d5ba..a5c0a09ae60cf 100644
--- a/lldb/tools/lldb-dap/src-ts/extension.ts
+++ b/lldb/tools/lldb-dap/src-ts/extension.ts
@@ -5,6 +5,8 @@ import { DisposableContext } from "./disposable-context";
 import { LaunchUriHandler } from "./uri-launch-handler";
 import { LLDBDapConfigurationProvider } from "./debug-configuration-provider";
 import { LLDBDapServer } from "./lldb-dap-server";
+import { DebugSessionTracker } from "./debug-session-tracker";
+import { ModulesDataProvider } from "./ui/modules-data-provider";
 
 /**
  * This class represents the extension and manages its life cycle. Other 
extensions
@@ -15,23 +17,27 @@ export class LLDBDapExtension extends DisposableContext {
     super();
 
     const lldbDapServer = new LLDBDapServer();
-    this.pushSubscription(lldbDapServer);
+    const sessionTracker = new DebugSessionTracker();
 
     this.pushSubscription(
+      lldbDapServer,
+      sessionTracker,
       vscode.debug.registerDebugConfigurationProvider(
         "lldb-dap",
         new LLDBDapConfigurationProvider(lldbDapServer),
       ),
-    );
-
-    this.pushSubscription(
       vscode.debug.registerDebugAdapterDescriptorFactory(
         "lldb-dap",
         new LLDBDapDescriptorFactory(),
       ),
-    );
-
-    this.pushSubscription(
+      vscode.debug.registerDebugAdapterTrackerFactory(
+        "lldb-dap",
+        sessionTracker,
+      ),
+      vscode.window.registerTreeDataProvider(
+        "lldb-dap.modules",
+        new ModulesDataProvider(sessionTracker),
+      ),
       vscode.window.registerUriHandler(new LaunchUriHandler()),
     );
   }

diff  --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts 
b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
new file mode 100644
index 0000000000000..5af3d52e9870c
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
@@ -0,0 +1,59 @@
+import * as vscode from "vscode";
+import { DebugProtocol } from "@vscode/debugprotocol";
+import { DebugSessionTracker } from "../debug-session-tracker";
+
+/** A tree data provider for listing loaded modules for the active debug 
session. */
+export class ModulesDataProvider
+  implements vscode.TreeDataProvider<DebugProtocol.Module>
+{
+  private changeTreeData = new vscode.EventEmitter<void>();
+  readonly onDidChangeTreeData = this.changeTreeData.event;
+
+  constructor(private readonly tracker: DebugSessionTracker) {
+    tracker.onDidChangeModules(() => this.changeTreeData.fire());
+    vscode.debug.onDidChangeActiveDebugSession(() =>
+      this.changeTreeData.fire(),
+    );
+  }
+
+  getTreeItem(module: DebugProtocol.Module): vscode.TreeItem {
+    let treeItem = new vscode.TreeItem(/*label=*/ module.name);
+    if (module.path) {
+      treeItem.description = `${module.id} -- ${module.path}`;
+    } else {
+      treeItem.description = `${module.id}`;
+    }
+
+    const tooltip = new vscode.MarkdownString();
+    tooltip.appendMarkdown(`# Module '${module.name}'\n\n`);
+    tooltip.appendMarkdown(`- **id** : ${module.id}\n`);
+    if (module.addressRange) {
+      tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`);
+    }
+    if (module.path) {
+      tooltip.appendMarkdown(`- **path** : ${module.path}\n`);
+    }
+    if (module.version) {
+      tooltip.appendMarkdown(`- **version** : ${module.version}\n`);
+    }
+    if (module.symbolStatus) {
+      tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`);
+    }
+    if (module.symbolFilePath) {
+      tooltip.appendMarkdown(
+        `- **symbol file path** : ${module.symbolFilePath}\n`,
+      );
+    }
+
+    treeItem.tooltip = tooltip;
+    return treeItem;
+  }
+
+  getChildren(): DebugProtocol.Module[] {
+    if (!vscode.debug.activeDebugSession) {
+      return [];
+    }
+
+    return this.tracker.debugSessionModules(vscode.debug.activeDebugSession);
+  }
+}


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

Reply via email to