Skip to content
Open
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
35 changes: 17 additions & 18 deletions src/GDBDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2088,10 +2088,15 @@ export class GDBDebugSession extends LoggingDebugSession {
});
}
// Grab the full path of parent.
const topLevelPathExpression =
const tempTopLevelPathExpression =
varobj?.expression ??
(await this.getFullPathExpression(parentVarname));

// In the case of Expressions, there might be casting, so add outer ()
const topLevelPathExpression = varobj?.isVar
? tempTopLevelPathExpression
: `(${tempTopLevelPathExpression})`;

// iterate through the children
for (const child of children.children) {
// check if we're dealing with a C++ object. If we are, we need to fetch the grandchildren instead.
Expand Down Expand Up @@ -2123,25 +2128,24 @@ export class GDBDebugSession extends LoggingDebugSession {
}
} else {
// check if we're dealing with an array
let name = `${ref.varobjName}.${child.exp}`;
let varobjName = name;
let variableName = child.exp;
let varobjName = `${ref.varobjName}.${child.exp}`;
let fullPath = `${topLevelPathExpression}.${variableName}`;
let 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;
if (isArrayChild) {
// update the display name for array elements to have square brackets
name = `[${child.exp}]`;
}
if (isArrayParent || isArrayChild) {
// can't use a relative varname (eg. var1.a.b.c) to create/update a new var so fetch and track these
// vars by evaluating their path expression from GDB
const fullPath = await this.getFullPathExpression(
child.name
);
if (isArrayChild) {
// update the display name for array elements to have square brackets
variableName = `[${child.exp}]`;
fullPath = `(${topLevelPathExpression})${variableName}`;
}
// create or update the var in GDB
let arrobj = this.gdb.varManager.getVar(
frame.frameId,
Expand Down Expand Up @@ -2182,14 +2186,9 @@ export class GDBDebugSession extends LoggingDebugSession {
arrobj.isChild = true;
varobjName = arrobj.varname;
}
const variableName = isArrayChild ? name : child.exp;
const evaluateName =
isArrayParent || isArrayChild
? await this.getFullPathExpression(child.name)
: `${topLevelPathExpression}.${child.exp}`;
variables.push({
name: variableName,
evaluateName,
evaluateName: fullPath,
value,
type: child.type,
variablesReference:
Expand All @@ -2212,8 +2211,8 @@ export class GDBDebugSession extends LoggingDebugSession {
this.gdb,
inputVarName
);
// result from GDB looks like (parentName).field so remove ().
return exprResponse.path_expr.replace(/[()]/g, '');
// GDB result => (parentName).field
return exprResponse.path_expr;
}

// Register view
Expand Down
1 change: 1 addition & 0 deletions src/integration-tests/test-programs/vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ int main()
int rax = 1;
const unsigned char h[] = {0x01, 0x10, 0x20};
const unsigned char k[] = "hello"; // char string setup
r.z.a = 10; // update embedded int
return 0;
}
115 changes: 115 additions & 0 deletions src/integration-tests/var.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('Variables Test Suite', function () {
'STOP HERE': 0,
'After array init': 0,
'char string setup': 0,
'update embedded int': 0,
};

const hexValueRegex = /^0x[\da-fA-F]+$/;
Expand Down Expand Up @@ -555,4 +556,118 @@ describe('Variables Test Suite', function () {
'[\n "104 \'h\'",\n "101 \'e\'",\n "108 \'l\'",\n "108 \'l\'",\n "111 \'o\'",\n "0 \'\\\\000\'"\n]'
);
});

it('support watch a character array', async function () {
// skip ahead to array initialization
const br = await dc.setBreakpointsRequest({
source: { path: varsSrc },
breakpoints: [{ line: lineTags['char string setup'] }],
});
expect(br.success).to.equal(true);
await dc.continue({ threadId: scope.thread.id }, 'breakpoint', {
line: lineTags['char string setup'],
path: varsSrc,
});
// step the program and see that the values were passed to the program and evaluated.
await dc.next(
{ threadId: scope.thread.id },
{ path: varsSrc, line: lineTags['char string setup'] + 1 }
);
scope = await getScopes(dc);
expect(
scope.scopes.body.scopes.length,
'Unexpected number of scopes returned'
).to.equal(2);
// Setup the new variable to be evaluated.
const res2 = await dc.evaluateRequest({
context: 'repl',
expression: '(char[])k',
frameId: scope.frame.id,
});
expect(res2.body.result).eq('[6]');
// Call handleVariableRequestObject() on above using returned variablesReference.
const vars = await dc.variablesRequest({
variablesReference: res2.body.variablesReference,
});
expect(
vars.body.variables.length,
'There is a different number of variables than expected'
).to.equal(6);
verifyVariable(vars.body.variables[0], '[0]', 'char', "104 'h'", {
hasMemoryReference: false,
});
verifyVariable(vars.body.variables[1], '[1]', 'char', "101 'e'", {
hasMemoryReference: false,
});
});

it.only('support watch of char cast of an int in complex structure', async function () {
// skip ahead to array initialization
const br = await dc.setBreakpointsRequest({
source: { path: varsSrc },
breakpoints: [{ line: lineTags['update embedded int'] }],
});
expect(br.success).to.equal(true);
await dc.continue({ threadId: scope.thread.id }, 'breakpoint', {
line: lineTags['update embedded int'],
path: varsSrc,
});
scope = await getScopes(dc);
expect(
scope.scopes.body.scopes.length,
'Unexpected number of scopes returned'
).to.equal(2);
// Setup the new variable to be evaluated.
const res = await dc.evaluateRequest({
context: 'watch',
expression: '(char[])r.z.a',
frameId: scope.frame.id,
});
expect(res.body.result).eq('[4]');
// Call handleVariableRequestObject() on above using returned variablesReference.
let vars = await dc.variablesRequest({
variablesReference: res.body.variablesReference,
});
expect(
vars.body.variables.length,
'There is a different number of variables than expected'
).to.equal(4);
verifyVariable(vars.body.variables[0], '[0]', 'char', "3 '\\003'", {
hasMemoryReference: false,
});
verifyVariable(vars.body.variables[1], '[1]', 'char', "0 '\\000'", {
hasMemoryReference: false,
});
// step the program and see that the values were passed to the program and evaluated.
await dc.next(
{ threadId: scope.thread.id },
{ path: varsSrc, line: lineTags['update embedded int'] + 1 }
);
scope = await getScopes(dc);
expect(
scope.scopes.body.scopes.length,
'Unexpected number of scopes returned'
).to.equal(2);
// Setup the updated embedded variable to be evaluated.
const res2 = await dc.evaluateRequest({
context: 'watch',
expression: '(char[])r.z.a',
frameId: scope.frame.id,
});
expect(res2.body.result).eq('[4]');
// Call handleVariableRequestObject() on above using returned variablesReference.
vars = await dc.variablesRequest({
variablesReference: res2.body.variablesReference,
});
expect(
vars.body.variables.length,
'There is a different number of variables than expected'
).to.equal(4);
verifyVariable(vars.body.variables[0], '[0]', 'char', "10 '\\n'", {
hasMemoryReference: false,
});
verifyVariable(vars.body.variables[1], '[1]', 'char', "0 '\\000'", {
hasMemoryReference: false,
});
});
});