2

我有一个带有奇怪错误的复杂程序,一些 int 值意外降至零。

所以我想跟踪这个内置类型值,然后我可以轻松调试。

为此,我创建了 ValueWatcher 模板类,因此我可以跟踪值的几乎变化,除非 ValueWatcher 取消引用。(我制作了这些取消引用运算符,因为程序需要 int *, &)

template <typename T>
class ValueWatcher
{   
public:
    ValueWatcher(const T &val)
    {
        cout << "constructor with raw value " << val << endl;

        _cur = _old = val;
    }

    ValueWatcher(const ValueWatcher& vw)    
    {
        cout << "constructor with ValueWatcher " << vw._cur << endl;

        _cur = vw._cur;
    }

    ValueWatcher& operator=(const ValueWatcher &rhs)
    {
        cout << "operator= with ValueWatcher " << rhs._cur << endl;

        _cur = rhs._cur;

        onChanged();

        return *this;
    }

    ValueWatcher& operator=(const T &val)
    {
        cout << "operator= with " << val << endl;

        _cur = val;

        onChanged();

        return *this;
    }

    int *operator&()
    {
        cout << "addressing operator" << endl;

        // can't track anymore!!!!!!!!!!!!!!!!!!!!!!!!!

        return &_cur;
    }

    operator int&()
    {
        cout << "operator int&" << endl;

        // can't track anymore!!!!!!!!!!!!!!!!!!!!!!!!!

        return _cur;
    }

    operator int&() const
    {
        cout << "const operator int&" << endl;

        return _cur;
    }

    operator int() const
    {
        cout << "operator int" << endl;

        return _cur;
    }

private:
    void onChanged()
    {
        // update old and do proper action

    }

    T _cur;
    T _old;

};

问题是,当客户端代码需要 ValueWatcher 的 int & 或 int * 时, - 它无论如何都可以提供 int & 或 int * 但是 - int * 或 & 不能保存 ValueWatcher 实例,因此无法再跟踪。

有没有办法解决这个问题?我认为它可以通过返回引用或指针类实例来解决,而不仅仅是返回 & 或 * 的内置类型。但我不知道该怎么做。

另外-我不能用调试器运行这个程序。该问题仅在真实环境中发生,并且很难重现。

4

6 回答 6

2

如果在调试器中运行时可以重现该行为,则应该能够设置值更改内存更改断点。这可能比引入代理实现更容易。

于 2010-06-20T15:08:49.773 回答
0

它可能不是最好的解决方案,但是如果您的 * 或 & 返回指向您的值观察者的指针/引用怎么办?否则我会禁止使用 * 或 &。(通过不实施或将其设为私有)。

于 2010-06-20T15:08:17.527 回答
0

我不认为这是可能的。一旦你返回一个 int* 或 int&,你就失去了追踪任何东西的能力。我能想到的唯一方法(也是正确的方法,IMO)是使用调试器并设置具有适当条件的观察点。当满足条件时,调试器将中断并暂停程序,以便您检查内存、调用堆栈等。

于 2010-06-20T15:12:31.837 回答
0

如果您可以为变量保留一个 PAGE_SIZE 字节,那么您可以使用VirtualProtect(如果您在 Windows 上)锁定这部分内存 - 例如,您可以设置只读访问权限。之后,任何试图访问该变量的东西都会使程序崩溃(因此您将能够编写内存转储和查明更改变量的例程)。我使用这种技术来查明类似的问题(多线程应用程序,随机覆盖内存块)。如果您不能立即调试机器,请尝试使用MiniDumpWriteDump写入转储。您将能够使用 WinDBG 或 Visual Studio 调试内存转储。

于 2010-06-20T15:33:49.433 回答
0

如果你真的很绝望:

#define int ValueWatcher<int>

在更好的情况下,您会使用

//typedef int intt;
typedef ValueWatcher<int> intt;

然后重新编写所有需要 int 的代码并替换它。替换int*intt*。替换int&intt&

于 2010-06-20T15:40:22.490 回答
0

你说你只在不调试时看到这个问题,所以我猜你有一个模糊的错误,只有在使用优化构建时才能看到。这种行为有几种可能的解释:

  • 你在某处有竞争条件

  • 您没有正确初始化变量......因此,在使用优化构建时,您的值的初始化方式与调试时不同。

  • 您在某个地方有一个缓冲区溢出,它正在覆盖您的一个变量。同样,这可能是您仅在使用优化构建时才能看到的东西...当您为调试而构建时,编译器将在堆栈上的变量周围留下额外的空间...这起到缓冲作用,可以防止一些错误暴露自己.

这是一个相关的 SO 帖子,它更详细地解释了这些问题:

程序仅在发布版本时崩溃——如何调试?

于 2010-06-20T15:48:18.157 回答