Skip to content

Commit e4cd97b

Browse files
authored
Refactoring as preparation for periodic refresh timer (#563)
* refactoring --------- Signed-off-by: Jens Reinecke <[email protected]>
1 parent 932389b commit e4cd97b

File tree

4 files changed

+125
-171
lines changed

4 files changed

+125
-171
lines changed

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

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ import {
1919
ContinuedEvent,
2020
GDBTargetDebugTracker,
2121
SessionStackItem,
22-
StackTraceRequest,
23-
StackTraceResponse,
22+
SessionStackTrace,
2423
StoppedEvent
2524
} from './gdbtarget-debug-tracker';
2625
import { debugSessionFactory, extensionContextFactory } from '../__test__/vscode.factory';
@@ -160,13 +159,12 @@ describe('GDBTargetDebugTracker', () => {
160159
expect(connectedSession).toBeDefined();
161160
});
162161

163-
it('sends an onStackTraceRequest event', async () => {
162+
it('sends an onStackTrace event', async () => {
164163
const tracker = await adapterFactory!.createDebugAdapterTracker(debugSessionFactory(debugConfigurationFactory()));
165164
let gdbSession: GDBTargetDebugSession|undefined = undefined;
166165
debugTracker.onWillStartSession(session => gdbSession = session);
167-
let result: StackTraceRequest|undefined = undefined;
168-
debugTracker.onStackTraceRequest(request => result = request);
169-
tracker!.onWillStartSession!();
166+
let result: SessionStackTrace;
167+
debugTracker.onStackTrace(request => result = request);
170168
const stackTraceRequest: DebugProtocol.StackTraceRequest = {
171169
command: 'stackTrace',
172170
type: 'request',
@@ -175,20 +173,6 @@ describe('GDBTargetDebugTracker', () => {
175173
threadId: 1
176174
}
177175
};
178-
tracker!.onWillReceiveMessage!(stackTraceRequest);
179-
expect(gdbSession).toBeDefined();
180-
expect(result).toBeDefined();
181-
expect(result!.session).toEqual(gdbSession);
182-
expect(result!.request).toEqual(stackTraceRequest);
183-
});
184-
185-
it('sends an onStackTraceResponse event', async () => {
186-
let gdbSession: GDBTargetDebugSession|undefined = undefined;
187-
debugTracker.onWillStartSession(session => gdbSession = session);
188-
let result: StackTraceResponse|undefined = undefined;
189-
debugTracker.onStackTraceResponse(response => result = response);
190-
const tracker = await adapterFactory!.createDebugAdapterTracker(debugSessionFactory(debugConfigurationFactory()));
191-
tracker!.onWillStartSession!();
192176
const stackTraceResponse: DebugProtocol.StackTraceResponse = {
193177
command: 'stackTrace',
194178
type: 'response',
@@ -206,11 +190,15 @@ describe('GDBTargetDebugTracker', () => {
206190
]
207191
}
208192
};
193+
tracker!.onWillStartSession!();
194+
tracker!.onWillReceiveMessage!(stackTraceRequest);
209195
tracker!.onDidSendMessage!(stackTraceResponse);
210196
expect(gdbSession).toBeDefined();
211-
expect(result).toBeDefined();
197+
expect(result!).toBeDefined();
212198
expect(result!.session).toEqual(gdbSession);
213-
expect(result!.response).toEqual(stackTraceResponse);
199+
expect(result!.threadId).toEqual(stackTraceRequest.arguments.threadId);
200+
expect(result!.stackFrames).toEqual(stackTraceResponse.body!.stackFrames);
201+
expect(result!.totalFrames).toEqual(stackTraceResponse.body!.totalFrames);
214202
});
215203

216204
it('sends a session continued event', async () => {

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

Lines changed: 102 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,13 @@ export interface SessionEvent<T extends DebugProtocol.Event> {
2828
export type ContinuedEvent = SessionEvent<DebugProtocol.ContinuedEvent>;
2929
export type StoppedEvent = SessionEvent<DebugProtocol.StoppedEvent>;
3030

31-
export interface SessionRequest<T extends DebugProtocol.Request> {
31+
export interface SessionStackTrace {
3232
session: GDBTargetDebugSession;
33-
request: T;
33+
threadId: number;
34+
stackFrames: DebugProtocol.StackFrame[];
35+
totalFrames?: number|undefined;
3436
}
3537

36-
export interface SessionResponse<T extends DebugProtocol.Response> {
37-
session: GDBTargetDebugSession;
38-
response: T;
39-
}
40-
41-
export type StackTraceRequest = SessionRequest<DebugProtocol.StackTraceRequest>;
42-
export type StackTraceResponse = SessionResponse<DebugProtocol.StackTraceResponse>;
43-
4438
export type StackItem = vscode.DebugThread | vscode.DebugStackFrame | undefined;
4539

4640
export interface SessionStackItem {
@@ -50,6 +44,7 @@ export interface SessionStackItem {
5044

5145
export class GDBTargetDebugTracker {
5246
private sessions: Map<string, GDBTargetDebugSession> = new Map();
47+
private stackTraceRequests: Map<string, Map<number, number>> = new Map();
5348

5449
private readonly _onWillStartSession: vscode.EventEmitter<GDBTargetDebugSession> = new vscode.EventEmitter<GDBTargetDebugSession>();
5550
public readonly onWillStartSession: vscode.Event<GDBTargetDebugSession> = this._onWillStartSession.event;
@@ -72,28 +67,14 @@ export class GDBTargetDebugTracker {
7267
private readonly _onStopped: vscode.EventEmitter<StoppedEvent> = new vscode.EventEmitter<StoppedEvent>();
7368
public readonly onStopped: vscode.Event<StoppedEvent> = this._onStopped.event;
7469

75-
private readonly _onStackTraceRequest: vscode.EventEmitter<StackTraceRequest> = new vscode.EventEmitter<StackTraceRequest>();
76-
public readonly onStackTraceRequest: vscode.Event<StackTraceRequest> = this._onStackTraceRequest.event;
77-
78-
private readonly _onStackTraceResponse: vscode.EventEmitter<StackTraceResponse> = new vscode.EventEmitter<StackTraceResponse>();
79-
public readonly onStackTraceResponse: vscode.Event<StackTraceResponse> = this._onStackTraceResponse.event;
70+
private readonly _onStackTrace: vscode.EventEmitter<SessionStackTrace> = new vscode.EventEmitter<SessionStackTrace>();
71+
public readonly onStackTrace: vscode.Event<SessionStackTrace> = this._onStackTrace.event;
8072

8173
public activate(context: vscode.ExtensionContext) {
8274
const createDebugAdapterTracker = (session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> => {
8375
return {
84-
onWillStartSession: () => {
85-
const gdbTargetSession = new GDBTargetDebugSession(session);
86-
this.sessions.set(session.id, gdbTargetSession);
87-
this.bringConsoleToFront.apply(this);
88-
this._onWillStartSession.fire(gdbTargetSession);
89-
},
90-
onWillStopSession: () => {
91-
const gdbTargetSession = this.sessions.get(session.id);
92-
if (gdbTargetSession) {
93-
this.sessions.delete(session.id);
94-
this._onWillStopSession.fire(gdbTargetSession);
95-
}
96-
},
76+
onWillStartSession: () => this.handleOnWillStartSession(session),
77+
onWillStopSession: () => this.handleOnWillStopSession(session),
9778
onDidSendMessage: (message) => this.handleOnDidSendMessage(session, message),
9879
onWillReceiveMessage: (message) => this.handleOnWillReceiveMessage(session, message),
9980
};
@@ -107,39 +88,81 @@ export class GDBTargetDebugTracker {
10788
);
10889
};
10990

91+
private handleOnWillStartSession(session: vscode.DebugSession): void {
92+
const gdbTargetSession = new GDBTargetDebugSession(session);
93+
this.sessions.set(session.id, gdbTargetSession);
94+
this.bringConsoleToFront();
95+
this._onWillStartSession.fire(gdbTargetSession);
96+
}
97+
98+
private handleOnWillStopSession(session: vscode.DebugSession): void {
99+
const gdbTargetSession = this.sessions.get(session.id);
100+
if (gdbTargetSession) {
101+
this.sessions.delete(session.id);
102+
this._onWillStopSession.fire(gdbTargetSession);
103+
}
104+
this.stackTraceRequests.delete(session.id);
105+
}
106+
110107
protected handleOnDidSendMessage(session: vscode.DebugSession, message?: DebugProtocol.ProtocolMessage): void {
111108
if (!message) {
112109
return;
113110
}
114111
if (message.type === 'event') {
115-
const event = message as DebugProtocol.Event;
116-
const gdbTargetSession = this.sessions.get(session.id);
117-
switch (event.event) {
118-
case 'continued':
119-
this._onContinued.fire({ session: gdbTargetSession, event } as ContinuedEvent);
120-
break;
121-
case 'stopped':
122-
this._onStopped.fire({ session: gdbTargetSession, event } as StoppedEvent);
123-
break;
124-
}
112+
this.handleEvent(session, message as DebugProtocol.Event);
125113
} else if (message.type === 'response') {
126-
const response = message as DebugProtocol.Response;
127-
const gdbTargetSession = this.sessions.get(session.id);
128-
switch (response.command) {
129-
case 'stackTrace':
130-
if (!gdbTargetSession) {
131-
break;
132-
}
133-
this._onStackTraceResponse.fire({ session: gdbTargetSession, response } as StackTraceResponse);
134-
break;
135-
case 'launch':
136-
case 'attach':
137-
if (response.success && gdbTargetSession) {
138-
this._onConnected.fire(gdbTargetSession);
139-
}
140-
break;
141-
}
114+
this.handleResponse(session, message as DebugProtocol.Response);
115+
}
116+
}
117+
118+
private handleEvent(session: vscode.DebugSession, event: DebugProtocol.Event): void {
119+
const gdbTargetSession = this.sessions.get(session.id);
120+
switch (event.event) {
121+
case 'continued':
122+
this._onContinued.fire({ session: gdbTargetSession, event } as ContinuedEvent);
123+
break;
124+
case 'stopped':
125+
this._onStopped.fire({ session: gdbTargetSession, event } as StoppedEvent);
126+
break;
127+
}
128+
}
129+
130+
private handleResponse(session: vscode.DebugSession, response: DebugProtocol.Response): void {
131+
const gdbTargetSession = this.sessions.get(session.id);
132+
if (!gdbTargetSession) {
133+
return;
134+
}
135+
switch (response.command) {
136+
case 'launch':
137+
case 'attach':
138+
this.handleLaunchAttachResponse(gdbTargetSession, response);
139+
break;
140+
case 'stackTrace':
141+
this.handleStackTraceResponse(gdbTargetSession, response as DebugProtocol.StackTraceResponse);
142+
break;
143+
}
144+
}
145+
146+
private handleLaunchAttachResponse(gdbTargetSession: GDBTargetDebugSession, response: DebugProtocol.Response): void {
147+
if (response.success) {
148+
this._onConnected.fire(gdbTargetSession);
149+
}
150+
}
151+
152+
private handleStackTraceResponse(gdbTargetSession: GDBTargetDebugSession, response: DebugProtocol.StackTraceResponse): void {
153+
const stackTraceRequest = this.stackTraceRequests.get(gdbTargetSession.session.id);
154+
const threadId = stackTraceRequest?.get(response.request_seq);
155+
stackTraceRequest?.delete(response.request_seq);
156+
if (!response.success || threadId === undefined) {
157+
return;
142158
}
159+
const stackTrace = {
160+
session: gdbTargetSession,
161+
threadId,
162+
stackFrames: response.body.stackFrames,
163+
totalFrames: response.body.totalFrames
164+
};
165+
this._onStackTrace.fire(stackTrace);
143166
}
144167

145168
protected handleOnWillReceiveMessage(session: vscode.DebugSession, message?: DebugProtocol.ProtocolMessage): void {
@@ -149,30 +172,41 @@ export class GDBTargetDebugTracker {
149172
if (message.type === 'request') {
150173
const request = message as DebugProtocol.Request;
151174
const gdbTargetSession = this.sessions.get(session.id);
175+
if (!gdbTargetSession) {
176+
return;
177+
}
152178
switch (request.command) {
153179
case 'launch':
154180
case 'attach':
155-
{
156-
const gdbTargetConfig = request.arguments as GDBTargetConfiguration;
157-
const cbuildRunFile = gdbTargetConfig.cmsis?.cbuildRunFile;
158-
if (cbuildRunFile && gdbTargetSession) {
159-
// Do not wait for it to keep the message flowing.
160-
// Session class will do the waiting in case requests
161-
// come early.
162-
gdbTargetSession.parseCbuildRun(cbuildRunFile);
163-
}
164-
}
181+
this.handleLaunchAttachRequest(gdbTargetSession, request);
165182
break;
166183
case 'stackTrace':
167-
if (!gdbTargetSession) {
168-
break;
169-
}
170-
this._onStackTraceRequest.fire({ session: gdbTargetSession, request } as StackTraceRequest);
184+
this.handleStackTraceRequest(gdbTargetSession, request as DebugProtocol.StackTraceRequest);
171185
break;
172186
}
173187
}
174188
}
175189

190+
private handleLaunchAttachRequest(gdbTargetSession: GDBTargetDebugSession, request: DebugProtocol.Request): void {
191+
const gdbTargetConfig = request.arguments as GDBTargetConfiguration;
192+
const cbuildRunFile = gdbTargetConfig.cmsis?.cbuildRunFile;
193+
if (cbuildRunFile) {
194+
// Do not wait for it to keep the message flowing.
195+
// Session class will do the waiting in case requests
196+
// come early.
197+
gdbTargetSession.parseCbuildRun(cbuildRunFile);
198+
}
199+
}
200+
201+
private handleStackTraceRequest(gdbTargetSession: GDBTargetDebugSession, request: DebugProtocol.StackTraceRequest): void {
202+
let stackTraceRequests = this.stackTraceRequests.get(gdbTargetSession.session.id);
203+
if (stackTraceRequests === undefined) {
204+
stackTraceRequests = new Map();
205+
this.stackTraceRequests.set(gdbTargetSession.session.id, stackTraceRequests);
206+
}
207+
stackTraceRequests.set(request.seq, request.arguments.threadId);
208+
}
209+
176210
protected handleOnDidChangeActiveStackItem(item: StackItem): void {
177211
const gdbTargetSession = item?.session.id ? this.sessions.get(item?.session.id) : undefined;
178212
if (!gdbTargetSession) {

src/features/cpu-states/cpu-states.test.ts

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import * as vscode from 'vscode';
1818
import { debugSessionFactory } from '../../__test__/vscode.factory';
1919
import { GDBTargetConfiguration } from '../../debug-configuration';
20-
import { ContinuedEvent, GDBTargetDebugSession, GDBTargetDebugTracker, SessionStackItem, StackTraceRequest, StackTraceResponse, StoppedEvent } from '../../debug-session';
20+
import { ContinuedEvent, GDBTargetDebugSession, GDBTargetDebugTracker, SessionStackItem, SessionStackTrace, StoppedEvent } from '../../debug-session';
2121
import { CpuStates } from './cpu-states';
2222
import { DebugProtocol } from '@vscode/debugprotocol';
2323
import { waitForMs } from '../../utils';
@@ -45,18 +45,6 @@ describe('CpuStates', () => {
4545
} as unknown as DebugProtocol.StoppedEvent
4646
});
4747

48-
const createStackTraceRequest = (session: GDBTargetDebugSession, seq: number, threadId: number): StackTraceRequest => ({
49-
session,
50-
request: {
51-
command: 'stackTrace',
52-
seq,
53-
type: 'request',
54-
arguments: {
55-
threadId
56-
}
57-
}
58-
});
59-
6048
const createStackFrame = (): DebugProtocol.StackFrame => ({
6149
column: 0,
6250
id: 1,
@@ -68,21 +56,13 @@ describe('CpuStates', () => {
6856
}
6957
});
7058

71-
const createStackTraceResponse = (session: GDBTargetDebugSession, seq: number, request_seq: number): StackTraceResponse => ({
59+
const createSessionStackTrace = (session: GDBTargetDebugSession, threadId: number): SessionStackTrace => ({
7260
session,
73-
response: {
74-
command: 'stackTrace',
75-
type: 'response',
76-
seq,
77-
request_seq,
78-
success: true,
79-
body: {
80-
totalFrames: 1,
81-
stackFrames: [
82-
createStackFrame()
83-
]
84-
}
85-
}
61+
threadId,
62+
stackFrames: [
63+
createStackFrame()
64+
],
65+
totalFrames: 1
8666
});
8767

8868
let debugConfig: GDBTargetConfiguration;
@@ -347,35 +327,12 @@ describe('CpuStates', () => {
347327
(tracker as any)._onStopped.fire(createStoppedEvent(gdbtargetDebugSession, 'step', 1 /*threadId*/));
348328
await waitForMs(0);
349329
// eslint-disable-next-line @typescript-eslint/no-explicit-any
350-
(tracker as any)._onStackTraceRequest.fire(createStackTraceRequest(gdbtargetDebugSession, 1, 1));
351-
await waitForMs(0);
352-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
353-
(tracker as any)._onStackTraceResponse.fire(createStackTraceResponse(gdbtargetDebugSession, 4, 1));
330+
(tracker as any)._onStackTrace.fire(createSessionStackTrace(gdbtargetDebugSession, 1));
354331
await waitForMs(0);
355332
cpuStates.showStatesHistory();
356333
expect(debugConsoleOutput.find(line => line.includes('(PC=0x08000396 <myframe>, myfunction::2)'))).toBeDefined();
357334
});
358335

359-
it('does not assign frame location to captured stop point due to threadId mismatch', async () => {
360-
const debugConsoleOutput: string[] = [];
361-
(vscode.debug.activeDebugConsole.appendLine as jest.Mock).mockImplementation(line => debugConsoleOutput.push(line));
362-
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({
363-
address: '0xE0001004',
364-
data: new Uint8Array([ 10, 0x00, 0x00, 0x00 ]).buffer
365-
});
366-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
367-
(tracker as any)._onStopped.fire(createStoppedEvent(gdbtargetDebugSession, 'step', 2 /*threadId*/));
368-
await waitForMs(0);
369-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
370-
(tracker as any)._onStackTraceRequest.fire(createStackTraceRequest(gdbtargetDebugSession, 1, 1));
371-
await waitForMs(0);
372-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
373-
(tracker as any)._onStackTraceResponse.fire(createStackTraceResponse(gdbtargetDebugSession, 4, 1));
374-
await waitForMs(0);
375-
cpuStates.showStatesHistory();
376-
expect(debugConsoleOutput.find(line => line.includes('(PC=0x08000396 <myframe>::2)'))).toBeUndefined();
377-
});
378-
379336
});
380337

381338
describe('tests with established connection and CPU states not supported', () => {

0 commit comments

Comments
 (0)