Bug 564506 - Wrong value for static variables in functions during debug
Summary: Wrong value for static variables in functions during debug
Status: NEW
Alias: None
Product: CDT
Classification: Tools
Component: cdt-debug-dsf-gdb (show other bugs)
Version: 9.9.0   Edit
Hardware: All All
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact: Jonah Graham CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-06-20 14:59 EDT by Torbjörn Svensson CLA
Modified: 2020-06-21 09:12 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Torbjörn Svensson CLA 2020-06-20 14:59:52 EDT
Consider the following trivial example:

#include <stdlib.h>

int foo(void) {
	static int static_var = 7;
	return static_var;
}

int bar(void) {
	static int static_var = 9;
	return static_var;
}

int main(void) {
	foo();
	bar();
	return EXIT_SUCCESS;
}


Place a break point on the return statement in both foo() and bar() functions and launch a debug session.
The value for "static_var" in foo function will be shown as 7, so far so good.
The value for "static_var" in bar function will also be shown as 7, or let me rephrase, it will be a mix of 7 and 9!

The "Variables" and "Expressions" views both show the value as 7 and this is obviously wrong!
If you move the mouse over the "static_var" in bar(), the top part of the window will show 7, the same value as both the "Variables" and the "Expressions " views, but the lower part is even more confusing, since there the following text is shown:
Name : static_var
	Details:9
	Default:7
	Decimal:7
	Hex:0x7
	Binary:111
	Octal:07

If you enter 'p static_var' in the GDB console manually, the value returned is 9 as expected.

So, to conclude, something in the caching of variables does not play well with static variables in functions.


I've been trying to debug what's happening, but I have reached a wall that I cannot simply get around. What I think happens is that once GDB reaches the return statement of bar(), it will find an already existing GDB variable and tries to simply update it. But that variable still points to the application variable in the foo-function and will thus return the wrong value. Keep in mind that both "static_var" symbols are "available" in the entire application since they are simply mapped to a global symbol with a generated name during compilation.
Since I'm not so well into the internal of the debug model in CDT, I think it's safe to say that there is an assumption somewhere that there cannot be 2 symbols with the same name in the application that is accessible outside of the function that defines them, but I cannot find where that assumption is made.


The issue was originally seen using a cross toolchain for Arm, but was reproduced using a native toolchain on GNU/Linux with GDB 9.1, so I assume the same issue applies to all possible GDB targets.


@CDT-devs: As this bug has the kind of impact that you simply cannot trust the debugging capabilities in Eclipse, I've set the severity to major, but feel free to lower/increase it as you see fit.
Comment 1 Jonah Graham CLA 2020-06-20 17:27:32 EDT
This is major, no doubt. Do you have a corresponding MI trace? I can look at that on my phone now, but will try to get in front of a computer soon to figure out what is happening.
Comment 2 Jonah Graham CLA 2020-06-20 19:56:38 EDT
I have reproduced this - and here are the critical parts of the MI trace. I have breakpoints on the return lines of foo and bar.

When I hit BP in foo, CDT has this interaction (some commands removed for clarity):

017,248 41-stack-list-locals --thread 1 --frame 0 1
017,249 41^done,locals=[{name="static_var",value="7"}]
017,271 43-var-create --thread 1 --frame 0 - * static_var
017,271 43^done,name="var1",numchild="0",value="7",type="int",has_more="0"

On continuing, when the BP in bar is hit, this is the MI:

018,163 49-stack-list-locals --thread 1 --frame 0 1
018,164 49^done,locals=[{name="static_var",value="9"}]
018,185 51-var-update 1 var1
018,185 51^done,changelist=[]


----

If I make a slight change to the code and use local variables instead, this is what I see:

BP in foo hit:

977,950 40-stack-list-locals --thread 1 --frame 0 1
977,951 40^done,locals=[{name="static_var",value="7"}]
977,952 (gdb) 
977,989 42-var-create --thread 1 --frame 0 - * static_var
977,989 42^done,name="var1",numchild="0",value="7",type="int",thread-id="1",has_more="0"

continue and then BP in bar hit:

979,090 48-stack-list-locals --thread 1 --frame 0 1
979,092 48^done,locals=[{name="static_var",value="9"}]
979,124 50-var-update 1 var1
979,125 50^done,changelist=[{name="var1",in_scope="false",type_changed="false",has_more="0"}]
979,126 51-var-delete var1
979,126 51^done,ndeleted="1"
979,126 52-var-create --thread 1 --frame 0 - * static_var
979,126 52^done,name="var2",numchild="0",value="9",type="int",thread-id="1",has_more="0"
Comment 3 Jonah Graham CLA 2020-06-20 20:53:12 EDT
The problem here is CDT has a mistake in how it identifies whether a variable object refers to the same thing or not. The bit of code that encapsulates that decision is VariableObjectId. Basically that takes the expression (static_var), stack depth and context to make a unique id. 

AFAICT this is the part of the GDB manual that CDT is (unintentionally?) relying on:

"If an expression specified when creating a fixed variable object refers to a local variable, the variable object becomes bound to the thread and frame in which the variable object is created. When such variable object is updated, GDB makes sure that the thread/frame combination the variable object is bound to still exists, and re-evaluates the variable object in context of that thread/frame."

What is needed is a better heuristic for CDT to know if the var object can be reused or needs to be recreated.
Comment 4 Jonah Graham CLA 2020-06-20 20:58:47 EDT
> If you move the mouse over the "static_var" in bar(), the top part of the window

(This observation also applies to the Details pane of the Variables view)

This is because the Details line is obtained with "-data-evaluate-expression" MI instead, e.g.:

981,297 64-data-evaluate-expression --thread 1 --frame 0 static_var
981,299 64^done,value="9"

That call comes from MIExpressions.getFormattedExpressionValue(FormattedValueDMContext, DataRequestMonitor<FormattedValueDMData>)

That is used (according to the CDT source) because it supports pretty-printed values. 

So, that explains why "Details" shows the correct thing. It is the most equivalent to doing "p static_var" in the console.
Comment 5 Torbjörn Svensson CLA 2020-06-21 09:12:14 EDT
Thanks for looking into this Jonah.

The only solution that I can think of is to somehow involve the address of the variable that is monitored with the GDB variable, but I cannot seam to find a way to get it right.
I don't think that the address can be acquired without some communication with the GDB client, but how should this be implemented? If every call to VariableObjectId.generateId() will issue a call to the GDB client, it will likely consume almost as much resources as we are trying to save using the cache...

Any ideas?