Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## Unreleased

- Fixes [`400`](https://github.com/eclipse-cdt-cloud/cdt-gdb-adapter/issues/400): Evaluation of variables to support RTOS Views extension.

## 1.4.0

- Implements [`#442`](https://github.com/eclipse-cdt-cloud/cdt-gdb-adapter/issues/442): Support auxiliary GDB connections to allow selected operations while CPU running.
Expand Down
14 changes: 5 additions & 9 deletions src/gdb/GDBDebugSessionBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2794,21 +2794,17 @@ export abstract class GDBDebugSessionBase extends LoggingDebugSession {
let name = `${ref.varobjName}.${child.exp}`;
const varobjName = name;
const value = child.value ? child.value : child.type;
const isArrayParent = arrayRegex.test(child.type);
const isArrayChild =
varobj !== undefined
? arrayRegex.test(varobj.type) &&
arrayChildRegex.test(child.exp)
: false;
arrayChildRegex.test(child.exp) &&
(!varobj || arrayRegex.test(varobj.type));
if (isArrayChild) {
// update the display name for array elements to have square brackets
name = `[${child.exp}]`;
}
const variableName = isArrayChild ? name : child.exp;
const evaluateName =
isArrayParent || isArrayChild
? `${topLevelPathExpression}[${child.exp}]`
: `${topLevelPathExpression}.${child.exp}`;
const evaluateName = isArrayChild
? `${topLevelPathExpression}[${child.exp}]`
: `${topLevelPathExpression}.${child.exp}`;
variables.push({
name: variableName,
evaluateName,
Expand Down
154 changes: 154 additions & 0 deletions src/integration-tests/evaluate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
fillDefaults,
getScopes,
isRemoteTest,
resolveLineTagLocations,
Scope,
standardBeforeEach,
testProgramsDir,
Expand Down Expand Up @@ -179,3 +180,156 @@ describe('evaluate request', function () {
expect(err.message).eq('Undefined MI command: a');
});
});

describe('evaluate request global variables', function () {
let dc: CdtDebugClient;
let scope: Scope;

const varsGlobalsProgram = path.join(testProgramsDir, 'vars_globals');
const varsGlobalsSrc = path.join(testProgramsDir, 'vars_globals.c');
const lineTags = {
INITIAL_STOP: 0,
};

before(function () {
resolveLineTagLocations(varsGlobalsSrc, lineTags);
});

beforeEach(async function () {
dc = await standardBeforeEach();
await dc.launchRequest(
fillDefaults(this.currentTest, {
program: varsGlobalsProgram,
})
);
await dc.setBreakpointsRequest({
source: { path: varsGlobalsSrc },
breakpoints: [{ line: lineTags['INITIAL_STOP'] }],
});
await Promise.all([
dc.waitForEvent('stopped'),
dc.configurationDoneRequest(),
]);
scope = await getScopes(dc);
});

afterEach(async function () {
await dc.stop();
});

it('evaluates a global struct variable and creates sensible evaluate names for members', async function () {
const arrayContent = 'char_array';
const arrayLength = arrayContent.length + 1; // +1 for '\0'
const resolvedExpression = await dc.evaluateRequest({
context: 'hover',
expression: 's0',
frameId: scope.frame.id,
});

expect(resolvedExpression.body.result).to.equal('{...}');

const children = await dc.variablesRequest({
variablesReference: resolvedExpression.body.variablesReference,
});
expect(children.body.variables).lengthOf(3);
const childrenContents = [
{ name: 'a', hasChildren: false },
{ name: 'b', hasChildren: false },
{ name: 'char_array', hasChildren: true },
];
children.body.variables.forEach((variable, index) => {
expect(variable.name).to.equal(childrenContents[index].name);
expect(variable.evaluateName).to.equal(
`s0.${childrenContents[index].name}`
);
if (childrenContents[index].hasChildren) {
expect(variable.variablesReference).not.to.equal(0);
} else {
expect(variable.variablesReference).to.equal(0);
}
});

const arrayChildren = await dc.variablesRequest({
variablesReference: children.body.variables[2].variablesReference,
});
expect(arrayChildren.body.variables).lengthOf(arrayLength);
arrayChildren.body.variables.forEach((variable, index) => {
expect(variable.name).to.equal(`[${index}]`);
expect(variable.evaluateName).to.equal(`s0.char_array[${index}]`);
expect(variable.variablesReference).to.equal(0);
const charCode =
index === arrayLength - 1 ? 0 : arrayContent.charCodeAt(index);
const charValue = `${charCode} '${charCode === 0 ? '\\000' : String.fromCharCode(charCode)}'`;
expect(variable.value).to.equal(charValue);
});
});

it('evaluates a pointer to a more complex struct variable and creates sensible evaluate names for members', async function () {
const resolvedExpression = await dc.evaluateRequest({
context: 'hover',
expression: 's1',
frameId: scope.frame.id,
});

expect(resolvedExpression.body.result).to.endWith('{...}');

const members = await dc.variablesRequest({
variablesReference: resolvedExpression.body.variablesReference,
});
expect(members.body.variables).lengthOf(4);
const memberContents = [
{ name: 'm', hasChildren: false },
{ name: 'n', hasChildren: false },
{ name: 'child', hasChildren: true },
{ name: 'children', hasChildren: true },
];
members.body.variables.forEach((variable, index) => {
expect(variable.name).to.equal(memberContents[index].name);
expect(variable.evaluateName).to.equal(
`s1.${memberContents[index].name}`
);
if (memberContents[index].hasChildren) {
expect(variable.variablesReference).not.to.equal(0);
} else {
expect(variable.variablesReference).to.equal(0);
}
});

// Child
const child = await dc.variablesRequest({
variablesReference: members.body.variables[2].variablesReference,
});
expect(child.body.variables).lengthOf(2);
child.body.variables.forEach((variable, index) => {
const childName = index === 0 ? 'x' : 'y';
expect(variable.name).to.equal(childName);
expect(variable.evaluateName).to.equal(`s1.child.${childName}`);
expect(variable.variablesReference).to.equal(0);
});

// Children
const children = await dc.variablesRequest({
variablesReference: members.body.variables[3].variablesReference,
});
expect(children.body.variables).lengthOf(2);
children.body.variables.forEach(async (variable, index) => {
expect(variable.name).to.equal(`[${index}]`);
expect(variable.value).to.equal(`{...}`);
expect(variable.evaluateName).to.equal(`s1.children[${index}]`);
expect(variable.variablesReference).not.to.equal(0);
// Grand children
const grandChildren = await dc.variablesRequest({
variablesReference: variable.variablesReference,
});
expect(grandChildren.body.variables).lengthOf(2);
grandChildren.body.variables.forEach((gcVariable, gcIndex) => {
const childName = gcIndex === 0 ? 'x' : 'y';
expect(gcVariable.name).to.equal(childName);
expect(gcVariable.evaluateName).to.equal(
`s1.children[${index}].${childName}`
);
expect(gcVariable.variablesReference).to.equal(0);
});
});
});
});
5 changes: 4 additions & 1 deletion src/integration-tests/test-programs/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BINS = empty empty\ space evaluate vars vars_cpp vars_env mem segv count disassemble functions loopforever MultiThread MultiThreadRunControl stderr bug275-测试 cwd.exe stepping
BINS = empty empty\ space evaluate vars vars_cpp vars_env vars_globals mem segv count disassemble functions loopforever MultiThread MultiThreadRunControl stderr bug275-测试 cwd.exe stepping

.PHONY: all
all: $(BINS)
Expand Down Expand Up @@ -71,6 +71,9 @@ vars_env: vars_env.o
vars_cpp: vars_cpp.o
$(LINK_CXX)

vars_globals: vars_globals.o
$(LINK)

segv: segv.o
$(LINK)

Expand Down
60 changes: 60 additions & 0 deletions src/integration-tests/test-programs/vars_globals.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*** Data Types for testing ***/
typedef struct structWithArray {
int a;
int b;
char char_array[sizeof("char_array")];
} STRUCT_WITH_ARRAY;

typedef struct childStruct {
int x;
int y;
} CHILD_STRUCT;

typedef struct parentStruct {
int m;
float n;
CHILD_STRUCT child;
CHILD_STRUCT children[2];
} PARENT_STRUCT;

/*** Global variables for testing, volatile to avoid optimizing them out ***/

volatile STRUCT_WITH_ARRAY s0 = {
1,
2,
"char_array"
};

volatile STRUCT_WITH_ARRAY *p_s0 = &s0;

volatile PARENT_STRUCT s1 = {
10,
3.14f,
{ 4, 5 },
{ { 6, 7 }, { 8, 9 } }
};

volatile PARENT_STRUCT *p_s1 = &s1;

int main()
{
// Struct with array
volatile STRUCT_WITH_ARRAY *p_s0_local = &s0;
unsigned long long s0_address = (unsigned long long)&s0;
s0.a *= 10; // INITIAL_STOP
s0.b *= 2;
p_s0_local->a += 12;
p_s0_local->b--;
// Parent-child struct
volatile PARENT_STRUCT *p_s1_local = &s1;
unsigned long long s1_address = (unsigned long long)&s1;
s1.m += 5;
s1.n *= 2.0f;
s1.child.x += 10;
s1.child.y += 20;
s1.children[0].x += 30;
s1.children[0].y += 40;
s1.children[1].x += 50;
s1.children[1].y += 60;
return 0; // RETURN
}