Skip to content

Commit 5f310bf

Browse files
committed
Avoid calling -var-update on new vars. Sometimes, causing GDB to go into an infinite loop or even crashing
1 parent b02ba99 commit 5f310bf

File tree

2 files changed

+135
-133
lines changed

2 files changed

+135
-133
lines changed

src/gdb.ts

Lines changed: 87 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ enum SessionMode {
9898
RESET = 'reset'
9999
}
100100

101+
const VarNotFoundMsg = 'Variable object not found';
101102
export class ExtendedVariable {
102103
constructor(public name, public options) {
103104
}
@@ -2631,58 +2632,64 @@ export class GDBDebugSession extends LoggingDebugSession {
26312632
threadId: number, frameId: number, isFloating: boolean): Promise<DebugProtocol.Variable> {
26322633
try {
26332634
let varObj: VariableObject;
2634-
let outOfScope = false;
2635-
try {
2636-
const changes = await this.miDebugger.varUpdate(gdbVarName, threadId, frameId);
2637-
const changelist = changes.result('changelist');
2638-
for (const change of changelist || []) {
2639-
const inScope = MINode.valueOf(change, 'in_scope');
2640-
if (inScope === 'true') {
2641-
const name = MINode.valueOf(change, 'name');
2642-
const vId = this.variableHandlesReverse[name];
2643-
const v = this.variableHandles.get(vId) as any;
2644-
v.applyChanges(change /*, variable.valueStr*/);
2645-
} else {
2646-
const msg = `${symOrExpr} currently not in scope`;
2647-
await this.miDebugger.sendCommand(`var-delete ${gdbVarName}`);
2648-
if (this.args.showDevDebugOutput) {
2649-
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
2650-
}
2651-
outOfScope = true;
2652-
throw new Error(msg);
2653-
}
2654-
}
2655-
const varId = this.variableHandlesReverse[gdbVarName];
2656-
varObj = this.variableHandles.get(varId) as any;
2657-
}
2658-
catch (err) {
2635+
let varId = this.variableHandlesReverse[gdbVarName];
2636+
let createNewVar = varId === undefined;
2637+
let updateError = undefined;
2638+
if (!createNewVar) {
26592639
try {
2660-
if (outOfScope || (err instanceof MIError && err.message === 'Variable object not found')) {
2661-
// Create variable in current frame/thread context. Matters when we have to set the variable */
2662-
if (isFloating) {
2663-
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName);
2640+
const changes = await this.miDebugger.varUpdate(gdbVarName, threadId, frameId);
2641+
const changelist = changes.result('changelist');
2642+
for (const change of changelist || []) {
2643+
const inScope = MINode.valueOf(change, 'in_scope');
2644+
if (inScope === 'true') {
2645+
const name = MINode.valueOf(change, 'name');
2646+
const vId = this.variableHandlesReverse[name];
2647+
const v = this.variableHandles.get(vId) as any;
2648+
v.applyChanges(change /*, variable.valueStr*/);
26642649
} else {
2665-
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@', threadId, frameId);
2650+
const msg = `${symOrExpr} currently not in scope`;
2651+
await this.miDebugger.sendCommand(`var-delete ${gdbVarName}`);
2652+
if (this.args.showDevDebugOutput) {
2653+
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
2654+
}
2655+
createNewVar = true;
2656+
throw new Error(msg);
26662657
}
2667-
const varId = this.findOrCreateVariable(varObj);
2668-
varObj.exp = symOrExpr;
2669-
varObj.id = varId;
2670-
} else if (isFloating) {
2671-
throw err;
26722658
}
2659+
varObj = this.variableHandles.get(varId) as any;
26732660
}
26742661
catch (err) {
2662+
updateError = err;
2663+
}
2664+
}
2665+
2666+
try {
2667+
if (createNewVar || (updateError instanceof MIError && updateError.message === VarNotFoundMsg)) {
2668+
// Create variable in current frame/thread context. Matters when we have to set the variable
26752669
if (isFloating) {
2676-
if (this.args.showDevDebugOutput) {
2677-
this.handleMsg('stderr', `Could not create global/static variable ${symOrExpr}\n`);
2678-
this.handleMsg('stderr', `Error: ${err}\n`);
2679-
}
2680-
varObj = null;
2670+
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@');
26812671
} else {
2682-
throw err;
2672+
varObj = await this.miDebugger.varCreate(parentVarReference, symOrExpr, gdbVarName, '@', threadId, frameId);
2673+
}
2674+
varId = this.findOrCreateVariable(varObj);
2675+
varObj.exp = symOrExpr;
2676+
varObj.id = varId;
2677+
} else if (!varObj) {
2678+
throw updateError || new Error('updateOrCreateVariable: unknown error');
2679+
}
2680+
}
2681+
catch (err) {
2682+
if (isFloating) {
2683+
if (this.args.showDevDebugOutput) {
2684+
this.handleMsg('stderr', `Could not create global/static variable ${symOrExpr}\n`);
2685+
this.handleMsg('stderr', `Error: ${err}\n`);
26832686
}
2687+
varObj = null;
2688+
} else {
2689+
throw err;
26842690
}
26852691
}
2692+
26862693
if (isFloating && varObj) {
26872694
this.putFloatingVariable(parentVarReference, symOrExpr, varObj);
26882695
}
@@ -3181,54 +3188,52 @@ export class GDBDebugSession extends LoggingDebugSession {
31813188
const exprName = hasher.digest('hex');
31823189
const varObjName = `${args.context}_${exprName}`;
31833190
let varObj: VariableObject;
3184-
let outOfScope = false;
3185-
try {
3186-
const changes = await this.miDebugger.varUpdate(varObjName, threadId, frameId);
3187-
const changelist = changes.result('changelist');
3188-
for (const change of changelist || []) {
3189-
const inScope = MINode.valueOf(change, 'in_scope');
3190-
if (inScope === 'true') {
3191-
const name = MINode.valueOf(change, 'name');
3192-
const vId = this.variableHandlesReverse[name];
3193-
const v = this.variableHandles.get(vId) as any;
3194-
v.applyChanges(change);
3195-
} else {
3196-
const msg = `${exp} currently not in scope`;
3197-
await this.miDebugger.sendCommand(`var-delete ${varObjName}`);
3198-
if (this.args.showDevDebugOutput) {
3199-
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
3191+
let varId = this.variableHandlesReverse[varObjName];
3192+
let createNewVar = varId === undefined;
3193+
let updateError = undefined;
3194+
if (!createNewVar) {
3195+
try {
3196+
const changes = await this.miDebugger.varUpdate(varObjName, threadId, frameId);
3197+
const changelist = changes.result('changelist');
3198+
for (const change of changelist || []) {
3199+
const inScope = MINode.valueOf(change, 'in_scope');
3200+
if (inScope === 'true') {
3201+
const name = MINode.valueOf(change, 'name');
3202+
const vId = this.variableHandlesReverse[name];
3203+
const v = this.variableHandles.get(vId) as any;
3204+
v.applyChanges(change);
3205+
} else {
3206+
const msg = `${exp} currently not in scope`;
3207+
await this.miDebugger.sendCommand(`var-delete ${varObjName}`);
3208+
if (this.args.showDevDebugOutput) {
3209+
this.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
3210+
}
3211+
createNewVar = true;
3212+
throw new Error(msg);
32003213
}
3201-
outOfScope = true;
3202-
throw new Error(msg);
32033214
}
3215+
varObj = this.variableHandles.get(varId) as any;
3216+
}
3217+
catch (err) {
3218+
updateError = err;
32043219
}
3205-
const varId = this.variableHandlesReverse[varObjName];
3206-
varObj = this.variableHandles.get(varId) as any;
32073220
}
3208-
catch (err) {
3209-
if (!this.isBusy() && (outOfScope || ((err instanceof MIError && err.message === 'Variable object not found')))) {
3210-
try {
3211-
// We always create a floating variable so it will be updated in the context of the current frame
3212-
// Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
3213-
// from previous stack frames and returns the wrong results or says nothing changed when in fact it has
3214-
if (args.frameId === undefined) {
3215-
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
3216-
} else {
3217-
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
3218-
}
3219-
3220-
const varId = findOrCreateVariable(varObj);
3221-
varObj.exp = exp;
3222-
varObj.id = varId;
3223-
}
3224-
catch (e) {
3225-
throw e;
3226-
}
3221+
if (!this.isBusy() && (createNewVar || ((updateError instanceof MIError && updateError.message === VarNotFoundMsg)))) {
3222+
// We always create a floating variable so it will be updated in the context of the current frame
3223+
// Technicall, we should be able to bind this to this frame but for some reason gdb gets confused
3224+
// from previous stack frames and returns the wrong results or says nothing changed when in fact it has
3225+
if (args.frameId === undefined) {
3226+
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
32273227
} else {
3228-
throw err;
3228+
varObj = await this.miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
32293229
}
3230-
}
32313230

3231+
const varId = findOrCreateVariable(varObj);
3232+
varObj.exp = exp;
3233+
varObj.id = varId;
3234+
} else if (!varObj) {
3235+
throw updateError || new Error('evaluateRequest: unknown error');
3236+
}
32323237
response.body = varObj.toProtocolEvaluateResponseBody();
32333238
this.sendResponse(response);
32343239
}

src/live-watch-monitor.ts

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -124,64 +124,61 @@ export class VariablesHandler {
124124
const exprName = hasher.digest('hex');
125125
const varObjName = `${args.context}_${exprName}`;
126126
let varObj: VariableObject;
127-
let forceCreate = false;
128-
try {
129-
let varId = this.variableHandlesReverse[varObjName];
130-
const cachedChange = this.cachedChangeList && this.cachedChangeList[varObjName];
131-
let changelist;
132-
if (cachedChange) {
133-
changelist = [];
134-
} else if (this.cachedChangeList && (varId !== undefined)) {
135-
changelist = [];
136-
} else {
137-
const changes = await miDebugger.varUpdate(varObjName, threadId, frameId);
138-
changelist = changes.result('changelist') ?? [];
139-
}
140-
for (const change of changelist) {
141-
const inScope = MINode.valueOf(change, 'in_scope');
142-
if (inScope === 'true') {
143-
const name = MINode.valueOf(change, 'name');
144-
const vId = this.variableHandlesReverse[name];
145-
const v = this.variableHandles.get(vId) as any;
146-
v.applyChanges(change);
147-
if (this.cachedChangeList) {
148-
this.cachedChangeList[name] = change;
149-
}
127+
let varId = this.variableHandlesReverse[varObjName];
128+
let forceCreate = varId === undefined;
129+
let updateError = undefined;
130+
if (!forceCreate) {
131+
try {
132+
const cachedChange = this.cachedChangeList && this.cachedChangeList[varObjName];
133+
let changelist;
134+
if (cachedChange) {
135+
changelist = [];
136+
} else if (this.cachedChangeList && (varId !== undefined)) {
137+
changelist = [];
150138
} else {
151-
const msg = `${exp} currently not in scope`;
152-
await miDebugger.sendCommand(`var-delete ${varObjName}`);
153-
if (session.args.showDevDebugOutput) {
154-
session.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
155-
}
156-
forceCreate = true;
157-
throw new Error(msg);
158-
}
159-
}
160-
varId = this.variableHandlesReverse[varObjName];
161-
varObj = this.variableHandles.get(varId) as any;
162-
}
163-
catch (err) {
164-
if (!this.isBusy() && (forceCreate || ((err instanceof MIError && err.message === 'Variable object not found')))) {
165-
if (this.cachedChangeList) {
166-
delete this.cachedChangeList[varObjName];
139+
const changes = await miDebugger.varUpdate(varObjName, threadId, frameId);
140+
changelist = changes.result('changelist') ?? [];
167141
}
168-
try {
169-
if (forceNoFrameId || (args.frameId === undefined)) {
170-
varObj = await miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
142+
for (const change of changelist) {
143+
const inScope = MINode.valueOf(change, 'in_scope');
144+
if (inScope === 'true') {
145+
const name = MINode.valueOf(change, 'name');
146+
const vId = this.variableHandlesReverse[name];
147+
const v = this.variableHandles.get(vId) as any;
148+
v.applyChanges(change);
149+
if (this.cachedChangeList) {
150+
this.cachedChangeList[name] = change;
151+
}
171152
} else {
172-
varObj = await miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
153+
const msg = `${exp} currently not in scope`;
154+
await miDebugger.sendCommand(`var-delete ${varObjName}`);
155+
if (session.args.showDevDebugOutput) {
156+
session.handleMsg('log', `Expression ${msg}. Will try to create again\n`);
157+
}
158+
forceCreate = true;
159+
throw new Error(msg);
173160
}
174-
const varId = this.findOrCreateVariable(varObj);
175-
varObj.exp = exp;
176-
varObj.id = varId;
177-
}
178-
catch (e) {
179-
throw e;
180161
}
162+
varObj = this.variableHandles.get(varId) as any;
181163
}
182-
else {
183-
throw err;
164+
catch (err) {
165+
updateError = err;
166+
}
167+
}
168+
if (!this.isBusy() && (forceCreate || ((updateError instanceof MIError && updateError.message === 'Variable object not found')))) {
169+
if (this.cachedChangeList) {
170+
delete this.cachedChangeList[varObjName];
171+
}
172+
if (forceNoFrameId || (args.frameId === undefined)) {
173+
varObj = await miDebugger.varCreate(0, exp, varObjName, '@'); // Create floating variable
174+
} else {
175+
varObj = await miDebugger.varCreate(0, exp, varObjName, '@', threadId, frameId);
184176
}
177+
const varId = this.findOrCreateVariable(varObj);
178+
varObj.exp = exp;
179+
varObj.id = varId;
180+
} else if (!varObj) {
181+
throw updateError || new Error('live watch unknown error');
185182
}
186183

187184
response.body = varObj.toProtocolEvaluateResponseBody();

0 commit comments

Comments
 (0)