Szoftver fejlesztoi es C++ halado technikak III - memory leak keresés

Qt-ben a hagyományos memória kezelési módszereken kívül választhatunk smartpointereket vagy rábízhatjuk egy objektum megszüntetését a "szülőjére". Kettőt egyszerre nem megy. Az utóbbinak van egy veszélye, mi van akkor ha elfelejtettük beállítani a szülőt? QObject *pObject = new QObject(this); helyett QObject *pObject = new QObject(); írtunk?

Probléma. Ha szerencsénk van használhatunk pl valgrind-et. http://www.cprogramming.com/debugging/valgrind.html Ha nincs, mert a platform nem támogatja, akkor ki kell valami okosat találni. Miután ezt a feladatot kaptam a meglévők mellé, pár napi keresgélés után (persze megtaláltam végül a kérdéses részt) megfogalmazódott bennem hogy kellene egy saját memória foglaltság nyilvántartót írni. Kicserélni a new/delete párosítást sajátra, a végén pedig kiiratni azokat a helyfoglalásokat, amelyek esetén nem alkalmaztunk delete-t, és ezáltal memória szivárgást kaptunk.

Guglizás után valóban találtam ilyen megoldásokat, de elég nehezen akadtam ténylegesen működő, lefordítható megoldásra.

http://www.linuxtopia.org/online_books/programming_books/c%2B%2B_practi…

Ennek felhasználásával elkészítettem a saját egyszerű megoldásomat:

Készítünk egy saját new operátort, amelynek átadjuk az igényelt memória méreten kívül a forrás fájl nevét és a sorszámot melyek mutatják hol történt a memória foglalás.

(Nem látom hogy a kód rendesen jelenne meg pedig használtam a code tag-et így itt van egy pastebin-es megoldás: http://pastebin.com/4i5rHRkP Innen érdemes inkább leszedni a kódot.)
//*******************************************************************
// mynew.h


#ifndef MYNEW_H
#define MYNEW_H

#include <cstddef>

void* operator new(std::size_t size, const char *__file__, unsigned int __line__);

void* operator new[](std::size_t size, const char *__file__, unsigned int __line__);

void operator delete(void *ptr);

void operator delete[](void *ptr);


#define new new (__FILE__, __LINE__)

#endif // MYNEW_H

Ez a megoldás meghív egy egyszerű lefoglalás/felszabadítás nyilvántartót. Ezt természetesen tovább lehet fejleszteni szál biztosra, singleton-ra, illetve hogy felismerje a new[] és delete problémáját.

//*******************************************************************
// mynew.cpp


#include "memorytracker.h"
#include "mynew.h"
extern MemoryTracker mTracker;

#undef new

void* operator new(std::size_t size, const char *__file__, unsigned int __line__) {
    void *ptr = malloc(size);
    mTracker.record_alloc(reinterpret_cast<unsigned int>(ptr),__file__,__line__);
    return ptr;
}

void* operator new[](std::size_t size, const char *__file__, unsigned int __line__) {
    void *ptr = malloc(size);
    mTracker.record_alloc(reinterpret_cast<unsigned int>(ptr),__file__,__line__);
    return ptr;
}

void operator delete(void *ptr)throw()
{
   mTracker.unrecord_alloc(reinterpret_cast<unsigned int>(ptr));
   free(ptr);
}

void operator delete[](void *ptr)throw()
{
   mTracker.unrecord_alloc(reinterpret_cast<unsigned int>(ptr));
   free(ptr);
}

Íme, itt a nyilvántartó:
//*******************************************************************
// memorytracker.h


#ifndef MEMORYTRACKER_H
#define MEMORYTRACKER_H

#include <QMap>
#include <QDebug>

class MemoryTracker {
public:
    ~MemoryTracker();
    void record_alloc(unsigned int ptr,const char *__file__,unsigned int __line__);
    void unrecord_alloc(unsigned int ptr);
private:
    QMap <int,QString>storage;
};

#endif // MEMORYTRACKER_H

//*******************************************************************
// memorytracker.cpp


#include "memorytracker.h"


void MemoryTracker::record_alloc(unsigned int ptr, const char *__file__, unsigned int __line__)
{
    storage.insert(ptr, QString(__file__) + " Line: " + QString().number(__line__));
}

void MemoryTracker::unrecord_alloc(unsigned int ptr)
{
    storage.remove(ptr);
}

MemoryTracker::~MemoryTracker()
{
    foreach (QString value, storage)qDebug() <<  value;
}

Hogyan működik?

Egyszerű dummy osztály ellenőrizni:
//*******************************************************************
// dummy.h


#ifndef DUMMY_H
#define DUMMY_H

class dummy
{
public:
    dummy();
};

#endif // DUMMY_H

//*******************************************************************
// dummy.cpp


#include "dummy.h"
#include "mynew.h" // fontos hogy utolso helyen legyen

dummy::dummy()
{
    int *ptr = new int [5]; // Memory leak
}

Főprogram:
//*******************************************************************
// main.cpp


#include "memorytracker.h"
#include "dummy.h"

MemoryTracker mTracker;

#include "mynew.h"


int main(int argc, char *argv[])
{
    int *p = new int[10]; // Ez ok, mert felszabadítjuk késöbb.
    dummy *d = new dummy; // biza ez is memory leak!
    delete[] p;
    return 0;
}

Futtatáskor gyönyörűen láthatjuk a problémás helyeket!

Ui.: abban a fájlban ahol le szeretnénk cserélni a saját megoldásunkra a new operatort, csak tegyük az include blokk VÉGÉRE a #include "mynew.h"-t. Persze lényegesen lassabb így a program, tehát csak tesztelési céllal érdemes használni.
Ui2.: Blackberry alatt valami gondja van, meg at kell neznem.

Ui3.: BB alatt implementálnom kellett a new és delete operátorokat eredeti paraméterekkel is (overloading). A fordítónak szüksége volt rá. Illetve le kellett cserélnem a Qt QMap-ot saját megoldásra, lásd lejjebb.

Ui4.: Átírtam hogy lehessen Qt gui alatt használni. QApplication-ből származtatnom kellett egy saját osztályt, melynek destruktorában listázom ki a problémás memória foglalásokat.

Letölteni a
git clone https://github.com/zhanisch/DetectingMemLeak.git

Vagy ha nincs telepítve git akkor a forrás kódot a fenti címet böngészve lehet megtekinteni.

Futtatva láthatjuk hol van gond:

Starting /home/zhanisch/documents/QtProjects/build-DetectingMemLeak-Desktop_Qt_5_0_2_GCC_32bit-Debug/DetectingMemLeak...
Not relased object at: 162713376, file: ../DetectingMemLeak/mainwindow.cpp, line: 19
Not relased object at: 162711392, file: ../DetectingMemLeak/mainwindow.cpp, line: 23
/home/zhanisch/documents/QtProjects/build-DetectingMemLeak-Desktop_Qt_5_0_2_GCC_32bit-Debug/DetectingMemLeak exited with code 0

Hozzászólások