gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:34:00 25 4
gpt4 key购买 nike

你能帮我理解 GTest 和结构打包的情况吗?

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

相关代码如下:

#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 }
));

编译和链接后(我用 cmake 构建了 GTest):

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 } 中的行而实例化的结构存在一些异常 INSTANTIATE_TEST_CASE_P

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

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

使用#pragma pack:

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

输出 20

使用 GNU g++ 打包属性:

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
};

输出 24

我有相当多的项目代码使用这种精确技术进行值参数化测试,在所有情况下,如果未使用其中一种回避策略,valgrind 都会报错。​​

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

最佳答案

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

来自上面的链接:

[...] It is the first address following the structure data that has the same alignment as the structure.

The general rule of trailing structure padding is this: the compiler will behave as though the structure has trailing padding out to its stride address. This rule controls what sizeof() will return.

Consider this example on a 64-bit x86 or ARM machine:

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

You might think that sizeof(struct foo3) should be 9, but it’s actually 16. The stride address is that of (&p)[2]. Thus, in the quad array, each member has 7 bytes of trailing padding, because the first member of each following struct wants to be self-aligned on an 8-byte boundary. The memory layout is as though the structure had been declared like this:

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 不会在 TestItem 中找到任何未初始化的字节。实例。

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

关于C++ - 对结构使用 GTest 值参数化测试会导致 valgrind 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33747056/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com