Skip to content

Commit 5d8b8db

Browse files
committed
updating all views on receiving invalidated event or memory events
1 parent e263708 commit 5d8b8db

7 files changed

Lines changed: 139 additions & 9 deletions

File tree

src/debug-session/__test__/debug-session.factory.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ export type TrackerCallbacks = {
3535
onStackTrace: (cb: (session: { session: Session }) => Promise<void>) => { dispose: jest.Mock };
3636
onDidChangeActiveStackItem: (cb: (session: { session: Session }) => Promise<void>) => { dispose: jest.Mock };
3737
onWillStartSession: (cb: (session: Session) => Promise<void>) => { dispose: jest.Mock };
38+
onMemory: (cb: (event: { session: Session }) => Promise<void>) => { dispose: jest.Mock };
3839
callbacks: Partial<{
3940
willStop: (session: Session) => Promise<void>;
4041
connected: (session: Session) => Promise<void>;
4142
activeSession: (session: Session | undefined) => Promise<void>;
4243
stackTrace: (session: { session: Session }) => Promise<void>;
4344
activeStackItem: (session: { session: Session }) => Promise<void>;
4445
willStart: (session: Session) => Promise<void>;
46+
memory: (event: { session: Session }) => Promise<void>;
4547
}>;
4648
};
4749

@@ -73,6 +75,10 @@ export const trackerFactory = (): TrackerCallbacks => {
7375
callbacks.willStart = cb;
7476
return { dispose: jest.fn() };
7577
},
78+
onMemory: (cb) => {
79+
callbacks.memory = cb;
80+
return { dispose: jest.fn() };
81+
},
7682
};
7783
};
7884

src/debug-session/gdbtarget-debug-tracker.test.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
GDBTargetDebugTracker,
2121
SessionStackItem,
2222
SessionStackTrace,
23-
StoppedEvent
23+
StoppedEvent,
24+
MemoryEvent
2425
} from './gdbtarget-debug-tracker';
2526
import { debugSessionFactory, extensionContextFactory } from '../__test__/vscode.factory';
2627
import { GDBTargetDebugSession } from './gdbtarget-debug-session';
@@ -223,6 +224,51 @@ describe('GDBTargetDebugTracker', () => {
223224
expect(result!.event).toEqual(continuedEvent);
224225
});
225226

227+
it('sends a session memory event', async () => {
228+
let gdbSession: GDBTargetDebugSession|undefined = undefined;
229+
debugTracker.onWillStartSession(session => gdbSession = session);
230+
let result: MemoryEvent|undefined = undefined;
231+
debugTracker.onMemory(event => result = event);
232+
const tracker = await adapterFactory!.createDebugAdapterTracker(debugSessionFactory(debugConfigurationFactory()));
233+
tracker!.onWillStartSession!();
234+
const memoryEvent: DebugProtocol.MemoryEvent = {
235+
event: 'memory',
236+
type: 'event',
237+
seq: 1,
238+
body: {
239+
memoryReference: '0x1234',
240+
offset: 0,
241+
count: 4
242+
}
243+
};
244+
tracker!.onDidSendMessage!(memoryEvent);
245+
expect(gdbSession).toBeDefined();
246+
expect(result).toBeDefined();
247+
expect(result!.session).toEqual(gdbSession);
248+
expect(result!.event).toEqual(memoryEvent);
249+
});
250+
251+
it('sends a stack trace event on receiving an invalidated event', async () => {
252+
let gdbSession: GDBTargetDebugSession|undefined = undefined;
253+
debugTracker.onWillStartSession(session => gdbSession = session);
254+
let result: SessionStackTrace|undefined = undefined;
255+
debugTracker.onStackTrace(event => result = event);
256+
const tracker = await adapterFactory!.createDebugAdapterTracker(debugSessionFactory(debugConfigurationFactory()));
257+
tracker!.onWillStartSession!();
258+
const invalidatedEvent: DebugProtocol.InvalidatedEvent = {
259+
event: 'invalidated',
260+
type: 'event',
261+
seq: 1,
262+
body: {
263+
areas: ['stack']
264+
}
265+
};
266+
tracker!.onDidSendMessage!(invalidatedEvent);
267+
expect(gdbSession).toBeDefined();
268+
expect(result).toBeDefined();
269+
expect(result!.session).toEqual(gdbSession);
270+
});
271+
226272
it('sends a session stopped event', async () => {
227273
let gdbSession: GDBTargetDebugSession|undefined = undefined;
228274
debugTracker.onWillStartSession(session => gdbSession = session);

src/debug-session/gdbtarget-debug-tracker.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface SessionEvent<T extends DebugProtocol.Event> {
2828

2929
export type ContinuedEvent = SessionEvent<DebugProtocol.ContinuedEvent>;
3030
export type StoppedEvent = SessionEvent<DebugProtocol.StoppedEvent>;
31+
export type MemoryEvent = SessionEvent<DebugProtocol.MemoryEvent>;
3132

3233
export interface SessionCapabilities {
3334
session: GDBTargetDebugSession;
@@ -78,6 +79,10 @@ export class GDBTargetDebugTracker {
7879
private readonly _onStackTrace: vscode.EventEmitter<SessionStackTrace> = new vscode.EventEmitter<SessionStackTrace>();
7980
public readonly onStackTrace: vscode.Event<SessionStackTrace> = this._onStackTrace.event;
8081

82+
private readonly _onMemory: vscode.EventEmitter<MemoryEvent> = new vscode.EventEmitter<MemoryEvent>();
83+
public readonly onMemory: vscode.Event<MemoryEvent> = this._onMemory.event;
84+
85+
8186
public activate(context: vscode.ExtensionContext) {
8287
const createDebugAdapterTracker = (session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> => {
8388
return {
@@ -151,6 +156,16 @@ export class GDBTargetDebugTracker {
151156
case 'output':
152157
gdbTargetSession?.filterOutputEvent(event as DebugProtocol.OutputEvent);
153158
break;
159+
case 'invalidated':
160+
if (gdbTargetSession && gdbTargetSession.targetState === 'stopped' && event.body && event.body.threadId) {
161+
this._onStackTrace.fire({session: gdbTargetSession, threadId: event.body.threadId, stackFrames: event.body.stackFrameId ?? [], totalFrames: 0});
162+
}
163+
break;
164+
case 'memory':
165+
if (gdbTargetSession) {
166+
this._onMemory.fire({session: gdbTargetSession, event: event as DebugProtocol.MemoryEvent});
167+
}
168+
break;
154169
}
155170
}
156171

src/views/component-viewer/component-viewer-base.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,19 @@ export class ComponentViewerBase {
328328
const onWillStartSessionDisposable = tracker.onWillStartSession(async (session) => {
329329
await this.handleOnWillStartSession(session);
330330
});
331+
const onMemoryDisposable = tracker.onMemory(async (event) => {
332+
const session = event.session;
333+
await this.handleOnMemoryEvent(session);
334+
});
331335
// clear all disposables on extension deactivation
332336
this._context.subscriptions.push(
333337
onWillStopSessionDisposable,
334338
onConnectedDisposable,
335339
onDidChangeActiveDebugSessionDisposable,
336340
onStackTraceDisposable,
337341
onDidChangeActiveStackItemDisposable,
338-
onWillStartSessionDisposable
342+
onWillStartSessionDisposable,
343+
onMemoryDisposable
339344
);
340345
}
341346

@@ -348,6 +353,13 @@ export class ComponentViewerBase {
348353
this.schedulePendingUpdate('stackTrace');
349354
}
350355

356+
protected async handleOnMemoryEvent(session: GDBTargetDebugSession): Promise<void> {
357+
if (this._activeSession?.session.id !== session.session.id) {
358+
throw new Error(`${this._viewName}: Received memory event for session ${session.session.id} while active session is ${this._activeSession?.session.id}`);
359+
}
360+
this.schedulePendingUpdate('stackTrace');
361+
}
362+
351363
protected async handleOnStackItemChanged(session: GDBTargetDebugSession): Promise<void> {
352364
// If the active session is not the one being updated, update it.
353365
// This can happen when a session is started and stack trace/item events are emitted before the session is set as active in the component viewer.

src/views/component-viewer/test/unit/component-viewer-base.test.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const instanceFactory = jest.fn(() => ({
3838
getGuiTree: jest.fn<ScvdGuiInterface[] | undefined, []>(() => []),
3939
updateActiveSession: jest.fn(),
4040
cancelExecution: jest.fn(),
41+
setSvdPath: jest.fn(),
4142
}));
4243

4344
jest.mock('../../component-viewer-instance', () => ({
@@ -157,12 +158,12 @@ describe('ComponentViewerBase', () => {
157158
expect(vscode.commands.registerCommand).toHaveBeenCalledWith('vscode-cmsis-debugger.testClass.lockComponent', expect.any(Function));
158159
expect(vscode.commands.registerCommand).toHaveBeenCalledWith('vscode-cmsis-debugger.testClass.unlockComponent', expect.any(Function));
159160
expect(vscode.commands.registerCommand).toHaveBeenCalledWith('vscode-cmsis-debugger.testClass.expandAll', expect.any(Function));
160-
// 1 tree view + 2 event listeners + 7 commands + 6 tracker disposables
161-
expect(context.subscriptions.length).toBe(16);
161+
// 1 tree view + 2 event listeners + 7 commands + 7 tracker disposables
162+
expect(context.subscriptions.length).toBe(17);
162163
expect(vscode.commands.registerCommand).toHaveBeenCalledWith('vscode-cmsis-debugger.testClass.filterTree', expect.any(Function));
163164
expect(vscode.commands.registerCommand).toHaveBeenCalledWith('vscode-cmsis-debugger.testClass.clearFilter', expect.any(Function));
164-
// 1 tree view + 2 event listeners + 7 commands + 6 tracker disposables
165-
expect(context.subscriptions.length).toBe(16);
165+
// 1 tree view + 2 event listeners + 7 commands + 7 tracker disposables
166+
expect(context.subscriptions.length).toBe(17);
166167
});
167168

168169
it('should fail to activate the test class tree data provider if view is not correctly loaded', async () => {
@@ -213,6 +214,22 @@ describe('ComponentViewerBase', () => {
213214
expect(instanceFactory).toHaveBeenCalledTimes(2);
214215
});
215216

217+
it('sets svd path from debug configuration when reading scvd files', async () => {
218+
const session = debugSessionFactory('s1', ['a.scvd']);
219+
(session.session as unknown as { configuration: { definitionPath: string } }).configuration = {
220+
definitionPath: '/device.svd',
221+
};
222+
(controller as unknown as { _activeSession?: Session })._activeSession = session;
223+
224+
const instance = instanceFactory();
225+
instanceFactory.mockImplementationOnce(() => instance);
226+
227+
const readScvdFiles = getReadScvdFiles(controller);
228+
await readScvdFiles(tracker, session);
229+
230+
expect(instance.setSvdPath).toHaveBeenCalledWith('/device.svd');
231+
});
232+
216233
it('skips reading scvd files when no active session is set', async () => {
217234
const session = debugSessionFactory('s1', ['a.scvd']);
218235
const readScvdFiles = getReadScvdFiles(controller);
@@ -235,6 +252,7 @@ describe('ComponentViewerBase', () => {
235252
getGuiTree: jest.fn(() => []),
236253
updateActiveSession: jest.fn(),
237254
cancelExecution: jest.fn(),
255+
setSvdPath: jest.fn(),
238256
}));
239257
const showErrorSpy = jest.spyOn(vscode.window, 'showErrorMessage').mockResolvedValue(undefined);
240258
const errorSpy = jest.spyOn(componentViewerLogger, 'error');
@@ -292,6 +310,8 @@ describe('ComponentViewerBase', () => {
292310
await tracker.callbacks.stackTrace?.({ session });
293311
expect((controller as unknown as { _activeSession?: Session })._activeSession).toBe(session);
294312

313+
await tracker.callbacks.memory?.({ session });
314+
295315

296316
(controller as unknown as { _activeSession?: Session })._activeSession = session;
297317
await tracker.callbacks.willStop?.(session);
@@ -351,6 +371,21 @@ describe('ComponentViewerBase', () => {
351371
expect((controller as unknown as { _activeSession?: Session })._activeSession).toBe(sessionA);
352372
});
353373

374+
it('updates instances on memory event', async () => {
375+
const sessionA = debugSessionFactory('s1', [], 'stopped');
376+
377+
(controller as unknown as { _activeSession?: Session })._activeSession = sessionA;
378+
379+
const scheduleSpy = jest
380+
.spyOn(controller as unknown as { schedulePendingUpdate: (reason: UpdateReason) => void }, 'schedulePendingUpdate')
381+
.mockImplementation(() => undefined);
382+
383+
const handleOnMemoryEvent = (controller as unknown as { handleOnMemoryEvent: (s: Session) => Promise<void> }).handleOnMemoryEvent.bind(controller);
384+
await handleOnMemoryEvent(sessionA);
385+
386+
expect(scheduleSpy).toHaveBeenCalledWith('stackTrace');
387+
});
388+
354389
it('does not update active session when stack item matches the active session', async () => {
355390
const sessionA = debugSessionFactory('s1');
356391
const updateSpy = jest.fn();

src/views/live-watch/live-watch.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('LiveWatchTreeDataProvider', () => {
9090
expect((liveWatchTreeDataProvider as any).activeSession).toBeUndefined();
9191
});
9292

93-
it('refreshes on stopped event and on onDidChangeActiveStackItem', async () => {
93+
it('refreshes on stopped event and on onDidChangeActiveStackItem and onMemory event', async () => {
9494
const refreshSpy = jest.spyOn(liveWatchTreeDataProvider as any, 'refresh').mockResolvedValue('');
9595
await liveWatchTreeDataProvider.activate(tracker);
9696
// Activate session
@@ -103,6 +103,10 @@ describe('LiveWatchTreeDataProvider', () => {
103103
// Fire onDidChangeActiveStackItem event
104104
(tracker as any)._onDidChangeActiveStackItem.fire({ item: { frameId: 1 } });
105105
expect(refreshSpy).toHaveBeenCalled();
106+
refreshSpy.mockClear();
107+
// Fire onMemory event
108+
(tracker as any)._onMemory.fire({ session: gdbtargetDebugSession, event: { memoryReference: '0x1234', offset: 0, count: 4 } });
109+
expect(refreshSpy).toHaveBeenCalled();
106110
});
107111

108112
it('calls save function when extension is deactivating', async () => {

src/views/live-watch/live-watch.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import * as vscode from 'vscode';
1818
import { DebugProtocol } from '@vscode/debugprotocol';
19-
import { GDBTargetDebugSession, GDBTargetDebugTracker } from '../../debug-session';
19+
import { GDBTargetDebugSession, GDBTargetDebugTracker, SessionEvent } from '../../debug-session';
2020
import { vscodeViewExists } from '../../vscode-utils';
2121
import { logger } from '../..';
2222

@@ -116,11 +116,15 @@ export class LiveWatchTreeDataProvider implements vscode.TreeDataProvider<LiveWa
116116
await this.refresh();
117117
await this.save();
118118
});
119+
const onMemory = tracker.onMemory(async (event) => {
120+
await this.handleOnMemoryEvent(event);
121+
});
119122
this._context.subscriptions.push(
120123
onDidChangeActiveDebugSession,
121124
onWillStartSession,
122125
onStackTrace,
123-
onWillStopSession);
126+
onWillStopSession,
127+
onMemory);
124128
return true;
125129
}
126130

@@ -141,6 +145,14 @@ export class LiveWatchTreeDataProvider implements vscode.TreeDataProvider<LiveWa
141145
});
142146
}
143147

148+
private async handleOnMemoryEvent(event: SessionEvent<DebugProtocol.MemoryEvent>): Promise<void> {
149+
const gdbTargetSession = event.session;
150+
if (this._activeSession?.session.id !== gdbTargetSession.session.id) {
151+
return;
152+
}
153+
await this.refresh();
154+
}
155+
144156
private async addVSCodeCommands(): Promise<boolean> {
145157
if (!await vscodeViewExists('liveWatch')) {
146158
return false;

0 commit comments

Comments
 (0)