Skip to content

Commit 6965610

Browse files
committed
Add session switching support
1 parent 8841a0f commit 6965610

File tree

9 files changed

+195
-118
lines changed

9 files changed

+195
-118
lines changed

src/entry-points/browser/extension.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import * as vscode from 'vscode';
1818
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
1919
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
2020
import { ContextTracker } from '../../plugin/context-tracker';
21-
import { MemoryProvider } from '../../plugin/memory-provider';
21+
import { MemoryProviderManager } from '../../plugin/memory-provider-manager';
2222
import { MemoryStorage } from '../../plugin/memory-storage';
2323
import { MemoryWebview } from '../../plugin/memory-webview-main';
2424
import { SessionTracker } from '../../plugin/session-tracker';
@@ -27,14 +27,14 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
2727
const registry = new AdapterRegistry();
2828
const sessionTracker = new SessionTracker();
2929
new ContextTracker(sessionTracker);
30-
const memoryProvider = new MemoryProvider(registry, sessionTracker);
31-
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
32-
const memoryStorage = new MemoryStorage(memoryProvider);
30+
const memoryProviderManager = new MemoryProviderManager(registry, sessionTracker);
31+
const memoryStorage = new MemoryStorage(sessionTracker, memoryProviderManager);
32+
const memoryView = new MemoryWebview(context.extensionUri, memoryProviderManager, sessionTracker, memoryStorage);
3333
const cAdapter = new CAdapter(registry);
3434

3535
registry.activate(context);
3636
sessionTracker.activate(context);
37-
memoryProvider.activate(context);
37+
memoryProviderManager.activate(context);
3838
memoryView.activate(context);
3939
memoryStorage.activate(context);
4040
cAdapter.activate(context);

src/entry-points/desktop/extension.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import * as vscode from 'vscode';
1818
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
1919
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
2020
import { ContextTracker } from '../../plugin/context-tracker';
21-
import { MemoryProvider } from '../../plugin/memory-provider';
21+
import { MemoryProviderManager } from '../../plugin/memory-provider-manager';
2222
import { MemoryStorage } from '../../plugin/memory-storage';
2323
import { MemoryWebview } from '../../plugin/memory-webview-main';
2424
import { SessionTracker } from '../../plugin/session-tracker';
@@ -27,14 +27,14 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
2727
const registry = new AdapterRegistry();
2828
const sessionTracker = new SessionTracker();
2929
new ContextTracker(sessionTracker);
30-
const memoryProvider = new MemoryProvider(registry, sessionTracker);
31-
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
32-
const memoryStorage = new MemoryStorage(memoryProvider);
30+
const memoryProviderManager = new MemoryProviderManager(registry, sessionTracker);
31+
const memoryStorage = new MemoryStorage(sessionTracker, memoryProviderManager);
32+
const memoryView = new MemoryWebview(context.extensionUri, memoryProviderManager, sessionTracker, memoryStorage);
3333
const cAdapter = new CAdapter(registry);
3434

3535
registry.activate(context);
3636
sessionTracker.activate(context);
37-
memoryProvider.activate(context);
37+
memoryProviderManager.activate(context);
3838
memoryView.activate(context);
3939
memoryStorage.activate(context);
4040
cAdapter.activate(context);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/********************************************************************************
2+
* Copyright (C) 2025 Ericsson, Arm and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
17+
import * as vscode from 'vscode';
18+
import { AdapterRegistry } from './adapter-registry/adapter-registry';
19+
import { MemoryProvider } from './memory-provider';
20+
import { SessionTracker } from './session-tracker';
21+
22+
export class MemoryProviderManager {
23+
24+
protected memoryProviders = new Map<vscode.DebugSession, MemoryProvider>();
25+
26+
constructor(protected adapterRegistry: AdapterRegistry, protected sessionTracker: SessionTracker) {
27+
}
28+
29+
public activate(context: vscode.ExtensionContext): void {
30+
const createDebugAdapterTracker = (session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> => {
31+
const handlerForSession = this.adapterRegistry.getHandlerForSession(session.type);
32+
const contributedTracker = handlerForSession?.initializeAdapterTracker?.(session);
33+
return contributedTracker;
34+
};
35+
context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('*', { createDebugAdapterTracker }));
36+
}
37+
38+
public getProvider(sessionId: string | undefined): MemoryProvider {
39+
const session = this.sessionTracker.assertSession(sessionId);
40+
41+
if (!this.memoryProviders.has(session)) {
42+
this.memoryProviders.set(session, new MemoryProvider(session.id, this.adapterRegistry, this.sessionTracker));
43+
}
44+
45+
return this.memoryProviders.get(session)!;
46+
}
47+
}

src/plugin/memory-provider.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
********************************************************************************/
1616

1717
import { DebugProtocol } from '@vscode/debugprotocol';
18-
import * as vscode from 'vscode';
1918
import { sendRequest } from '../common/debug-requests';
2019
import { stringToBytesMemory } from '../common/memory';
2120
import { VariableRange } from '../common/memory-range';
@@ -24,37 +23,17 @@ import { MemoryDisplaySettingsContribution } from '../common/webview-configurati
2423
import { AdapterRegistry } from './adapter-registry/adapter-registry';
2524
import { isSessionEvent, SessionTracker } from './session-tracker';
2625

27-
export interface LabeledUint8Array extends Uint8Array {
28-
label?: string;
29-
}
30-
3126
export class MemoryProvider {
3227
protected scheduledOnDidMemoryWriteEvents: { [sessionidmemoryReference: string]: ((response: WriteMemoryResult) => void) | undefined } = {};
3328

34-
protected _sessionId: string | undefined;
35-
public get sessionId(): string | undefined { return this._sessionId; }
36-
37-
constructor(protected adapterRegistry: AdapterRegistry, protected sessionTracker: SessionTracker) {
29+
constructor(protected sessionId: string, protected adapterRegistry: AdapterRegistry, protected sessionTracker: SessionTracker) {
3830
this.sessionTracker.onSessionEvent(event => {
39-
if (isSessionEvent('memory-written', event)) {
31+
if (isSessionEvent('memory-written', event) && event.session.raw.id === this.sessionId) {
4032
delete this.scheduledOnDidMemoryWriteEvents[event.session.raw.id + '_' + event.data.memoryReference];
4133
}
4234
});
4335
}
4436

45-
public activate(context: vscode.ExtensionContext): void {
46-
const createDebugAdapterTracker = (session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> => {
47-
const handlerForSession = this.adapterRegistry.getHandlerForSession(session.type);
48-
const contributedTracker = handlerForSession?.initializeAdapterTracker?.(session);
49-
return contributedTracker;
50-
};
51-
context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('*', { createDebugAdapterTracker }));
52-
}
53-
54-
public setSessionId(value: string | undefined): void {
55-
this._sessionId = value;
56-
}
57-
5837
public async readMemory(args: DebugProtocol.ReadMemoryArguments): Promise<ReadMemoryResult> {
5938
const session = this.sessionTracker.assertSession(this.sessionId);
6039
return sendRequest(this.sessionTracker.assertDebugCapability(session, 'supportsReadMemoryRequest', 'read memory'), 'readMemory', args);

src/plugin/memory-storage.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ import { toHexStringWithRadixMarker } from '../common/memory-range';
2828
import { ApplyMemoryArguments, ApplyMemoryResult, MemoryOptions, StoreMemoryArguments } from '../common/messaging';
2929
import { isWebviewContext } from '../common/webview-context';
3030
import { MemoryProvider } from './memory-provider';
31+
import { MemoryProviderManager } from './memory-provider-manager';
32+
import { SessionTracker } from './session-tracker';
3133

32-
export const StoreCommandType = `${manifest.PACKAGE_NAME}.store-file`;
33-
export const ApplyCommandType = `${manifest.PACKAGE_NAME}.apply-file`;
34+
const StoreCommandType = `${manifest.PACKAGE_NAME}.store-file`;
35+
const ApplyCommandType = `${manifest.PACKAGE_NAME}.apply-file`;
3436

3537
const VALID_FILE_NAME_CHARS = /[^a-zA-Z0-9 _-]/g;
3638

@@ -49,19 +51,26 @@ interface ApplyMemoryOptions {
4951
uri: vscode.Uri;
5052
}
5153

54+
const getActiveSession = () => vscode.debug.activeDebugSession;
55+
5256
export class MemoryStorage {
53-
constructor(protected memoryProvider: MemoryProvider) {
57+
constructor(protected sessionTracker: SessionTracker, protected memoryProviderManager: MemoryProviderManager) {
5458
}
5559

5660
public activate(context: vscode.ExtensionContext): void {
5761
context.subscriptions.push(
58-
vscode.commands.registerCommand(StoreCommandType, args => this.storeMemory(args)),
59-
vscode.commands.registerCommand(ApplyCommandType, args => this.applyMemory(args))
62+
vscode.commands.registerCommand(StoreCommandType, args => this.storeMemory(getActiveSession()?.id, args)),
63+
vscode.commands.registerCommand(ApplyCommandType, args => this.applyMemory(getActiveSession()?.id, args))
6064
);
6165
}
6266

63-
public async storeMemory(args?: StoreMemoryArguments): Promise<void> {
64-
const providedDefaultOptions = await this.storeArgsToOptions(args);
67+
public async storeMemory(sessionId: string | undefined, args?: StoreMemoryArguments): Promise<void> {
68+
// Even if we disable the command in VS Code through enablement or when condition, programmatic execution is still possible.
69+
// However, we want to fail early in case the user tries to execute a disabled command
70+
this.sessionTracker.assertDebugCapability(this.sessionTracker.assertSession(sessionId), 'supportsReadMemoryRequest', 'store memory');
71+
72+
const memoryProvider = this.memoryProviderManager.getProvider(sessionId);
73+
const providedDefaultOptions = await this.storeArgsToOptions(memoryProvider, args);
6574
const options = await this.getStoreMemoryOptions(providedDefaultOptions);
6675
if (!options) {
6776
// user aborted process
@@ -70,7 +79,7 @@ export class MemoryStorage {
7079

7180
const { outputFile, ...readArgs } = options;
7281
try {
73-
const memoryResponse = await this.memoryProvider.readMemory(readArgs);
82+
const memoryResponse = await memoryProvider.readMemory(readArgs);
7483
const memory = createMemoryFromRead(memoryResponse);
7584
const memoryMap = new MemoryMap({ [Number(memory.address)]: memory.bytes });
7685
await vscode.workspace.fs.writeFile(outputFile, new TextEncoder().encode(memoryMap.asHexString()));
@@ -89,7 +98,7 @@ export class MemoryStorage {
8998
}
9099
}
91100

92-
protected async storeArgsToOptions(args?: StoreMemoryArguments): Promise<Partial<StoreMemoryOptions>> {
101+
protected async storeArgsToOptions(memoryProvider: MemoryProvider, args?: StoreMemoryArguments): Promise<Partial<StoreMemoryOptions>> {
93102
if (!args) {
94103
return {};
95104
}
@@ -99,8 +108,8 @@ export class MemoryStorage {
99108
if (isVariablesContext(args)) {
100109
try {
101110
const variableName = args.variable.evaluateName ?? args.variable.name;
102-
const count = await this.memoryProvider.getSizeOfVariable(variableName);
103-
const memoryReference = args.variable.memoryReference ?? await this.memoryProvider.getAddressOfVariable(variableName);
111+
const count = await memoryProvider.getSizeOfVariable(variableName);
112+
const memoryReference = args.variable.memoryReference ?? await memoryProvider.getAddressOfVariable(variableName);
104113
return { count: Number(count), memoryReference, offset: 0, proposedOutputName: variableName };
105114
} catch (error) {
106115
// ignore, we are just using them as default values
@@ -153,7 +162,12 @@ export class MemoryStorage {
153162
return { memoryReference, offset: Number(offset), count: Number(count), outputFile };
154163
}
155164

156-
public async applyMemory(args?: ApplyMemoryArguments): Promise<ApplyMemoryResult> {
165+
public async applyMemory(sessionId: string | undefined, args?: ApplyMemoryArguments): Promise<ApplyMemoryResult> {
166+
// Even if we disable the command in VS Code through enablement or when condition, programmatic execution is still possible.
167+
// However, we want to fail early in case the user tries to execute a disabled command
168+
this.sessionTracker.assertDebugCapability(this.sessionTracker.assertSession(sessionId), 'supportsWriteMemoryRequest', 'apply memory');
169+
170+
const memoryProvider = this.memoryProviderManager.getProvider(sessionId);
157171
const providedDefaultOptions = await this.applyArgsToOptions(args);
158172
const options = await this.getApplyMemoryOptions(providedDefaultOptions);
159173
if (!options) {
@@ -169,7 +183,7 @@ export class MemoryStorage {
169183
memoryReference = toHexStringWithRadixMarker(address);
170184
count = memory.length;
171185
const data = bytesToStringMemory(memory);
172-
await this.memoryProvider.writeMemory({ memoryReference, data });
186+
await memoryProvider.writeMemory({ memoryReference, data });
173187
}
174188
await vscode.window.showInformationMessage(`Memory from '${vscode.workspace.asRelativePath(options.uri)}' applied.`);
175189
return { memoryReference, count, offset: 0 };

0 commit comments

Comments
 (0)