Reputation: 2243
After some changes the output of an application is no longer valid in some cases. Some output values are wrong. The values used to compute those outputs are correct, and at some point in the complicated processing things take a wrong turn.
Is there a tool to track the origin of a C++ variable's value? I've used valgrind before to track NULL values, but what I would like is something more general. Is there a more general tool that shows the chain (or tree) of assignments that led to a variable to have its value at a certain point in time?
PS: The code is, as almost all legacy code, hard to follow, has no unit tests, etc.
EDIT: Data breakpoints on the variable would only show me the endpoint in the chain. A bit more than that would be nice to have.
Upvotes: 1
Views: 1918
Reputation: 3660
What you could do is wrap your variables of interest with a family of generic wrappers that would log a stacktrace and the values on each call. Something like (omitting some details):
template <typename T>
class TracingValue
{
private:
T m_Val;
...
void LogStackTrace() {...}
public:
// write
TracingValue& operator= (const T& val) {
LogStackTrace();
m_Val=val;
return *this;
}
// read
operator T () const { return m_Val; }
// "connect" to other values
TracingValue& operator=(const TracingValue &other) {
LogStackTrace();
m_Val = other.m_Val;
std::cout << "id: " << this->Id() << " new value: " << m_Val
<< " from id: " << other.Id() << std::endl;
return *this;
}
};
Logging the stack traces will be slow and may generate too much data, but if you use it sparingly, you might get a better picture what is happening in your software. You can then put breakpoints in the wrapper to catch the modifications when they're happening.
That should work for the trivial cases. In case serialization and other operations are involved, it might need to be refined further.
Tracing value changes and constructions from other wrapped values is possible. See →Ideone for an example:
TracingValue<double> d;
d = 3.;
d = 42.;
double x = d - 2.;
std::cout << x << std::endl;
TracingValue<double> other_d(d);
TracingValue<double> another_d;
another_d = other_d;
outputting
id: 1 constructed with value: 0
id: 1 new value: 3
id: 1 new value: 42
40
id: 2 constructed with value: 42
id: 2 constructed from id: 1
id: 3 constructed with value: 0
id: 3 new value: 42 from id: 2
Upvotes: 2