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 () {