Skip to content

Commit

Permalink
Avoid calling -var-update on new vars. Sometimes, causing GDB to go i…
Browse files Browse the repository at this point in the history
…nto an infinite loop or even crashing
  • Loading branch information
haneefdm committed Aug 29, 2023
1 parent b02ba99 commit 5f310bf
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 133 deletions.
169 changes: 87 additions & 82 deletions src/gdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ enum SessionMode {
RESET = 'reset'
}

const VarNotFoundMsg = 'Variable object not found';
export class ExtendedVariable {
constructor(public name, public options) {
}
Expand Down Expand Up @@ -2631,58 +2632,64 @@ export class GDBDebugSession extends LoggingDebugSession {
threadId: number, frameId: number, isFloating: boolean): Promise<DebugProtocol.Variable> {
try {
let varObj: VariableObject;
let outOfScope = false;
try {
const changes = await this.miDebugger.varUpdate(gdbVarName, threadId, frameId);
const changelist = changes.result('changelist');
for (const change of changelist || []) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change /*, variable.valueStr*/);
} else {
const msg = `${symOrExpr} currently not in scope`;
await this.miDebugger.sendCommand(`var-delete ${gdbVarName}`);
if (this.args.showDevDebugOutput) {
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
}
outOfScope = true;
throw new Error(msg);
}
}
const varId = this.variableHandlesReverse[gdbVarName];
varObj = this.variableHandles.get(varId) as any;
}
catch (err) {
let varId = this.variableHandlesReverse[gdbVarName];
let createNewVar = varId === undefined;
let updateError = undefined;
if (!createNewVar) {
try {
if (outOfScope || (err instanceof MIError && err.message === 'Variable object not found')) {
// Create variable in current frame/thread context. Matters when we have to set the variable */
if (isFloating) {
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName);
const changes = await this.miDebugger.varUpdate(gdbVarName, threadId, frameId);
const changelist = changes.result('changelist');
for (const change of changelist || []) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change /*, variable.valueStr*/);
} else {
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@', threadId, frameId);
const msg = `${symOrExpr} currently not in scope`;
await this.miDebugger.sendCommand(`var-delete ${gdbVarName}`);
if (this.args.showDevDebugOutput) {
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
}
createNewVar = true;
throw new Error(msg);
}
const varId = this.findOrCreateVariable(varObj);
varObj.exp = symOrExpr;
varObj.id = varId;
} else if (isFloating) {
throw err;
}
varObj = this.variableHandles.get(varId) as any;
}
catch (err) {
updateError = err;
}
}

try {
if (createNewVar || (updateError instanceof MIError && updateError.message === VarNotFoundMsg)) {
// Create variable in current frame/thread context. Matters when we have to set the variable
if (isFloating) {
if (this.args.showDevDebugOutput) {
this.handleMsg('stderr', `Could not create global/static variable ${symOrExpr}\n`);
this.handleMsg('stderr', `Error: ${err}\n`);
}
varObj = null;
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@');
} else {
throw err;
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@', threadId, frameId);
}
varId = this.findOrCreateVariable(varObj);
varObj.exp = symOrExpr;
varObj.id = varId;
} else if (!varObj) {
throw updateError || new Error('updateOrCreateVariable: unknown error');
}
}
catch (err) {
if (isFloating) {
if (this.args.showDevDebugOutput) {
this.handleMsg('stderr', `Could not create global/static variable ${symOrExpr}\n`);
this.handleMsg('stderr', `Error: ${err}\n`);
}
varObj = null;
} else {
throw err;
}
}

if (isFloating && varObj) {
this.putFloatingVariable(parentVarReference, symOrExpr, varObj);
}
Expand Down Expand Up @@ -3181,54 +3188,52 @@ export class GDBDebugSession extends LoggingDebugSession {
const exprName = hasher.digest('hex');
const varObjName = `${args.context}_${exprName}`;
let varObj: VariableObject;
let outOfScope = false;
try {
const changes = await this.miDebugger.varUpdate(varObjName, threadId, frameId);
const changelist = changes.result('changelist');
for (const change of changelist || []) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
} else {
const msg = `${exp} currently not in scope`;
await this.miDebugger.sendCommand(`var-delete ${varObjName}`);
if (this.args.showDevDebugOutput) {
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
let varId = this.variableHandlesReverse[varObjName];
let createNewVar = varId === undefined;
let updateError = undefined;
if (!createNewVar) {
try {
const changes = await this.miDebugger.varUpdate(varObjName, threadId, frameId);
const changelist = changes.result('changelist');
for (const change of changelist || []) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
} else {
const msg = `${exp} currently not in scope`;
await this.miDebugger.sendCommand(`var-delete ${varObjName}`);
if (this.args.showDevDebugOutput) {
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
}
createNewVar = true;
throw new Error(msg);
}
outOfScope = true;
throw new Error(msg);
}
varObj = this.variableHandles.get(varId) as any;
}
catch (err) {
updateError = err;
}
const varId = this.variableHandlesReverse[varObjName];
varObj = this.variableHandles.get(varId) as any;
}
catch (err) {
if (!this.isBusy() && (outOfScope || ((err instanceof MIError && err.message === 'Variable object not found')))) {
try {
// We always create a floating variable so it will be updated in the context of the current frame
// Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
// from previous stack frames and returns the wrong results or says nothing changed when in fact it has
if (args.frameId === undefined) {
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
} else {
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
}

const varId = findOrCreateVariable(varObj);
varObj.exp = exp;
varObj.id = varId;
}
catch (e) {
throw e;
}
if (!this.isBusy() && (createNewVar || ((updateError instanceof MIError && updateError.message === VarNotFoundMsg)))) {
// We always create a floating variable so it will be updated in the context of the current frame
// Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
// from previous stack frames and returns the wrong results or says nothing changed when in fact it has
if (args.frameId === undefined) {
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
} else {
throw err;
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
}
}

const varId = findOrCreateVariable(varObj);
varObj.exp = exp;
varObj.id = varId;
} else if (!varObj) {
throw updateError || new Error('evaluateRequest: unknown error');
}
response.body = varObj.toProtocolEvaluateResponseBody();
this.sendResponse(response);
}
Expand Down
99 changes: 48 additions & 51 deletions src/live-watch-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,64 +124,61 @@ export class VariablesHandler {
const exprName = hasher.digest('hex');
const varObjName = `${args.context}_${exprName}`;
let varObj: VariableObject;
let forceCreate = false;
try {
let varId = this.variableHandlesReverse[varObjName];
const cachedChange = this.cachedChangeList && this.cachedChangeList[varObjName];
let changelist;
if (cachedChange) {
changelist = [];
} else if (this.cachedChangeList && (varId !== undefined)) {
changelist = [];
} else {
const changes = await miDebugger.varUpdate(varObjName, threadId, frameId);
changelist = changes.result('changelist') ?? [];
}
for (const change of changelist) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
if (this.cachedChangeList) {
this.cachedChangeList[name] = change;
}
let varId = this.variableHandlesReverse[varObjName];
let forceCreate = varId === undefined;
let updateError = undefined;
if (!forceCreate) {
try {
const cachedChange = this.cachedChangeList && this.cachedChangeList[varObjName];
let changelist;
if (cachedChange) {
changelist = [];
} else if (this.cachedChangeList && (varId !== undefined)) {
changelist = [];
} else {
const msg = `${exp} currently not in scope`;
await miDebugger.sendCommand(`var-delete ${varObjName}`);
if (session.args.showDevDebugOutput) {
session.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
}
forceCreate = true;
throw new Error(msg);
}
}
varId = this.variableHandlesReverse[varObjName];
varObj = this.variableHandles.get(varId) as any;
}
catch (err) {
if (!this.isBusy() && (forceCreate || ((err instanceof MIError && err.message === 'Variable object not found')))) {
if (this.cachedChangeList) {
delete this.cachedChangeList[varObjName];
const changes = await miDebugger.varUpdate(varObjName, threadId, frameId);
changelist = changes.result('changelist') ?? [];
}
try {
if (forceNoFrameId || (args.frameId === undefined)) {
varObj = await miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
for (const change of changelist) {
const inScope = MINode.valueOf(change, 'in_scope');
if (inScope === 'true') {
const name = MINode.valueOf(change, 'name');
const vId = this.variableHandlesReverse[name];
const v = this.variableHandles.get(vId) as any;
v.applyChanges(change);
if (this.cachedChangeList) {
this.cachedChangeList[name] = change;
}
} else {
varObj = await miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
const msg = `${exp} currently not in scope`;
await miDebugger.sendCommand(`var-delete ${varObjName}`);
if (session.args.showDevDebugOutput) {
session.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
}
forceCreate = true;
throw new Error(msg);
}
const varId = this.findOrCreateVariable(varObj);
varObj.exp = exp;
varObj.id = varId;
}
catch (e) {
throw e;
}
varObj = this.variableHandles.get(varId) as any;
}
else {
throw err;
catch (err) {
updateError = err;
}
}
if (!this.isBusy() && (forceCreate || ((updateError instanceof MIError && updateError.message === 'Variable object not found')))) {
if (this.cachedChangeList) {
delete this.cachedChangeList[varObjName];
}
if (forceNoFrameId || (args.frameId === undefined)) {
varObj = await miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
} else {
varObj = await miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
}
const varId = this.findOrCreateVariable(varObj);
varObj.exp = exp;
varObj.id = varId;
} else if (!varObj) {
throw updateError || new Error('live watch unknown error');
}

response.body = varObj.toProtocolEvaluateResponseBody();
Expand Down

0 comments on commit 5f310bf

Please sign in to comment.