【问题标题】:C++ Tracking primitive type value changeC++ 跟踪原始类型值变化
【发布时间】:2010-06-20 15:04:57
【问题描述】:

我有一个复杂的程序,有一个奇怪的错误,一些 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 实例,因此无法再跟踪。

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

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

【问题讨论】:

  • 也许带有条件断点的调试器会是一个更好的解决方案?

标签: c++ reference overloading dereference operator-keyword


【解决方案1】:

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

【讨论】:

  • 无法使用调试器运行此程序。问题发生在真实环境中,很难重现。
【解决方案2】:

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

【讨论】:

  • 那么,我应该更改所有相关代码。程序代码非常庞大,所以我正在考虑另一种解决方法。
【解决方案3】:

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

【讨论】:

  • 很遗憾,不能使用调试器什么的,因为程序是在真实条件下运行的。
  • 然后你需要像你之前做的那样包装这个值,但是停止返回对底层变量的访问。您需要代理每个请求。当然,如果问题非常微妙,那么这种更改可能会使其消失。
  • 另一个 hacky 解决方案可能是生成一个线程,该线程定期检查您看到问题的内存位置的值。但这引出了一个问题,当你在运行时检测到这个错误时你会怎么做。大概您需要检查程序及其内存内容以了解问题存在的原因。
  • 再想一想,您最好只在关键区域周围添加日志记录。通过这种方式,您可以添加必要的调试信息,以便在问题发生后尝试解决问题。或者,如果您可以在事后检测到问题,您可以添加一个断言以确保它没有发生。如果发生这种情况,您可以让程序退出并将其内存内容转储到您可以检查的转储/核心文件中。
  • 感谢您的长篇回复。就像你说的那样,如果没有办法轻易找到问题,我应该修改更多的代码。
【解决方案4】:

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

【讨论】:

  • 感谢您的回复。程序在 Windows 上运行,但我不能使用这种技术。一些逻辑错误导致的问题,不是随机写入内存。并且应该允许写入该值。
  • @ljh131:好吧,在这种情况下我只能考虑硬件断点。不过,您仍然需要某种调试器。
【解决方案5】:

如果你真的很绝望:

#define int ValueWatcher<int>

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

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

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

【讨论】:

  • 类型无关紧要。变量只声明一次。但我的问题是 - 正如我所提到的 - 当通过 * 或 & 取消引用时无法再跟踪。
  • 这就是为什么您需要将 int* 和 int& 替换为 ValueWatcher* 和 ValueWatcher&。
  • 啊……我明白了。在这种情况下,这似乎是唯一的解决方案。
【解决方案6】:

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

  • 你在某处有竞争条件

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

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

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

Program only crashes as release build -- how to debug?

【讨论】:

  • 感谢您的回复,但我的意思是无法使用调试器进行调试或跟踪,因为该错误很难重现给我。找到该错误的唯一方法是使用这些技术并在真实(许多用户)的情况下查看。它似乎不是由内存覆盖引起的。
  • 你确定吗??您描述的症状听起来与上面列出的一种情况完全一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多