C ++ - 对结构使用GTest值参数化测试会导致valgrind错误

时间:2015-11-17 00:19:18

标签: c++ struct valgrind googletest packing

你能帮我理解GTest和struct packing的内容吗?

问题似乎与在GTest中作为值参数化测试中的值使用时如何打包结构有关。采用直接的方法为每个值实例化一个结构会导致与未初始化值相关的valgrind错误。

以下是相关代码:

$230.00  -> 230

当编译和链接时(我使用cmake构建了GTest):

#include <gtest/gtest.h>

struct TestItem
{
    const char * aString;
    int anInt0;
    int anInt1;
    int anInt2;
};

class TestBase : public ::testing::Test, public ::testing::WithParamInterface<TestItem> {};

TEST_P(TestBase, TestAtoi)
{
    TestItem item = GetParam();
    std::cout << sizeof(TestItem) << std::endl;
    ASSERT_FALSE(0);   // actual test doesn't matter
}

INSTANTIATE_TEST_CASE_P(
        TestBaseInstantiation,
        TestBase,
        ::testing::Values(
                TestItem { "0", 0, 0, 0 }
));

然后执行:

g++ gtest_valgrind.c -o gtest_valgrind -I gtest-1.7.0/include -L gtest-1.7.0/build -lgtest -lpthread -lgtest_main -std=c++11

生成以下输出:

valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./gtest_valgrind

这是很多输出,但基本上我认为它表明由于==17290== Memcheck, a memory error detector ==17290== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==17290== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==17290== Command: ./gtest_valgrind ==17290== Running main() from gtest_main.cc ==17290== Use of uninitialised value of size 8 ==17290== at 0x55B89F1: _itoa_word (_itoa.c:180) ==17290== by 0x55BC6F6: vfprintf (vfprintf.c:1660) ==17290== by 0x55E1578: vsnprintf (vsnprintf.c:119) ==17290== by 0x55C3531: snprintf (snprintf.c:33) ==17290== by 0x41F107: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F1AA: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F252: testing::internal2::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B8E: testing::internal2::TypeWithoutFormatter<TestItem, (testing::internal2::TypeKind)2>::PrintValue(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B63: std::basic_ostream<char, std::char_traits<char> >& testing::internal2::operator<< <char, std::char_traits<char>, TestItem>(std::basic_ostream<char, std::char_traits<char> >&, TestItem const&) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B3E: void testing_internal::DefaultPrintNonContainerTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B19: void testing::internal::DefaultPrintTo<TestItem>(char, testing::internal::bool_constant<false>, TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409ADB: void testing::internal::PrintTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== Uninitialised value was created by a stack allocation ==17290== at 0x404AAE: gtest_TestBaseInstantiationTestBase_EvalGenerator_() (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== ==17290== Conditional jump or move depends on uninitialised value(s) ==17290== at 0x55B89F8: _itoa_word (_itoa.c:180) ==17290== by 0x55BC6F6: vfprintf (vfprintf.c:1660) ==17290== by 0x55E1578: vsnprintf (vsnprintf.c:119) ==17290== by 0x55C3531: snprintf (snprintf.c:33) ==17290== by 0x41F107: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F1AA: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F252: testing::internal2::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B8E: testing::internal2::TypeWithoutFormatter<TestItem, (testing::internal2::TypeKind)2>::PrintValue(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B63: std::basic_ostream<char, std::char_traits<char> >& testing::internal2::operator<< <char, std::char_traits<char>, TestItem>(std::basic_ostream<char, std::char_traits<char> >&, TestItem const&) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B3E: void testing_internal::DefaultPrintNonContainerTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B19: void testing::internal::DefaultPrintTo<TestItem>(char, testing::internal::bool_constant<false>, TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409ADB: void testing::internal::PrintTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== Uninitialised value was created by a stack allocation ==17290== at 0x404AAE: gtest_TestBaseInstantiationTestBase_EvalGenerator_() (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== ==17290== Conditional jump or move depends on uninitialised value(s) ==17290== at 0x55BC742: vfprintf (vfprintf.c:1660) ==17290== by 0x55E1578: vsnprintf (vsnprintf.c:119) ==17290== by 0x55C3531: snprintf (snprintf.c:33) ==17290== by 0x41F107: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F1AA: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F252: testing::internal2::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B8E: testing::internal2::TypeWithoutFormatter<TestItem, (testing::internal2::TypeKind)2>::PrintValue(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B63: std::basic_ostream<char, std::char_traits<char> >& testing::internal2::operator<< <char, std::char_traits<char>, TestItem>(std::basic_ostream<char, std::char_traits<char> >&, TestItem const&) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B3E: void testing_internal::DefaultPrintNonContainerTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B19: void testing::internal::DefaultPrintTo<TestItem>(char, testing::internal::bool_constant<false>, TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409ADB: void testing::internal::PrintTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409AA6: testing::internal::UniversalPrinter<TestItem>::Print(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== Uninitialised value was created by a stack allocation ==17290== at 0x404AAE: gtest_TestBaseInstantiationTestBase_EvalGenerator_() (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== ==17290== Conditional jump or move depends on uninitialised value(s) ==17290== at 0x55B9659: vfprintf (vfprintf.c:1660) ==17290== by 0x55E1578: vsnprintf (vsnprintf.c:119) ==17290== by 0x55C3531: snprintf (snprintf.c:33) ==17290== by 0x41F107: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F1AA: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F252: testing::internal2::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B8E: testing::internal2::TypeWithoutFormatter<TestItem, (testing::internal2::TypeKind)2>::PrintValue(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B63: std::basic_ostream<char, std::char_traits<char> >& testing::internal2::operator<< <char, std::char_traits<char>, TestItem>(std::basic_ostream<char, std::char_traits<char> >&, TestItem const&) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B3E: void testing_internal::DefaultPrintNonContainerTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B19: void testing::internal::DefaultPrintTo<TestItem>(char, testing::internal::bool_constant<false>, TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409ADB: void testing::internal::PrintTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409AA6: testing::internal::UniversalPrinter<TestItem>::Print(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== Uninitialised value was created by a stack allocation ==17290== at 0x404AAE: gtest_TestBaseInstantiationTestBase_EvalGenerator_() (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== ==17290== Conditional jump or move depends on uninitialised value(s) ==17290== at 0x55B96DC: vfprintf (vfprintf.c:1660) ==17290== by 0x55E1578: vsnprintf (vsnprintf.c:119) ==17290== by 0x55C3531: snprintf (snprintf.c:33) ==17290== by 0x41F107: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F1AA: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x41F252: testing::internal2::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B8E: testing::internal2::TypeWithoutFormatter<TestItem, (testing::internal2::TypeKind)2>::PrintValue(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B63: std::basic_ostream<char, std::char_traits<char> >& testing::internal2::operator<< <char, std::char_traits<char>, TestItem>(std::basic_ostream<char, std::char_traits<char> >&, TestItem const&) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B3E: void testing_internal::DefaultPrintNonContainerTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409B19: void testing::internal::DefaultPrintTo<TestItem>(char, testing::internal::bool_constant<false>, TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409ADB: void testing::internal::PrintTo<TestItem>(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== by 0x409AA6: testing::internal::UniversalPrinter<TestItem>::Print(TestItem const&, std::ostream*) (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== Uninitialised value was created by a stack allocation ==17290== at 0x404AAE: gtest_TestBaseInstantiationTestBase_EvalGenerator_() (in /home/davida/git/gitlab/fcdm/lwm2m.git/gtest_valgrind) ==17290== [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from TestBaseInstantiation/TestBase [ RUN ] TestBaseInstantiation/TestBase.TestAtoi/0 24 [ OK ] TestBaseInstantiation/TestBase.TestAtoi/0 (76 ms) [----------] 1 test from TestBaseInstantiation/TestBase (126 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (246 ms total) [ PASSED ] 1 test. ==17290== ==17290== HEAP SUMMARY: ==17290== in use at exit: 0 bytes in 0 blocks ==17290== total heap usage: 239 allocs, 239 frees, 46,622 bytes allocated ==17290== ==17290== All heap blocks were freed -- no leaks are possible ==17290== ==17290== For counts of detected and suppressed errors, rerun with: -v ==17290== ERROR SUMMARY: 20 errors from 5 contexts (suppressed: 0 from 0) 中的TestItem { "0", 0, 0, 0 }行而实例化的结构有一些不寻常之处。

另请注意,TestItem的大小输出为24.在64位系统上,我不太确定如何协调它。 INSTANTIATE_TEST_CASE_P为8个字节,char *成员为4 * 3 = 12个字节。如果它们与8字节边界对齐,则应该是24 + 4 = 28字节(或者如果包括打包则为32)。如果它们与4个字节的边界对齐,那么这应该是20个。这里有一些关于结构打包我不明白的事情。

可以通过不同地打包结构来消除valgrind警告。例如,所有这三个修改都会导致干净的valgrind运行:

使用#pragma pack:

int

输出#pragma pack(1) struct TestItem { const char * aString; int anInt0; int anInt1; int anInt2; };

使用GNU g ++打包属性:

20

也输出struct TestItem { const char * aString; int anInt0; int anInt1; int anInt2; } __attribute__((packed));

最后,在结构中添加一个虚拟值:

20

输出struct TestItem { const char * aString; int anInt0; int anInt1; int anInt2; int anInt3; // add an extra member };

我有相当数量的项目代码使用这种精确的技术进行价值参数化测试,并且在所有情况下,如果不使用其中一种避免策略,valgrind会抱怨。

我想知道我为这个测试创建值的方法是否存在根本性的错误,或者这是gtest实例化测试用例的结果?或者,最不可能的是,这是一个gtest错误吗?

1 个答案:

答案 0 :(得分:5)

sizeof结构的TestItem输出是编译器structure alignment and trailing padding的结果。

从上面的链接:

  

[...]这是结构数据后面的第一个地址相同的地址   作为结构的对齐

     

尾随结构填充的一般规则是:编译器   表现就好像结构有尾随填充   大踏步地址。此规则控制sizeof()将返回的内容。

     

在64位x86或ARM计算机上考虑此示例:

struct foo3 {
    char *p;     /* 8 bytes */
    char c;      /* 1 byte */
};
struct foo3 singleton;
struct foo3 quad[4];
     

你可能认为sizeof(struct foo3)应该是9,但它实际上是16.大步   地址是(&amp; p)[2]的地址。因此,在四元组阵列中,每个成员具有7个   尾随填充的字节数,因为每个后续的第一个成员   struct希望在8字节边界上进行自对齐。记忆   布局就好像结构已经这样声明:

struct foo3 {
     char *p;     /* 8 bytes */
     char c;      /* 1 byte */
     char pad[7];
};

这解释了为什么你的sizeof(TestItem)得到24,因为尾随填充会将结构与sizeof (const char*)的倍数对齐,即8。

这个尾随填充字节是未初始化的,这是valgrind报告的内容。 Gtest正在运行一些代码,以便在测试失败时打印TestItem参数的实际值。如果您通过测试并且valgrind没有显示错误,则可以确认这一点。

当您强制编译器使用特定对齐或向结构添加新成员以使结构不需要任何尾部填充时,valgrind在{{1>中找不到任何未初始化的字节实例。

Gtest通常在打印值时调用TestItem,如果它不可用则返回打印对象中的原始字节数组。这可能是为什么要访问未初始化的尾随填充字节的原因。因此,您也可以通过为operator<<定义operator<<来消除valgrind错误。