Skip to content

Commit 098fec0

Browse files
committed
Improvement for step functions latencies
The step functions were providing poor UX in slow debugging sessions. The debug buttons were unresponsive for longer times cause users to repeatedly click on stepping buttons which leads to multiple unintended step operations. In this implementation, a timeouted response is returned to the user interface to change the UI state properly. For this purpose, we implemented: - A new utility function called `sendResponseWithTimeout` to handle the operation. - Improved the `nextRequest`, `stepInRequest` and `stepOutRequest` methods to introduce the timeouted response, with a default of 100ms timeout. - Introduce a new launch variable called `steppingResponseTimeout` to provide an option that user can change the default timeout for stepping functions.
1 parent 21bf966 commit 098fec0

File tree

8 files changed

+893
-40
lines changed

8 files changed

+893
-40
lines changed

src/constants/session.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*********************************************************************
2+
* Copyright (c) 2025 Renesas Electronics Corporation and others
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*********************************************************************/
10+
11+
/**
12+
* The default stepping response timeout.
13+
* Used if it is not defined in the launch configuration.
14+
*/
15+
export const DEFAULT_STEPPING_RESPONSE_TIMEOUT = 100;

src/desktop/GDBTargetDebugSession.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class GDBTargetDebugSession extends GDBDebugSession {
164164
args: TargetLaunchRequestArguments | TargetAttachRequestArguments
165165
) {
166166
await this.setupCommonLoggerAndBackends(args);
167-
this.initializeCustomResetCommands(args);
167+
this.initializeSessionArguments(args);
168168

169169
if (request === 'launch') {
170170
const launchArgs = args as TargetLaunchRequestArguments;

src/gdb/GDBDebugSessionBase.ts

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import { IGDBBackend, IGDBBackendFactory } from '../types/gdb';
4343
import { getInstructions } from '../util/disassembly';
4444
import { calculateMemoryOffset } from '../util/calculateMemoryOffset';
4545
import { isWindowsPath } from '../util/isWindowsPath';
46+
import { sendResponseWithTimeout } from '../util/sendResponseWithTimeout';
47+
import { DEFAULT_STEPPING_RESPONSE_TIMEOUT } from '../constants/session';
4648

4749
class ThreadWithStatus implements DebugProtocol.Thread {
4850
id: number;
@@ -166,6 +168,12 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
166168
*/
167169
protected customResetCommands?: string[];
168170

171+
/**
172+
* steppingResponseTimeout from launch.json
173+
*/
174+
protected steppingResponseTimeout: number =
175+
DEFAULT_STEPPING_RESPONSE_TIMEOUT;
176+
169177
constructor(protected readonly backendFactory: IGDBBackendFactory) {
170178
super();
171179
this.logger = logger;
@@ -242,10 +250,12 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
242250
* Apply the initial custom reset arguments.
243251
* @param args the arguments from the user to apply custom reset arguments to.
244252
*/
245-
protected initializeCustomResetCommands(
253+
protected initializeSessionArguments(
246254
args: LaunchRequestArguments | AttachRequestArguments
247255
) {
248256
this.customResetCommands = args.customResetCommands;
257+
this.steppingResponseTimeout =
258+
args.steppingResponseTimeout ?? DEFAULT_STEPPING_RESPONSE_TIMEOUT;
249259
}
250260

251261
/**
@@ -1151,55 +1161,88 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
11511161
response: DebugProtocol.NextResponse,
11521162
args: DebugProtocol.NextArguments
11531163
): Promise<void> {
1154-
try {
1155-
await (args.granularity === 'instruction'
1156-
? mi.sendExecNextInstruction(this.gdb, args.threadId)
1157-
: mi.sendExecNext(this.gdb, args.threadId));
1158-
this.sendResponse(response);
1159-
} catch (err) {
1160-
this.sendErrorResponse(
1161-
response,
1162-
1,
1163-
err instanceof Error ? err.message : String(err)
1164-
);
1165-
}
1164+
return sendResponseWithTimeout({
1165+
execute: async () => {
1166+
await (args.granularity === 'instruction'
1167+
? mi.sendExecNextInstruction(this.gdb, args.threadId)
1168+
: mi.sendExecNext(this.gdb, args.threadId));
1169+
},
1170+
onResponse: () => {
1171+
this.sendResponse(response);
1172+
},
1173+
onError: (err) => {
1174+
const errorMessage =
1175+
err instanceof Error ? err.message : String(err);
1176+
1177+
this.sendEvent(
1178+
new OutputEvent(
1179+
`Error occurred during the nextRequest: ${errorMessage}\n`,
1180+
'console'
1181+
)
1182+
);
1183+
this.sendErrorResponse(response, 1, errorMessage);
1184+
},
1185+
timeout: this.steppingResponseTimeout,
1186+
});
11661187
}
11671188

11681189
protected async stepInRequest(
11691190
response: DebugProtocol.StepInResponse,
11701191
args: DebugProtocol.StepInArguments
11711192
): Promise<void> {
1172-
try {
1173-
await (args.granularity === 'instruction'
1174-
? mi.sendExecStepInstruction(this.gdb, args.threadId)
1175-
: mi.sendExecStep(this.gdb, args.threadId));
1176-
this.sendResponse(response);
1177-
} catch (err) {
1178-
this.sendErrorResponse(
1179-
response,
1180-
1,
1181-
err instanceof Error ? err.message : String(err)
1182-
);
1183-
}
1193+
return sendResponseWithTimeout({
1194+
execute: async () => {
1195+
await (args.granularity === 'instruction'
1196+
? mi.sendExecStepInstruction(this.gdb, args.threadId)
1197+
: mi.sendExecStep(this.gdb, args.threadId));
1198+
},
1199+
onResponse: () => {
1200+
this.sendResponse(response);
1201+
},
1202+
onError: (err) => {
1203+
const errorMessage =
1204+
err instanceof Error ? err.message : String(err);
1205+
1206+
this.sendEvent(
1207+
new OutputEvent(
1208+
`Error occurred during the stepInRequest: ${errorMessage}\n`,
1209+
'console'
1210+
)
1211+
);
1212+
this.sendErrorResponse(response, 1, errorMessage);
1213+
},
1214+
timeout: this.steppingResponseTimeout,
1215+
});
11841216
}
11851217

11861218
protected async stepOutRequest(
11871219
response: DebugProtocol.StepOutResponse,
11881220
args: DebugProtocol.StepOutArguments
11891221
): Promise<void> {
1190-
try {
1191-
await mi.sendExecFinish(this.gdb, {
1192-
threadId: args.threadId,
1193-
frameId: 0,
1194-
});
1195-
this.sendResponse(response);
1196-
} catch (err) {
1197-
this.sendErrorResponse(
1198-
response,
1199-
1,
1200-
err instanceof Error ? err.message : String(err)
1201-
);
1202-
}
1222+
return sendResponseWithTimeout({
1223+
execute: async () => {
1224+
await mi.sendExecFinish(this.gdb, {
1225+
threadId: args.threadId,
1226+
frameId: 0,
1227+
});
1228+
},
1229+
onResponse: () => {
1230+
this.sendResponse(response);
1231+
},
1232+
onError: (err) => {
1233+
const errorMessage =
1234+
err instanceof Error ? err.message : String(err);
1235+
1236+
this.sendEvent(
1237+
new OutputEvent(
1238+
`Error occurred during the stepOutRequest: ${errorMessage}\n`,
1239+
'console'
1240+
)
1241+
);
1242+
this.sendErrorResponse(response, 1, errorMessage);
1243+
},
1244+
timeout: this.steppingResponseTimeout,
1245+
});
12031246
}
12041247

12051248
protected async continueRequest(

0 commit comments

Comments
 (0)