1) मेरी कुछ जांच के बाद और क्रिस बेके के बहुत अच्छे समाधान (विंडोज़ के लिए) के आधार पर, मैंने लिनक्स ओएस के लिए एक बहुत ही समान समाधान किया है।
2) मेरे स्मृति रिसाव का पता लगाने के लक्ष्यों:
बहुत स्पष्ट हैं - रिसाव का पता लगाने के साथ-साथ, जबकि:
2.1) एक सटीक ढंग से आदर्श रूप में - से संकेत मिलता है कि उनकी संख्या कितनी बाइट्स अभी तक आबंटित किया गया पुनः आवंटित की जाती नहीं।
2.2) सर्वोत्तम प्रयास - यदि बिल्कुल नहीं, तो "झूठी सकारात्मक" तरीके से संकेत मिलता है (हमें एक रिसाव के बारे में बताएं, भले ही यह जरूरी नहीं है और साथ ही साथ किसी भी रिसाव का पता लगाना न भूलें)। यहां अपने आप पर अधिक कठोर होना बेहतर है।
2.3) चूंकि मैं जीटीएस्ट फ्रेमवर्क में अपने यूनिट परीक्षण लिख रहा हूं - प्रत्येक जीटीएस्ट इकाई परीक्षण को "परमाणु इकाई" के रूप में जांचें।
2.4) मॉलोक/फ्री का उपयोग करके "सी-स्टाइल" आवंटन (विध्वंस) भी ध्यान में रखें।
2.5) आदर्श रूप से - सी ++ "स्थान आवंटन" पर विचार करें।
2.6) मौजूदा कोड में उपयोग और एकीकृत करने के लिए आसान (यूनिट परीक्षण के लिए जीटीएस्ट आधारित कक्षाएं)।
2।7) प्रत्येक परीक्षण और/या पूरे टेस्ट क्लास के लिए मुख्य चेक सेटिंग्स को "कॉन्फ़िगर" करने की क्षमता (स्मृति जांच सक्षम/अक्षम करें ...) की क्षमता है।
3) समाधान वास्तुकला:
मेरे समाधान GTest ढांचे का उपयोग करने का विरासत में मिला क्षमताओं का उपयोग करता है, इसलिए यह प्रत्येक इकाई परीक्षण वर्ग हम भविष्य में जोड़ देगा के लिए एक "आधार" वर्ग परिभाषित करता है। असल में, बेस क्लास मुख्य कार्यक्षमताओं को निम्नलिखित में विभाजित किया जा सकता है:
3.1) परीक्षण विफलता के मामले में ढेर पर आवंटित "अतिरिक्त स्मृति" की मात्रा को समझने के लिए "पहला" जीटीएस्ट स्टाइल परीक्षण चलाएं जैसा कि क्रिस बेके ने उपरोक्त उत्तर के अंतिम वाक्य में उल्लेख किया था।
3.2) एकीकृत करने में आसान - बस इस बेस क्लास से प्राप्त होता है और आपके यूनिट परीक्षण "TEST_F शैली" फ़ंक्शन लिखता है।
3.3.1) प्रत्येक परीक्षा के लिए, हम तय कर सकते हैं अन्यथा न कहा गया है कि क्या स्मृति रिसाव की जांच करते हैं या नहीं.यह SetIgnoreMemoryLeakCheckForThisTest() metohd के माध्यम से किया जाता है। नोट: इसे फिर से "रीसेट" करने की आवश्यकता नहीं है - यह जीटीएस्ट यूनिट परीक्षणों के तरीके के कारण स्वचालित रूप से अगले परीक्षण के लिए होगा (वे प्रत्येक फ़ंक्शन कॉल के लिए पहले सीटीओआर को कॉल करते हैं)।
3.3.2) इसके अलावा, किसी कारण से आप पहले से पता चलता है आपके परीक्षण "याद आती है" स्मृति के कुछ deallocations जाएगा और आप राशि पता है - आप में इस बात को ले जाने में दो कार्यों का लाभ ले सकते एक बार मेमोरी चेक करने पर विचार (जो, वैसे, परीक्षण के अंत में उपयोग की जाने वाली स्मृति की मात्रा से परीक्षण की शुरुआत में उपयोग में स्मृति की मात्रा को घटाकर "बस" द्वारा किया जाता है)।
// memoryLeakDetector.h:
#include "gtest/gtest.h"
extern int g_numOfExtraBytesAllocatedByGtestUponTestFailure;
// The fixture for testing class Foo.
class MemoryLeakDetectorBase : public ::testing::Test
{
// methods:
// -------
public:
void SetIgnoreMemoryLeakCheckForThisTest() { m_ignoreMemoryLeakCheckForThisTest= true; }
void SetIsFirstCheckRun() { m_isFirstTestRun = true; }
protected:
// You can do set-up work for each test here.
MemoryLeakDetectorBase();
// You can do clean-up work that doesn't throw exceptions here.
virtual ~MemoryLeakDetectorBase();
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
// Code here will be called immediately after the constructor (right
// before each test).
virtual void SetUp();
// Code here will be called immediately after each test (right
// before the destructor).
virtual void TearDown();
private:
void getSmartDiff(int naiveDiff);
// Add the extra memory check logic according to our
// settings for each test (this method is invoked right
// after the Dtor).
virtual void PerformMemoryCheckLogic();
// members:
// -------
private:
bool m_ignoreMemoryLeakCheckForThisTest;
bool m_isFirstTestRun;
bool m_getSmartDiff;
size_t m_numOfBytesNotToConsiderAsMemoryLeakForThisTest;
int m_firstCheck;
int m_secondCheck;
};
यहाँ और इस आधार वर्ग का स्रोत है:
नीचे हैडर आधार वर्ग है
// memoryLeakDetectorBase.cpp
#include <iostream>
#include <malloc.h>
#include "memoryLeakDetectorBase.h"
int g_numOfExtraBytesAllocatedByGtestUponTestFailure = 0;
static int display_mallinfo_and_return_uordblks()
{
struct mallinfo mi;
mi = mallinfo();
std::cout << "========================================" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "Total non-mmapped bytes (arena):" << mi.arena << std::endl;
std::cout << "# of free chunks (ordblks):" << mi.ordblks << std::endl;
std::cout << "# of free fastbin blocks (smblks):" << mi.smblks << std::endl;
std::cout << "# of mapped regions (hblks):" << mi.hblks << std::endl;
std::cout << "Bytes in mapped regions (hblkhd):"<< mi.hblkhd << std::endl;
std::cout << "Max. total allocated space (usmblks):"<< mi.usmblks << std::endl;
std::cout << "Free bytes held in fastbins (fsmblks):"<< mi.fsmblks << std::endl;
std::cout << "Total allocated space (uordblks):"<< mi.uordblks << std::endl;
std::cout << "Total free space (fordblks):"<< mi.fordblks << std::endl;
std::cout << "Topmost releasable block (keepcost):" << mi.keepcost << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << std::endl;
std::cout << std::endl;
return mi.uordblks;
}
MemoryLeakDetectorBase::MemoryLeakDetectorBase()
: m_ignoreMemoryLeakCheckForThisTest(false)
, m_isFirstTestRun(false)
, m_getSmartDiff(false)
, m_numOfBytesNotToConsiderAsMemoryLeakForThisTest(0)
{
std::cout << "MemoryLeakDetectorBase::MemoryLeakDetectorBase" << std::endl;
m_firstCheck = display_mallinfo_and_return_uordblks();
}
MemoryLeakDetectorBase::~MemoryLeakDetectorBase()
{
std::cout << "MemoryLeakDetectorBase::~MemoryLeakDetectorBase" << std::endl;
m_secondCheck = display_mallinfo_and_return_uordblks();
PerformMemoryCheckLogic();
}
void MemoryLeakDetectorBase::PerformMemoryCheckLogic()
{
if (m_isFirstTestRun) {
std::cout << "MemoryLeakDetectorBase::PerformMemoryCheckLogic - after the first test" << std::endl;
int diff = m_secondCheck - m_firstCheck;
if (diff > 0) {
std::cout << "MemoryLeakDetectorBase::PerformMemoryCheckLogic - setting g_numOfExtraBytesAllocatedByGtestUponTestFailure to:" << diff << std::endl;
g_numOfExtraBytesAllocatedByGtestUponTestFailure = diff;
}
return;
}
if (m_ignoreMemoryLeakCheckForThisTest) {
return;
}
std::cout << "MemoryLeakDetectorBase::PerformMemoryCheckLogic" << std::endl;
int naiveDiff = m_secondCheck - m_firstCheck;
// in case you wish for "more accurate" difference calculation call this method
if (m_getSmartDiff) {
getSmartDiff(naiveDiff);
}
EXPECT_EQ(m_firstCheck,m_secondCheck);
std::cout << "MemoryLeakDetectorBase::PerformMemoryCheckLogic - the difference is:" << naiveDiff << std::endl;
}
void MemoryLeakDetectorBase::getSmartDiff(int naiveDiff)
{
// according to some invastigations and assumemptions, it seems like once there is at least one
// allocation which is not handled - GTest allocates 32 bytes on the heap, so in case the difference
// prior for any further substrcutions is less than 32 - we will assume that the test does not need to
// go over memory leak check...
std::cout << "MemoryLeakDetectorBase::getMoreAccurateAmountOfBytesToSubstructFromSecondMemoryCheck - start" << std::endl;
if (naiveDiff <= 32) {
std::cout << "MemoryLeakDetectorBase::getSmartDiff - the naive diff <= 32 - ignoring..." << std::endl;
return;
}
size_t numOfBytesToReduceFromTheSecondMemoryCheck = m_numOfBytesNotToConsiderAsMemoryLeakForThisTest + g_numOfExtraBytesAllocatedByGtestUponTestFailure;
m_secondCheck -= numOfBytesToReduceFromTheSecondMemoryCheck;
std::cout << "MemoryLeakDetectorBase::getSmartDiff - substructing " << numOfBytesToReduceFromTheSecondMemoryCheck << std::endl;
}
void MemoryLeakDetectorBase::SetUp()
{
std::cout << "MemoryLeakDetectorBase::SetUp" << std::endl;
}
void MemoryLeakDetectorBase::TearDown()
{
std::cout << "MemoryLeakDetectorBase::TearDown" << std::endl;
}
// The actual test of this module:
TEST_F(MemoryLeakDetectorBase, getNumOfExtraBytesGTestAllocatesUponTestFailureTest)
{
std::cout << "MemoryLeakDetectorPocTest::getNumOfExtraBytesGTestAllocatesUponTestFailureTest - START" << std::endl;
// Allocate some bytes on the heap and DO NOT delete them so we can find out the amount
// of extra bytes GTest framework allocates upon a failure of a test.
// This way, upon our legit test failure, we will be able to determine of many bytes were NOT
// deleted EXACTLY by our test.
std::cout << "MemoryLeakDetectorPocTest::getNumOfExtraBytesGTestAllocatesUponTestFailureTest - size of char:" << sizeof(char) << std::endl;
char* pChar = new char('g');
SetIsFirstCheckRun();
std::cout << "MemoryLeakDetectorPocTest::getNumOfExtraBytesGTestAllocatesUponTestFailureTest - END" << std::endl;
}
अंत में, एक नमूना "GTest आधारित" इकाई परीक्षण वर्ग इस का उपयोग करता है बेस क्लास और मिस्ड डी-आवंटन का पता लगाने में सक्षम (या नहीं) सभी प्रकार के विभिन्न आवंटन और सत्यापन के लिए उपयोग और कई अलग-अलग पीओसी (अवधारणा का सबूत) दिखाता है।
// memoryLeakDetectorPocTest.cpp
#include "memoryLeakDetectorPocTest.h"
#include <cstdlib> // for malloc
class MyObject
{
public:
MyObject(int a, int b) : m_a(a), m_b(b) { std::cout << "MyObject::MyObject" << std::endl; }
~MyObject() { std::cout << "MyObject::~MyObject" << std::endl; }
private:
int m_a;
int m_b;
};
MemoryLeakDetectorPocTest::MemoryLeakDetectorPocTest()
{
std::cout << "MemoryLeakDetectorPocTest::MemoryLeakDetectorPocTest" << std::endl;
}
MemoryLeakDetectorPocTest::~MemoryLeakDetectorPocTest()
{
std::cout << "MemoryLeakDetectorPocTest::~MemoryLeakDetectorPocTest" << std::endl;
}
void MemoryLeakDetectorPocTest::SetUp()
{
std::cout << "MemoryLeakDetectorPocTest::SetUp" << std::endl;
}
void MemoryLeakDetectorPocTest::TearDown()
{
std::cout << "MemoryLeakDetectorPocTest::TearDown" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyNewAllocationForNativeType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForNativeType - START" << std::endl;
// allocate some bytes on the heap and intentially DONT release them...
const size_t numOfCharsOnHeap = 23;
std::cout << "size of char is:" << sizeof(char) << " bytes" << std::endl;
std::cout << "allocating " << sizeof(char) * numOfCharsOnHeap << " bytes on the heap using new []" << std::endl;
char* arr = new char[numOfCharsOnHeap];
// DO NOT delete it on purpose...
//delete [] arr;
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForNativeType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyNewAllocationForUserDefinedType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForUserDefinedType - START" << std::endl;
std::cout << "size of MyObject is:" << sizeof(MyObject) << " bytes" << std::endl;
std::cout << "allocating MyObject on the heap using new" << std::endl;
MyObject* myObj1 = new MyObject(12, 17);
delete myObj1;
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForUserDefinedType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyMallocAllocationForNativeType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyMallocAllocationForNativeType - START" << std::endl;
size_t numOfDoublesOnTheHeap = 3;
std::cout << "MemoryLeakDetectorPocTest::verifyMallocAllocationForNativeType - sizeof double is " << sizeof(double) << std::endl;
std::cout << "MemoryLeakDetectorPocTest::verifyMallocAllocationForNativeType - allocaitng " << sizeof(double) * numOfDoublesOnTheHeap << " bytes on the heap" << std::endl;
double* arr = static_cast<double*>(malloc(sizeof(double) * numOfDoublesOnTheHeap));
// NOT free-ing them on purpose !!
// free(arr);
std::cout << "MemoryLeakDetectorPocTest::verifyMallocAllocationForNativeType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyNewAllocationForNativeSTLVectorType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForNativeSTLVectorType - START" << std::endl;
std::vector<int> vecInt;
vecInt.push_back(12);
vecInt.push_back(15);
vecInt.push_back(17);
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForNativeSTLVectorType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyNewAllocationForUserDefinedSTLVectorType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForUserDefinedSTLVectorType - START" << std::endl;
std::vector<MyObject*> vecMyObj;
vecMyObj.push_back(new MyObject(7,8));
vecMyObj.push_back(new MyObject(9,10));
size_t vecSize = vecMyObj.size();
for (int i = 0; i < vecSize; ++i) {
delete vecMyObj[i];
}
std::cout << "MemoryLeakDetectorPocTest::verifyNewAllocationForUserDefinedSTLVectorType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyInPlaceAllocationAndDeAllocationForUserDefinedType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyInPlaceAllocationAndDeAllocationForUserDefinedType - START" << std::endl;
void* p1 = malloc(sizeof(MyObject));
MyObject *p2 = new (p1) MyObject(12,13);
p2->~MyObject();
std::cout << "MemoryLeakDetectorPocTest::verifyInPlaceAllocationAndDeAllocationForUserDefinedType - END" << std::endl;
}
TEST_F(MemoryLeakDetectorPocTest, verifyInPlaceAllocationForUserDefinedType)
{
std::cout << "MemoryLeakDetectorPocTest::verifyInPlaceAllocationForUserDefinedType - START" << std::endl;
void* p1 = malloc(sizeof(MyObject));
MyObject *p2 = new (p1) MyObject(12,13);
// Dont delete the object on purpose !!
//p2->~MyObject();
std::cout << "MemoryLeakDetectorPocTest::verifyInPlaceAllocationForUserDefinedType - END" << std::endl;
}
इस वर्ग के हेडर फाइल:
// memoryLeakDetectorPocTest.h
#include "gtest/gtest.h"
#include "memoryLeakDetectorBase.h"
// The fixture for testing class Foo.
class MemoryLeakDetectorPocTest : public MemoryLeakDetectorBase
{
protected:
// You can do set-up work for each test here.
MemoryLeakDetectorPocTest();
// You can do clean-up work that doesn't throw exceptions here.
virtual ~MemoryLeakDetectorPocTest();
// Code here will be called immediately after the constructor (right
// before each test).
virtual void SetUp();
// Code here will be called immediately after each test (right
// before the destructor).
virtual void TearDown();
};
आशा है कि यह उपयोगी है और मुझे पता है जो कुछ भी स्पष्ट नहीं है वहाँ है अगर बताएं।
चीयर्स,
गाय।
यह एक बहुत ही "साफ" समाधान है। मैंने कोशिश की (एक स्वीकार्य रूप से सरल मामले में) और यह उम्मीद के रूप में काम किया। +1 – sevaxx
मैं दूसरा करूंगा। यह वास्तव में एक साफ समाधान है।बस _CrtDumpMemoryLeaks फ़ंक्शन को कॉल करना Google परीक्षण के साथ काम नहीं करता है क्योंकि यह गलती से ढांचे में कुछ रिसाव की रिपोर्ट करता है, लेकिन यह समाधान उस समस्या से बचाता है। हालांकि आपको प्रत्येक टेस्ट केस के शीर्ष पर कक्षा का उदाहरण बनाने के लिए याद रखना होगा। – tathagata
मैंने सफलतापूर्वक इस समाधान को सैकड़ों परीक्षणों के एक बड़े सेट में जोड़ा। आप एक क्लाइंट वैरिएबल के रूप में एक पॉइंटर जोड़ सकते हैं और इसे TEST_METHOD_INITIALIZE में आवंटित कर सकते हैं और इसे TEST_METHOD_CLEANUP में हटा सकते हैं। इस तरह यह प्रति बार TEST_CLASS – SecsAndCyber