Reputation: 415
I have following test in foo.cpp
which I build with g++ ./foo.cpp -lgtest -lgtest_main -lpthread -O0 -ggdb
:
#include "gtest/gtest.h"
struct s {};
class FooTest : public testing::TestWithParam<s> {};
TEST_P(FooTest, DoesBlah) {}
INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
When I run it with valgrind ./a.out
, valgrind reports a bunch of errors like the following:
==33778== Conditional jump or move depends on uninitialised value(s)
==33778== at 0x4C317CC: _itoa_word (_itoa.c:180)
==33778== by 0x4C4D6F4: __vfprintf_internal (vfprintf-internal.c:1687)
==33778== by 0x4C62119: __vsnprintf_internal (vsnprintf.c:114)
==33778== by 0x4C37F75: snprintf (snprintf.c:31)
==33778== by 0x14200A: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778== by 0x1420AB: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778== by 0x142151: testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/scandit/private/a.out)
==33778== by 0x11C519: void testing::internal::RawBytesPrinter::PrintValue<s, 1ul>(s const&, std::ostream*) (gtest-printers.h:268)
==33778== by 0x11C3C2: void testing::internal::PrintWithFallback<s>(s const&, std::ostream*) (gtest-printers.h:310)
==33778== by 0x11C158: void testing::internal::PrintTo<s>(s const&, std::ostream*) (gtest-printers.h:439)
==33778== by 0x11BF2E: testing::internal::UniversalPrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:701)
==33778== by 0x11BB5B: void testing::internal::UniversalPrint<s>(s const&, std::ostream*) (gtest-printers.h:998)
When I use e.g. std::string
instead of struct s
in TestWithParam<...>
, then I don't see any errors reported by valgrind.
What is missing in my struct ?
I am using g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
, valgrind-3.15.0
and gtest 1.11.0
.
Update:
Yes, I did try with a standard constructor, without any difference. I run now valgrind --track-origins=yes --num-callers=100 ./a.out
.
#include "gtest/gtest.h"
struct s {
s() {};
};
class FooTest : public testing::TestWithParam<s> {};
TEST_P(FooTest, DoesBlah) {}
INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
==60500== Conditional jump or move depends on uninitialised value(s)
==60500== at 0x4C317CC: _itoa_word (_itoa.c:180)
==60500== by 0x4C4D6F4: __vfprintf_internal (vfprintf-internal.c:1687)
==60500== by 0x4C62119: __vsnprintf_internal (vsnprintf.c:114)
==60500== by 0x4C37F75: snprintf (snprintf.c:31)
==60500== by 0x141868: testing::(anonymous namespace)::PrintByteSegmentInObjectTo(unsigned char const*, unsigned long, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500== by 0x141909: testing::(anonymous namespace)::PrintBytesInObjectToImpl(unsigned char const*, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500== by 0x1419AF: testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::ostream*) (in /home/user/private/a.out)
==60500== by 0x11C407: void testing::internal::RawBytesPrinter::PrintValue<s, 1ul>(s const&, std::ostream*) (gtest-printers.h:270)
==60500== by 0x11C2B0: void testing::internal::PrintWithFallback<s>(s const&, std::ostream*) (gtest-printers.h:312)
==60500== by 0x11C046: void testing::internal::PrintTo<s>(s const&, std::ostream*) (gtest-printers.h:441)
==60500== by 0x11BE1C: testing::internal::UniversalPrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:691)
==60500== by 0x11BA49: void testing::internal::UniversalPrint<s>(s const&, std::ostream*) (gtest-printers.h:980)
==60500== by 0x11B240: testing::internal::UniversalTersePrinter<s>::Print(s const&, std::ostream*) (gtest-printers.h:865)
==60500== by 0x11A861: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > testing::PrintToString<s>(s const&) (gtest-printers.h:1018)
==60500== by 0x119E9E: testing::internal::ParameterizedTestSuiteInfo<FooTest>::RegisterTests() (gtest-param-util.h:590)
==60500== by 0x147534: testing::internal::ParameterizedTestSuiteRegistry::RegisterTests() (in /home/user/private/a.out)
==60500== by 0x126425: testing::internal::UnitTestImpl::RegisterParameterizedTests() (in /home/user/private/a.out)
==60500== by 0x135FD7: testing::internal::UnitTestImpl::PostFlagParsingInit() (in /home/user/private/a.out)
==60500== by 0x153A81: void testing::internal::InitGoogleTestImpl<char>(int*, char**) (in /home/user/private/a.out)
==60500== by 0x138B27: testing::InitGoogleTest(int*, char**) (in /home/user/private/a.out)
==60500== by 0x16CFA6: main (in /home/user/private/a.out)
==60500== Uninitialised value was created by a heap allocation
==60500== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==60500== by 0x11C74A: testing::internal::ValuesInIteratorRangeGenerator<s>::Iterator::Current() const (gtest-param-util.h:334)
==60500== by 0x11A5C4: testing::internal::ParamIterator<s>::operator*() const (gtest-param-util.h:137)
==60500== by 0x119B18: testing::internal::ParameterizedTestSuiteInfo<FooTest>::RegisterTests() (gtest-param-util.h:572)
==60500== by 0x147534: testing::internal::ParameterizedTestSuiteRegistry::RegisterTests() (in /home/user/private/a.out)
==60500== by 0x126425: testing::internal::UnitTestImpl::RegisterParameterizedTests() (in /home/user/private/a.out)
==60500== by 0x135FD7: testing::internal::UnitTestImpl::PostFlagParsingInit() (in /home/user/private/a.out)
==60500== by 0x153A81: void testing::internal::InitGoogleTestImpl<char>(int*, char**) (in /home/user/private/a.out)
==60500== by 0x138B27: testing::InitGoogleTest(int*, char**) (in /home/user/private/a.out)
==60500== by 0x16CFA6: main (in /home/user/private/a.out)
Upvotes: 3
Views: 1901
Reputation: 1901
There's nothing wrong with your structure. The errors come from C++ not initializing the padding byte (sizeof(struct s) == 1
), so valgrind duly reports that particular byte wasn't initialized (because it really wasn't). The C++ standard doesn't mandate zero-initialisation
Quoting cppreference.com Zero-initialization:
Note that this is not the syntax for zero-initialization, which does not have a dedicated syntax in the language. These are examples of other types of initializations, which might perform zero-initialization.
I've taken your example
#include "gtest/gtest.h"
struct s {};
class FooTest : public testing::TestWithParam<s> {};
TEST_P(FooTest, DoesBlah) {}
INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
I've tried using the -ftrivial-auto-var-init=zero
flag (from here), but that doesn't work because Google Test initialises using new
(so not an automatic variable). So much for the easy fixes.
The solution I found is to overload the new
and new[]
operators:
#include <iostream>
#include "gtest/gtest.h"
struct s {
static void* operator new(std::size_t count)
{
std::cout << "custom new for size " << count << '\n';
void *p = ::operator new(count);
memset(p, 0, count);
return p;
}
static void* operator new[](std::size_t count)
{
std::cout << "custom new[] for size " << count << '\n';
void *p = ::operator new[](count);
memset(p, 0, count);
return p;
}
};
class FooTest : public testing::TestWithParam<s> {};
TEST_P(FooTest, DoesBlah) {}
INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, FooTest, testing::Values(s()));
$ g++ -O3 -Wall -g -o test test.cpp -lgtest -lgtest_main
$ valgrind --track-origins=yes ./test
==73422== Memcheck, a memory error detector
==73422== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==73422== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==73422== Command: ./test
==73422==
Running main() from ./googletest/src/gtest_main.cc
custom new for size 1
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MeenyMinyMoe/FooTest
[ RUN ] MeenyMinyMoe/FooTest.DoesBlah/0
[ OK ] MeenyMinyMoe/FooTest.DoesBlah/0 (6 ms)
[----------] 1 test from MeenyMinyMoe/FooTest (10 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (38 ms total)
[ PASSED ] 1 test.
==73422==
==73422== HEAP SUMMARY:
==73422== in use at exit: 0 bytes in 0 blocks
==73422== total heap usage: 217 allocs, 217 frees, 114,909 bytes allocated
==73422==
==73422== All heap blocks were freed -- no leaks are possible
==73422==
==73422== For lists of detected and suppressed errors, rerun with: -s
==73422== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Also check this answer for a better explanation.
Unfortunately I could not come up with a global replacement for new
. If I use the example code in that page I get a ton of mismatched-new-delete
warnings and running the resulting binary with valgrind generates a ton of Mismatched free() / delete / delete []
errors.
Cheers!
Upvotes: 3
Reputation: 6946
A quick google for the gtest source:
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
size_t count, ostream* os) {
char text[5] = "";
for (size_t i = 0; i != count; i++) {
const size_t j = start + i;
if (i != 0) {
// Organizes the bytes into groups of 2 for easy parsing by
// human.
if ((j % 2) == 0)
*os << ' ';
else
*os << '-';
}
GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
*os << text;
}
}
With gdb you may be able to get from there back to your code. Only 15 layers to wade through.
Upvotes: 0