[Lldb-commits] [lldb] 611d81b - [lldb-dap] Adding a modules explorer to lldb-dap ext. (#138977)
via lldb-commits
lldb-commits at lists.llvm.org
Thu May 8 15:25:32 PDT 2025
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);
+ }
+}
More information about the lldb-commits
mailing list