Puoi aiutarmi a capire cosa sta succedendo qui con GTest e struct packing?C++: l'uso dei test con parametri GTest con struct causa errori valgrind
Il problema sembra essere correlato al modo in cui una struct viene impacchettata quando viene utilizzata come valore in un test con parametri parametrizzati in GTest. L'approccio diretto di istanziare una struttura per ogni valore determina errori valgrind relativi a valori non inizializzati.
Ecco il codice in questione:
#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 }
));
Quando questo viene compilato e collegato (ho costruito GTEST con cmake):
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
e quindi eseguito con:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./gtest_valgrind
Il viene prodotto l'output seguente:
==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)
Questo è un sacco di output, ma in sostanza penso che sta indicando che c'è qualcosa di insolito nella struttura che viene istanziata a causa della linea TestItem { "0", 0, 0, 0 }
in INSTANTIATE_TEST_CASE_P
.
Si noti inoltre che la dimensione di TestItem viene emessa come 24. Su un sistema a 64 bit, non sono abbastanza sicuro di come riconciliare questo. 8 byte per char *
e 4 * 3 = 12 byte per i membri int
. Se sono allineati ai limiti di 8 byte, questa deve essere la dimensione 24 + 4 = 28 byte (o 32 se include l'imballaggio). Se sono allineati ai limiti di 4 byte, allora questo dovrebbe essere 20. C'è qualcosa che non capisco qui su struct packing.
L'avviso valgrind può essere eliminato imballando la struttura in modo diverso. Ad esempio, tutti e tre tali modifiche determinino una corsa valgrind pulito:
Uso #pragma pacchetto:
#pragma pack(1)
struct TestItem
{
const char * aString;
int anInt0;
int anInt1;
int anInt2;
};
che emette 20
.
Uso GNU g ++ attributo imballaggio:
struct TestItem
{
const char * aString;
int anInt0;
int anInt1;
int anInt2;
} __attribute__((packed));
che emette 20
troppo.
Infine, aggiungendo un valore fittizia alla struct:
struct TestItem
{
const char * aString;
int anInt0;
int anInt1;
int anInt2;
int anInt3; // add an extra member
};
che emette 24
.
Ho una discreta quantità di codice di progetto che utilizza questa tecnica esatta per i test con parametri parametrici, e in tutti i casi valgrind si lamenterà se una delle strategie di elusione non viene utilizzata.
Mi piacerebbe sapere se c'è qualcosa di fondamentalmente sbagliato nel mio approccio alla creazione di valori per questo test, o è una conseguenza di qualcosa di più dei casi di test di istanziazione? O, molto improbabile, questo è un bug gtest?
Grazie, ha senso. Comunque il test sta passando - 'ASSERT_FALSE (0)' riuscirà, quindi non viene stampato nessun dump dei dati. Ma penso che il tuo punto sia ancora valido: il framework di test deve eseguire la scansione dei dati in base a 'sizeof' e leggendo i valori non inizializzati nel padding. – meowsqueak
@meowsqueak Quindi Gtest sta tentando di stampare il valore del parametro di test anche quando il test è superato. È strano, perché non puoi vedere il valore reale stampato in output standard, giusto? Hai provato a definire l'operatore << '? –
Sì, penso che stia facendo almeno un snprintf sul valore, anche se non lo stampa. Forse prepara l'output in modo che la funzione che esegue effettivamente il test possa facilmente stampare la descrizione precotta, se necessario. Ho la fonte, potrei scoprire se ne avessi bisogno. La tua idea di definire l'operatore '' funziona come previsto ed elimina l'errore valgrind. È molto più utile di un buffer dump comunque, quindi probabilmente lo userò per tutto il progetto. – meowsqueak