diff --git a/src/gdb/GDBDebugSessionBase.ts b/src/gdb/GDBDebugSessionBase.ts index d4818349..f2f99849 100644 --- a/src/gdb/GDBDebugSessionBase.ts +++ b/src/gdb/GDBDebugSessionBase.ts @@ -52,6 +52,7 @@ import { DEFAULT_STEPPING_RESPONSE_TIMEOUT } from '../constants/session'; import { ThreadWithStatus } from './common'; import { RESUME_COMMANDS, SET_ALL_CHARSET_REGEXPS } from '../constants/gdb'; import { GDBThreadRunning } from './errors'; +import { MIGDBDataEvaluateExpressionResponse } from '../mi'; /** * Keeps track of where in the configuration phase (between initialized event @@ -157,6 +158,8 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { protected isRunning = false; protected supportsRunInTerminalRequest = false; + protected supportsMemoryReferences = false; + protected supportsMemoryEvent = false; public supportsGdbConsole = false; /* A reference to the logger to be used by subclasses */ @@ -419,6 +422,8 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { ): void { this.supportsRunInTerminalRequest = args.supportsRunInTerminalRequest === true; + this.supportsMemoryReferences = args.supportsMemoryReferences === true; + this.supportsMemoryEvent = args.supportsMemoryEvent === true; this.supportsGdbConsole = os.platform() === 'linux' && this.supportsRunInTerminalRequest; response.body = response.body || {}; @@ -2674,10 +2679,35 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { varobjName: varobj.varname, }) : 0; + let memoryReferenceResult: + | MIGDBDataEvaluateExpressionResponse + | undefined; + let hasMemoryReference = false; + if (this.supportsMemoryReferences) { + try { + memoryReferenceResult = + await mi.sendDataEvaluateExpression( + gdb, + `&(${varobj.expression})` + ); + // Depending on the GDBServer being used, sometimes the result of evaluating an address of a symbol returns "
" + if (memoryReferenceResult.value?.includes(' ')) { + memoryReferenceResult.value = + memoryReferenceResult.value.split(' ')[0]; + } + hasMemoryReference = true; + } catch { + hasMemoryReference = false; + } + } response.body = { result, type: varobj.type, variablesReference, + memoryReference: + hasMemoryReference && memoryReferenceResult + ? memoryReferenceResult.value + : undefined, }; } @@ -3311,6 +3341,9 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession { this.handleCmdParamChanged(notifyData); break; case 'memory-changed': { + if (!this.supportsMemoryEvent) { + break; + } let length = 0; if (isNaN(notifyData.len)) { logger.warn( diff --git a/src/integration-tests/breakpoints.spec.ts b/src/integration-tests/breakpoints.spec.ts index b36c457a..fd9e6864 100644 --- a/src/integration-tests/breakpoints.spec.ts +++ b/src/integration-tests/breakpoints.spec.ts @@ -444,6 +444,7 @@ describe('breakpoints', async function () { // win32 host can only pause remote + mi-async targets this.skip(); } + const firstStoppedEvent = dc.waitForEvent('stopped'); let response = await dc.setBreakpointsRequest({ source: { name: 'count.c', @@ -458,7 +459,7 @@ describe('breakpoints', async function () { }); expect(response.body.breakpoints.length).to.eq(1); await dc.configurationDoneRequest(); - await dc.waitForEvent('stopped'); + await firstStoppedEvent; const scope = await getScopes(dc); await dc.continueRequest({ threadId: scope.thread.id }); diff --git a/src/integration-tests/debugClient.ts b/src/integration-tests/debugClient.ts index 5e9937e1..99e79de2 100644 --- a/src/integration-tests/debugClient.ts +++ b/src/integration-tests/debugClient.ts @@ -141,6 +141,8 @@ export class CdtDebugClient extends DebugClient { if (!args) { args = { supportsRunInTerminalRequest: true, + supportsMemoryEvent: true, + supportsMemoryReferences: true, adapterID: this['_debugType'], linesStartAt1: true, columnsStartAt1: true, diff --git a/src/integration-tests/evaluate.spec.ts b/src/integration-tests/evaluate.spec.ts index c5d47bbb..097d6ade 100644 --- a/src/integration-tests/evaluate.spec.ts +++ b/src/integration-tests/evaluate.spec.ts @@ -531,6 +531,26 @@ describe('evaluate request global variables', function () { }); expect(evalRes.body.result).to.equal('25'); }); + + it('response for variables should include memory reference if the expression has an lvalue', async function () { + // Read a global variable using evaluateRequest + const watchRes = await dc.evaluateRequest({ + context: 'watch', + expression: 'global_int', + frameId: scope.frame.id, + }); + expect(watchRes.body.memoryReference).to.not.be.undefined; + }); + + it('should not include memory reference if the expression does not have an lvalue', async function () { + const watchRes = await dc.evaluateRequest({ + context: 'watch', + expression: 'global_int + 1', + frameId: scope.frame.id, + }); + + expect(watchRes.body.memoryReference).to.be.undefined; + }); }); describe('evaluate request - watch local variable across lexical scope transition', function () {