2015-11-17 6 views
5

क्या आप मुझे समझने में मदद कर सकते हैं कि जीटीएस्ट और स्ट्रक्चर पैकिंग के साथ क्या हो रहा है?सी ++ - structs के साथ जीटीएस्ट वैल्यू-पैरामीटरयुक्त परीक्षणों का उपयोग करते हुए वाल्ग्रिंड त्रुटियों का कारण बनता है

समस्या यह है कि जीटीएस्ट में मान-पैरामीटर परीक्षण में मान के रूप में उपयोग किए जाने पर समस्या को कैसे पैक किया जाता है। प्रत्येक मूल्य परिणामों के लिए एक संरचना को तत्काल करने के सीधी दृष्टिकोण को अनियमित मानों से संबंधित वाल्ग्रिंड त्रुटियों में लेना।

यहाँ चिंतित कोड है:

#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 बनाया):

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

:

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 

और फिर साथ निष्पादित निम्नलिखित आउटपुट का उत्पादन होता है:

==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) 

यह बहुत सारे आउटपुट है, लेकिन अनिवार्य रूप से मुझे लगता है कि यह संकेत दे रहा है कि INSTANTIATE_TEST_CASE_P में TestItem { "0", 0, 0, 0 } लाइन के कारण तत्काल प्रारंभिक संरचना के बारे में कुछ असामान्य है।

ध्यान दें कि टेस्टआईटम का आकार 24 के रूप में आउटपुट है। 64-बिट सिस्टम पर, मुझे पूरा यकीन नहीं है कि इसे कैसे सुलझाना है। char * के लिए 8 बाइट्स, और int सदस्यों के लिए 4 * 3 = 12 बाइट्स। अगर वे 8 बाइट सीमाओं के साथ गठबंधन हैं, तो यह आकार 24 + 4 = 28 बाइट्स (या पैकिंग सहित 32 होना चाहिए) होना चाहिए। अगर वे 4 बाइट सीमाओं के साथ गठबंधन हैं, तो यह 20 होना चाहिए। कुछ ऐसा है जो मैं संरचना पैकिंग के बारे में नहीं समझता।

संरचना को अलग-अलग पैक करके वालग्रिंड चेतावनी को समाप्त किया जा सकता है। उदाहरण के लिए, इन संशोधनों के सभी तीन एक साफ valgrind रन में परिणाम:

#pragma पैक का उपयोग करना:

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

कि 20 आउटपुट।

जीएनयू जी का उपयोग ++ पैकिंग विशेषता:

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

कि 20 भी आउटपुट।

अन्त में, struct करने के लिए एक डमी मूल्य जोड़ने:

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

24 आउटपुट है।

मेरे पास एक उचित मात्रा में प्रोजेक्ट कोड है जो मूल्य-पैरामीटर परीक्षणों के लिए इस सटीक तकनीक का उपयोग करता है, और सभी मामलों में वाल्ग्रिंड शिकायत करेगा यदि बचपन की रणनीतियों में से कोई एक का उपयोग नहीं किया जाता है।

मैं जानना चाहता हूं कि इस परीक्षण के लिए मूल्य बनाने के मेरे दृष्टिकोण के साथ मूलभूत रूप से कुछ गलत है या क्या यह कुछ मामलों का परिणाम है जो तत्काल परीक्षण मामलों के साथ करता है? या, सबसे अधिक संभावना है, यह एक gtest बग है?

उत्तर

5

sizeof का उत्पादन TestItem संरचना के लिए संकलक structure alignment and trailing padding का परिणाम है।

ऊपर के लिंक से:

[...] यह संरचना डेटा संरचना रूप ही संरेखण है कि बाद पहले पता है।

पिछला संरचना पैडिंग का सामान्य नियम यह है: कंपाइलर इस तरह व्यवहार करेगा जैसे संरचना स्ट्रैड पते पर पैडिंग का पीछा कर रही है। यह नियम नियंत्रित करता है कि किस आकार() वापस आ जाएगा।

64-बिट x86 या एआरएम मशीन पर इस उदाहरण पर विचार:

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

आपको लगता है कि हो सकता है कि sizeof(struct foo3) 9 होना चाहिए, लेकिन यह वास्तव में 16 है स्ट्राइड पते का है (& पी) [ 2]। इस प्रकार, क्वाड सरणी में, प्रत्येक सदस्य के पासपीछे की पैडिंग के बाइट हैं, क्योंकि प्रत्येक निम्नलिखित संरचना का पहला सदस्य 8-बाइट सीमा पर स्वयं-गठबंधन होना चाहता है। स्मृति लेआउट के रूप में यद्यपि संरचना इस तरह की घोषणा की गई थी जाता है:

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

बताते हैं यही कारण है कि आप पीछे चल गद्दी के बाद से sizeof(TestItem) के लिए 24 हो रही है sizeof (const char*) की एक बहु है, जो 8 के लिए संरचना संरेखित होगा

यह पिछला पैडिंग बाइट अनियमित है और यही वाल्ग्रिंड रिपोर्ट है। परीक्षण विफल होने पर TestItem पैरामीटर के वास्तविक मान को मुद्रित करने के लिए Gtest कुछ कोड चला रहा है। यह पुष्टि की जा सकती है कि यदि आप अपना परीक्षण पास करते हैं और वालग्रिंड त्रुटि नहीं दिखाता है।

जब आप कंपाइलर को एक विशिष्ट संरेखण का उपयोग करने के लिए मजबूर करते हैं या संरचना में एक नया सदस्य जोड़ते हैं ताकि संरचना को किसी भी पिछली पैडिंग की आवश्यकता न हो, तो वाल्ग्रिंड को TestItem उदाहरणों में कोई अनियमित बाइट नहीं मिला है।

जीटीएस्ट आमतौर पर operator<< पर कॉल करता है जब मूल्यों को प्रिंट करता है और ऑब्जेक्ट में बाइट्स की कच्ची सरणी मुद्रित करने के लिए वापस गिर जाता है यदि यह उपलब्ध नहीं है। शायद यही कारण है कि अनियंत्रित पिछला पैडिंग बाइट एक्सेस हो रहा है। तो आप TestItem के लिए operator<< को परिभाषित करके valgrind त्रुटियों से छुटकारा पा सकते हैं।

+0

धन्यवाद, यह समझ में आता है। हालांकि परीक्षण पास हो रहा है - 'ASSERT_FALSE (0)' सफल होगा, इसलिए डेटा का कोई डंप मुद्रित नहीं होता है। लेकिन मुझे लगता है कि आपका बिंदु अभी भी ध्वनि है - टेस्ट फ्रेमवर्क डेटा के माध्यम से 'आकार' के अनुसार स्कैनिंग और पैडिंग में अनियमित मूल्यों को पढ़ना चाहिए। – meowsqueak

+0

@meowsqueak तो Gtest परीक्षण पास होने पर भी परीक्षण पैरामीटर मान मुद्रित करने का प्रयास कर रहा है। यह अजीब है, क्योंकि आप मानक आउटपुट में मुद्रित वास्तविक मूल्य नहीं देख सकते हैं, है ना? क्या आपने 'ऑपरेटर <<' को परिभाषित करने का प्रयास किया था? –

+1

हां, मुझे लगता है कि यह कम से कम मूल्य पर एक स्नप्रिंट कर रहा है, भले ही यह इसे प्रिंट न करे। हो सकता है कि यह आउटपुट तैयार करता है ताकि वास्तव में परीक्षण चलाने वाले फ़ंक्शन को आवश्यक होने पर पूर्व-पका हुआ विवरण आसानी से प्रिंट कर सके। मेरे पास स्रोत है, मैं _could_ पता लगाना चाहिए कि मुझे क्या चाहिए। 'ऑपरेटर <<' को परिभाषित करने का आपका विचार अपेक्षित कार्य करता है, और वाल्ग्रिंड त्रुटि को समाप्त करता है। वैसे भी बफर डंप की तुलना में यह अधिक उपयोगी है, इसलिए मैं शायद इस परियोजना में इसका उपयोग करूंगा। – meowsqueak

संबंधित मुद्दे