From f532493bda1919a0969551f368d611cc6e38eef4 Mon Sep 17 00:00:00 2001 From: Asim Gunes Date: Thu, 26 Sep 2024 14:21:06 +0100 Subject: [PATCH] Handling the ContinuedEvent This update implements handling mechanism for "resume" gdb signal and redirect the "resume" signal as "ContinuedEvent" as described in the Debug Adapter Protocol --- src/events/continuedEvent.ts | 30 ++++++++ src/{ => events}/stoppedEvent.ts | 0 src/gdb/GDBDebugSessionBase.ts | 42 +++++++++- src/integration-tests/continues.spec.ts | 34 ++++++++- src/integration-tests/multithread.spec.ts | 93 ++++++++++++++++++++++- 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 src/events/continuedEvent.ts rename src/{ => events}/stoppedEvent.ts (100%) diff --git a/src/events/continuedEvent.ts b/src/events/continuedEvent.ts new file mode 100644 index 00000000..414f122a --- /dev/null +++ b/src/events/continuedEvent.ts @@ -0,0 +1,30 @@ +/********************************************************************* + * Copyright (c) 2024 Renesas Electronics Corporation and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *********************************************************************/ +import { Event } from '@vscode/debugadapter'; +import { DebugProtocol } from '@vscode/debugprotocol'; + +export class ContinuedEvent + extends Event + implements DebugProtocol.ContinuedEvent +{ + public body: { + threadId: number; + allThreadsContinued?: boolean; + }; + + constructor(threadId: number, allThreadsContinued = false) { + super('continued'); + + this.body = { + threadId, + allThreadsContinued, + }; + } +} diff --git a/src/stoppedEvent.ts b/src/events/stoppedEvent.ts similarity index 100% rename from src/stoppedEvent.ts rename to src/events/stoppedEvent.ts diff --git a/src/gdb/GDBDebugSessionBase.ts b/src/gdb/GDBDebugSessionBase.ts index 676d7101..868c1157 100644 --- a/src/gdb/GDBDebugSessionBase.ts +++ b/src/gdb/GDBDebugSessionBase.ts @@ -23,7 +23,8 @@ import { TerminatedEvent, } from '@vscode/debugadapter'; import { DebugProtocol } from '@vscode/debugprotocol'; -import { StoppedEvent } from '../stoppedEvent'; +import { ContinuedEvent } from '../events/continuedEvent'; +import { StoppedEvent } from '../events/stoppedEvent'; import { VarObjType } from '../varManager'; import { FrameReference, @@ -1601,6 +1602,37 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { } } + protected sendContinuedEvent( + threadId: number, + allThreadsContinued?: boolean + ) { + // Reset frame handles and variables for new context + this.frameHandles.reset(); + this.variableHandles.reset(); + // Send the event + this.sendEvent(new ContinuedEvent(threadId, allThreadsContinued)); + } + + protected handleGDBResume(result: any) { + const getThreadId = (resultData: any) => { + return parseInt(resultData['thread-id'], 10); + }; + const getAllThreadsContinued = (resultData: any) => { + return ( + !!resultData['thread-id'] && resultData['thread-id'] === 'all' + ); + }; + + const isAllThreadsContinued = getAllThreadsContinued(result); + if (isAllThreadsContinued) { + // If all threads continued, then the value of the 'thread-id' is 'all', + // hence, there isn't a thread id number. We are sending the thread id as '1' + // along with the allThreadsContinued=true information. + return this.sendContinuedEvent(1, true); + } + return this.sendContinuedEvent(getThreadId(result)); + } + protected handleGDBAsync(resultClass: string, resultData: any) { const updateIsRunning = () => { this.isRunning = this.threads.length ? true : false; @@ -1613,9 +1645,10 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { switch (resultClass) { case 'running': if (this.gdb.isNonStopMode()) { - const id = parseInt(resultData['thread-id'], 10); + const rawId = resultData['thread-id']; + const id = parseInt(rawId, 10); for (const thread of this.threads) { - if (thread.id === id) { + if (thread.id === id || rawId === 'all') { thread.running = true; } } @@ -1625,6 +1658,9 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { } } updateIsRunning(); + if (this.isInitialized) { + this.handleGDBResume(resultData); + } break; case 'stopped': { let suppressHandleGDBStopped = false; diff --git a/src/integration-tests/continues.spec.ts b/src/integration-tests/continues.spec.ts index dcbc7c94..7fad87a5 100644 --- a/src/integration-tests/continues.spec.ts +++ b/src/integration-tests/continues.spec.ts @@ -15,7 +15,7 @@ import { getScopes, gdbNonStop, } from './utils'; -import { expect } from 'chai'; +import { expect, assert } from 'chai'; import * as path from 'path'; describe('continues', async function () { @@ -55,4 +55,36 @@ describe('continues', async function () { }); expect(continueResponse.body.allThreadsContinued).to.eq(!gdbNonStop); }); + + it('handles async continued events', async function () { + await dc.setBreakpointsRequest({ + source: { + name: 'count.c', + path: path.join(testProgramsDir, 'count.c'), + }, + breakpoints: [ + { + column: 1, + line: 4, + }, + ], + }); + await dc.configurationDoneRequest(); + await dc.waitForEvent('stopped'); + const scope = await getScopes(dc); + dc.continueRequest({ + threadId: scope.thread.id, + }); + const event = await dc.waitForEvent('continued'); + + assert.deepEqual( + event.body, + gdbNonStop + ? { + threadId: 1, + allThreadsContinued: false, + } + : { threadId: 1, allThreadsContinued: true } + ); + }); }); diff --git a/src/integration-tests/multithread.spec.ts b/src/integration-tests/multithread.spec.ts index b9c5cf63..2b6938eb 100644 --- a/src/integration-tests/multithread.spec.ts +++ b/src/integration-tests/multithread.spec.ts @@ -16,7 +16,7 @@ import { gdbNonStop, fillDefaults, } from './utils'; -import { expect } from 'chai'; +import { assert, expect } from 'chai'; import * as path from 'path'; import { fail } from 'assert'; import * as os from 'os'; @@ -36,6 +36,7 @@ describe('multithread', async function () { const lineTags = { LINE_MAIN_ALL_THREADS_STARTED: 0, + LINE_THREAD_IN_HELLO: 0, }; before(function () { @@ -182,4 +183,94 @@ describe('multithread', async function () { } } }); + + it.only('async resume for gdb-non-stop off', async function () { + if (gdbNonStop) { + // This test is covering only gdb-non-stop off + this.skip(); + } + + await dc.launchRequest( + fillDefaults(this.test, { + program, + }) + ); + await dc.setBreakpointsRequest({ + source: { + path: source, + }, + breakpoints: [ + { + line: lineTags['LINE_MAIN_ALL_THREADS_STARTED'], + }, + { + line: lineTags['LINE_THREAD_IN_HELLO'], + }, + ], + }); + + + await dc.configurationDoneRequest(); + await dc.waitForEvent('stopped'); + + const threads = await dc.threadsRequest(); + + // make sure that there is at least 6 threads. + expect(threads.body.threads).length.greaterThanOrEqual(6); + + // Any thread receive a continue + dc.continueRequest({ threadId: 3 }); + + const event = await dc.waitForEvent('continued'); + + assert.deepEqual(event.body, { + threadId: 1, + allThreadsContinued: true, + }); + }); + + it.only('async resume for gdb-non-stop on', async function () { + if (!gdbNonStop) { + // This test is covering only gdb-non-stop on + this.skip(); + } + + await dc.hitBreakpoint( + fillDefaults(this.test, { + program: program, + }), + { + path: source, + line: lineTags['LINE_MAIN_ALL_THREADS_STARTED'], + } + ); + + const threads = await dc.threadsRequest(); + + // make sure that there is at least 6 threads. + expect(threads.body.threads).length.greaterThanOrEqual(6); + + // stop the running threads + const runningThreads = threads.body.threads.filter( + (t) => (t as unknown as { running?: boolean }).running + ); + for (const thread of runningThreads) { + await dc.pauseRequest({ threadId: thread.id }); + await dc.waitForEvent('stopped'); + } + + for (const thread of threads.body.threads) { + // Send an async continue request and wait for the continue event. + dc.continueRequest({ threadId: thread.id }); + const event = await dc.waitForEvent('continued'); + + assert.deepEqual( + event.body, + { + threadId: thread.id, + allThreadsContinued: false, + } + ); + } + }); });