https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/138977
>From 756174c282fc7101bfadb29556f39ce046ad0116 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Wed, 7 May 2025 14:54:29 -0700 Subject: [PATCH 1/2] [lldb-dap] Adding a modules explorer to lldb-dap ext. 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. --- lldb/tools/lldb-dap/package-lock.json | 12 ++- lldb/tools/lldb-dap/package.json | 15 +++- .../src-ts/debug-configuration-provider.ts | 2 +- .../lldb-dap/src-ts/debug-session-tracker.ts | 87 +++++++++++++++++++ .../lldb-dap/src-ts/disposable-context.ts | 4 +- lldb/tools/lldb-dap/src-ts/extension.ts | 18 ++++ .../src-ts/ui/modules-data-provider.ts | 58 +++++++++++++ 7 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts create mode 100644 lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts 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 f66badc2a930f..a8dce7cab8a79 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": [ @@ -763,6 +764,16 @@ } ] } - ] + ], + "views": { + "debug": [ + { + "id": "lldb-dap.modulesExplorer", + "name": "LLDB Modules Explorer", + "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..d756e9e743bb0 --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -0,0 +1,87 @@ +import { DebugProtocol } from "@vscode/debugprotocol"; +import * as vscode from "vscode"; + +interface EventMap { + module: DebugProtocol.ModuleEvent; +} + +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) + ); +} + +export class DebugSessionTracker + implements vscode.DebugAdapterTrackerFactory, vscode.Disposable +{ + private modules = new Map<vscode.DebugSession, DebugProtocol.Module[]>(); + private modulesChanged = new vscode.EventEmitter<void>(); + 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), + }; + } + + debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] { + return this.modules.get(session) ?? []; + } + + private onExit(session: vscode.DebugSession) { + this.modules.delete(session); + } + + 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..1bca842480809 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 { ModuleDataProvider } from "./ui/modules-data-provider"; /** * This class represents the extension and manages its life cycle. Other extensions @@ -31,6 +33,22 @@ export class LLDBDapExtension extends DisposableContext { ), ); + const sessionTracker = new DebugSessionTracker(); + + this.pushSubscription( + vscode.debug.registerDebugAdapterTrackerFactory( + "lldb-dap", + sessionTracker, + ), + ); + + this.pushSubscription( + vscode.window.registerTreeDataProvider( + "lldb-dap.modulesExplorer", + new ModuleDataProvider(sessionTracker), + ), + ); + this.pushSubscription( 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..d5cea513d7ef4 --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -0,0 +1,58 @@ +import * as vscode from "vscode"; +import { DebugProtocol } from "@vscode/debugprotocol"; +import { DebugSessionTracker } from "../debug-session-tracker"; + +export class ModuleDataProvider + 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); + } +} >From da002083736f97692471d661cd31a64b37835205 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Wed, 7 May 2025 16:16:37 -0700 Subject: [PATCH 2/2] Adjusting the modules tree view name and adding some documentation to help explain the code a bit. --- lldb/tools/lldb-dap/package.json | 4 ++-- .../lldb-dap/src-ts/debug-session-tracker.ts | 24 ++++++++++++++++++- lldb/tools/lldb-dap/src-ts/extension.ts | 24 +++++-------------- .../src-ts/ui/modules-data-provider.ts | 3 ++- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index a8dce7cab8a79..1149a33719ae5 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -768,8 +768,8 @@ "views": { "debug": [ { - "id": "lldb-dap.modulesExplorer", - "name": "LLDB Modules Explorer", + "id": "lldb-dap.modules", + "name": "Modules", "when": "inDebugMode && debugType == 'lldb-dap'", "icon": "$(symbol-module)" } 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 d756e9e743bb0..1ce190938d9c7 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -1,10 +1,13 @@ 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; + "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; @@ -22,11 +25,23 @@ function isEvent( ); } +/** 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() { @@ -43,12 +58,19 @@ export class DebugSessionTracker }; } + /** + * 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( diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts index 1bca842480809..a5c0a09ae60cf 100644 --- a/lldb/tools/lldb-dap/src-ts/extension.ts +++ b/lldb/tools/lldb-dap/src-ts/extension.ts @@ -6,7 +6,7 @@ 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 { ModuleDataProvider } from "./ui/modules-data-provider"; +import { ModulesDataProvider } from "./ui/modules-data-provider"; /** * This class represents the extension and manages its life cycle. Other extensions @@ -17,39 +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(), ), - ); - - const sessionTracker = new DebugSessionTracker(); - - this.pushSubscription( vscode.debug.registerDebugAdapterTrackerFactory( "lldb-dap", sessionTracker, ), - ); - - this.pushSubscription( vscode.window.registerTreeDataProvider( - "lldb-dap.modulesExplorer", - new ModuleDataProvider(sessionTracker), + "lldb-dap.modules", + new ModulesDataProvider(sessionTracker), ), - ); - - this.pushSubscription( 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 index d5cea513d7ef4..5af3d52e9870c 100644 --- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts @@ -2,7 +2,8 @@ import * as vscode from "vscode"; import { DebugProtocol } from "@vscode/debugprotocol"; import { DebugSessionTracker } from "../debug-session-tracker"; -export class ModuleDataProvider +/** 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>(); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits