Skip to content

Commit 756174c

Browse files
committed
[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.
1 parent 90d8e4d commit 756174c

File tree

7 files changed

+189
-7
lines changed

7 files changed

+189
-7
lines changed

lldb/tools/lldb-dap/package-lock.json

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lldb/tools/lldb-dap/package.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
"devDependencies": {
3131
"@types/node": "^18.19.41",
3232
"@types/vscode": "1.75.0",
33+
"@vscode/debugprotocol": "^1.68.0",
3334
"@vscode/vsce": "^3.2.2",
34-
"prettier-plugin-curly": "^0.3.1",
3535
"prettier": "^3.4.2",
36+
"prettier-plugin-curly": "^0.3.1",
3637
"typescript": "^5.7.3"
3738
},
3839
"activationEvents": [
@@ -763,6 +764,16 @@
763764
}
764765
]
765766
}
766-
]
767+
],
768+
"views": {
769+
"debug": [
770+
{
771+
"id": "lldb-dap.modulesExplorer",
772+
"name": "LLDB Modules Explorer",
773+
"when": "inDebugMode && debugType == 'lldb-dap'",
774+
"icon": "$(symbol-module)"
775+
}
776+
]
777+
}
767778
}
768779
}

lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export class LLDBDapConfigurationProvider
7878
debugConfiguration: vscode.DebugConfiguration,
7979
token?: vscode.CancellationToken,
8080
): Promise<vscode.DebugConfiguration> {
81-
let config = vscode.workspace.getConfiguration("lldb-dap.defaults");
81+
let config = vscode.workspace.getConfiguration("lldb-dap");
8282
for (const [key, cfg] of Object.entries(configurations)) {
8383
if (Reflect.has(debugConfiguration, key)) {
8484
continue;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { DebugProtocol } from "@vscode/debugprotocol";
2+
import * as vscode from "vscode";
3+
4+
interface EventMap {
5+
module: DebugProtocol.ModuleEvent;
6+
}
7+
8+
function isEvent(
9+
message: DebugProtocol.ProtocolMessage,
10+
): message is DebugProtocol.Event;
11+
function isEvent<K extends keyof EventMap>(
12+
message: DebugProtocol.ProtocolMessage,
13+
event: K,
14+
): message is EventMap[K];
15+
function isEvent(
16+
message: DebugProtocol.ProtocolMessage,
17+
event?: string,
18+
): boolean {
19+
return (
20+
message.type === "event" &&
21+
(!event || (message as DebugProtocol.Event).event === event)
22+
);
23+
}
24+
25+
export class DebugSessionTracker
26+
implements vscode.DebugAdapterTrackerFactory, vscode.Disposable
27+
{
28+
private modules = new Map<vscode.DebugSession, DebugProtocol.Module[]>();
29+
private modulesChanged = new vscode.EventEmitter<void>();
30+
onDidChangeModules: vscode.Event<void> = this.modulesChanged.event;
31+
32+
dispose() {
33+
this.modules.clear();
34+
this.modulesChanged.dispose();
35+
}
36+
37+
createDebugAdapterTracker(
38+
session: vscode.DebugSession,
39+
): vscode.ProviderResult<vscode.DebugAdapterTracker> {
40+
return {
41+
onDidSendMessage: (message) => this.onDidSendMessage(session, message),
42+
onExit: () => this.onExit(session),
43+
};
44+
}
45+
46+
debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] {
47+
return this.modules.get(session) ?? [];
48+
}
49+
50+
private onExit(session: vscode.DebugSession) {
51+
this.modules.delete(session);
52+
}
53+
54+
private onDidSendMessage(
55+
session: vscode.DebugSession,
56+
message: DebugProtocol.ProtocolMessage,
57+
) {
58+
if (isEvent(message, "module")) {
59+
const { module, reason } = message.body;
60+
const modules = this.modules.get(session) ?? [];
61+
switch (reason) {
62+
case "new":
63+
case "changed": {
64+
const index = modules.findIndex((m) => m.id === module.id);
65+
if (index !== -1) {
66+
modules[index] = module;
67+
} else {
68+
modules.push(module);
69+
}
70+
break;
71+
}
72+
case "removed": {
73+
const index = modules.findIndex((m) => m.id === module.id);
74+
if (index !== -1) {
75+
modules.splice(index, 1);
76+
}
77+
break;
78+
}
79+
default:
80+
console.error("unexpected module event reason");
81+
break;
82+
}
83+
this.modules.set(session, modules);
84+
this.modulesChanged.fire();
85+
}
86+
}
87+
}

lldb/tools/lldb-dap/src-ts/disposable-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class DisposableContext implements vscode.Disposable {
2121
*
2222
* @param disposable The disposable to register.
2323
*/
24-
public pushSubscription(disposable: vscode.Disposable) {
25-
this._disposables.push(disposable);
24+
public pushSubscription(...disposable: vscode.Disposable[]) {
25+
this._disposables.push(...disposable);
2626
}
2727
}

lldb/tools/lldb-dap/src-ts/extension.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { DisposableContext } from "./disposable-context";
55
import { LaunchUriHandler } from "./uri-launch-handler";
66
import { LLDBDapConfigurationProvider } from "./debug-configuration-provider";
77
import { LLDBDapServer } from "./lldb-dap-server";
8+
import { DebugSessionTracker } from "./debug-session-tracker";
9+
import { ModuleDataProvider } from "./ui/modules-data-provider";
810

911
/**
1012
* This class represents the extension and manages its life cycle. Other extensions
@@ -31,6 +33,22 @@ export class LLDBDapExtension extends DisposableContext {
3133
),
3234
);
3335

36+
const sessionTracker = new DebugSessionTracker();
37+
38+
this.pushSubscription(
39+
vscode.debug.registerDebugAdapterTrackerFactory(
40+
"lldb-dap",
41+
sessionTracker,
42+
),
43+
);
44+
45+
this.pushSubscription(
46+
vscode.window.registerTreeDataProvider(
47+
"lldb-dap.modulesExplorer",
48+
new ModuleDataProvider(sessionTracker),
49+
),
50+
);
51+
3452
this.pushSubscription(
3553
vscode.window.registerUriHandler(new LaunchUriHandler()),
3654
);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as vscode from "vscode";
2+
import { DebugProtocol } from "@vscode/debugprotocol";
3+
import { DebugSessionTracker } from "../debug-session-tracker";
4+
5+
export class ModuleDataProvider
6+
implements vscode.TreeDataProvider<DebugProtocol.Module>
7+
{
8+
private changeTreeData = new vscode.EventEmitter<void>();
9+
readonly onDidChangeTreeData = this.changeTreeData.event;
10+
11+
constructor(private readonly tracker: DebugSessionTracker) {
12+
tracker.onDidChangeModules(() => this.changeTreeData.fire());
13+
vscode.debug.onDidChangeActiveDebugSession(() =>
14+
this.changeTreeData.fire(),
15+
);
16+
}
17+
18+
getTreeItem(module: DebugProtocol.Module): vscode.TreeItem {
19+
let treeItem = new vscode.TreeItem(/*label=*/ module.name);
20+
if (module.path) {
21+
treeItem.description = `${module.id} -- ${module.path}`;
22+
} else {
23+
treeItem.description = `${module.id}`;
24+
}
25+
26+
const tooltip = new vscode.MarkdownString();
27+
tooltip.appendMarkdown(`# Module '${module.name}'\n\n`);
28+
tooltip.appendMarkdown(`- **id** : ${module.id}\n`);
29+
if (module.addressRange) {
30+
tooltip.appendMarkdown(`- **load address** : ${module.addressRange}\n`);
31+
}
32+
if (module.path) {
33+
tooltip.appendMarkdown(`- **path** : ${module.path}\n`);
34+
}
35+
if (module.version) {
36+
tooltip.appendMarkdown(`- **version** : ${module.version}\n`);
37+
}
38+
if (module.symbolStatus) {
39+
tooltip.appendMarkdown(`- **symbol status** : ${module.symbolStatus}\n`);
40+
}
41+
if (module.symbolFilePath) {
42+
tooltip.appendMarkdown(
43+
`- **symbol file path** : ${module.symbolFilePath}\n`,
44+
);
45+
}
46+
47+
treeItem.tooltip = tooltip;
48+
return treeItem;
49+
}
50+
51+
getChildren(): DebugProtocol.Module[] {
52+
if (!vscode.debug.activeDebugSession) {
53+
return [];
54+
}
55+
56+
return this.tracker.debugSessionModules(vscode.debug.activeDebugSession);
57+
}
58+
}

0 commit comments

Comments
 (0)