首先,我们有一个框架,它将多次运行一系列测试,并确保每个runthrough的状态相同。这会捕获许多产生不确定行为的情况,包括由多线程或按指针值排序引起的行为。这些测试在Visual Studio 2008上的Debug中运行(即将推出到2010年)。
问题:不幸的是,测试并没有像我想的那样经常使用未初始化的变量。考虑以下情况:
struct Foo{ int m_a; int m_b; };
void doStuff( struct Foo& f);
...
Foo* bar = new Foo();
// Uninitialized in ctor, but heap initialized to 0xCD,
// so appears "deterministic"
if (bar->m_a)
{ ... }
Foo baz;
// may or may not initialize all of baz
// uninitialized members are left to 0xCC
doStuff( baz );
if (baz.m_b)
{ ... }
我想做的是在每个runthrough上使未初始化的值不同以捕获这些情况,例如:第一次运行时已知垃圾,第二次运行时为0。这样,对未初始化成员的任何计算都会给出不同的结果,并且在if
语句中检查它们也将采用相反的分支。
我控制了第一种情况,因为我们通过自己的堆路由new
和delete
。但是,我无法找到有关如何控制堆栈变量填充值的任何信息 - 这有可能吗?我在这里找到的最接近的问题是Can g++ fill uninitialized POD variables with known values?,但这是针对g ++的。我不需要便携式解决方案; Visual Studio特有的技巧就可以了。
注意#1:我知道lint / Rational Purify / Valgrind / [插入静态代码分析魔术子弹]会抓住这个,并且可能更强大。但我正在寻找一个可以对我们现有框架做出的小改动,这些可能需要更多时间来整合,而不是我准备花费,所以请不要提出这些建议。
注意#2:我们已经将警告级别设置为max和警告,因为错误已打开,这会捕获一些未初始化的变量,但是这并没有捕获“doStuff”函数忘记初始化的所有情况一些结构。
注意#3:我不太担心性能,因为它已经在Debug中运行,并且仅用于内部测试。
注意#4:相同的可执行文件用于测试(在“写入”模式下运行一次,然后再在“检查”模式下比较结果),所以不幸的是,目前不同的编译设置不是一个选项。
提前致谢!
答案 0 :(得分:1)
您可以尝试确定哪些代码正在填充并在运行时对其进行修补。如果dllversion是固定的,那么这对于调试构建来说应该是可以接受的。您可能需要能够处理不同的dll加载地址,因为出于安全原因,它们可能会在随机位置加载。
答案 1 :(得分:1)
我发现编译器可以非常有用地使用以下内容来跟踪这些问题:
class checked_int_t {
int i_;
public:
checked_int_t(int const i) : i_ (i) { }
operator int() const { return i_; }
};
现在您的代码可能会更改为以下内容,这会在您分配Foo对象时生成编译时错误,因为m_a未在默认构造函数中初始化。
struct Foo{
checked_int_t m_a;
int m_b;
};
请注意,您可以逐步执行此操作以检查单位变量,这样您就不必进行大的更改。只需找到一个您认为未正确初始化的变量并更改其类型。如果您以后必须设置该值,您可以选择执行以下操作:
class checked_int_t {
int i_;
bool set_;
public:
checked_int_t() : set_ (false) { }
checked_int_t(int const i) : i_ (i), set_ (true) { }
operator int() const { assert(set_); return i_; }
};
您可以使用条件编译来删除发布模式的类。
#ifdef NDEBUG
typedef int checked_int_t;
#else
class checked_int_t {
int i_;
public:
checked_int_t(int const i) : i_ (i) { }
operator int() const { return i_; }
};
#endif