Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 3 апреля!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Давно заметил, что всё-таки языки С/C++ это не самый лучший вариант программирования под Windows. Сейчас появилась масса более современных и удобных языков, например тот же Python - кроссплатформенный язык, очень легок в изучение. Я его изучил буквально за несколько дней по этому курсу - ссылка. Автор постарался, там видеоуроки на удивление легкие и понятные.

Как обнаружить утечку памяти

Введение

Разработка больших приложений, оперирующих большими объемами информации, чревата неравильным распределением памяти. Суть проблемы состоит в том, что если мы выделили участок памяти, а затем освободили не весь выделенный объем, то образуются блоки памяти, которые помечены как занятые, но на самом деле они не используются. При длительной работе программы такие блоки могут накапливаться, приводя к значительному расходу памяти.

Для обнаружения подобных ошибок создано специализированное программное обеспечение (типа BoundsChecker от Numega), однако чаще бывает удобнее встроить механизм обнаружения утечки в свои проекты. Поэтому метод должен быть простым, и в то же время как можно более универсальным. Кроме того, не хотелось бы переписывать годами накопленные мегабайты кода, написанного и отлаженного задолго до того, как вам пришло в голову оградить себя от ошибок. Так что к списку требований добавляется стандартизация, т.е. нужно каким-то образом встроить защиту от ошибок в стандартный код.

Предлагаемое решение основывается на перегрузке стандартных операторов распределения памяти new и delete. Причем перегружать мы будем глобальные операторы new|delete, т.к. переписать эти операторы для каждого разработанного ранее класса было бы очень трудоемким процессом. Т.о. после перегрузки нам нужно будет только отследить распределение памяти и, соответственно, освобождение ее в момент завершения программы. Все несоответствия - ошибка.

Реализация

Проект написан на Visual C++, но переписать его на любой другой диалект С++ не будет слишком сложной задачей. Во-первых, нужно переопределить стандартные операторы new и delete так, чтобы это работало во всех проектах. Поэтому в stdafx.h добавляем следующий фрагмент:

 
 
 
 
 
       #ifdef _DEBUG
 
       inline void * __cdecl operator new(unsigned int size,
 
                                          const char *file, int line)
 
       {
 
       };
 
 
 
       inline void __cdecl operator delete(void *p)
 
       {
 
       };
 
       #endif
 
 
 
 
 
 

Как видите, переопределение операторов происходит в блоке #ifdef/#endif. Это ограждает наш код от влияния на релиз компилируемой программы. Вы, наверное, заметили, что теперь оператор new имеет три параметра вместо одного. Два дополнительных параметра содержат имя файла и номер строки, в которой выделяется память. Это удобно для обнаружения конкретного места, где происходит ошибка. Однако код наших проектов по-прежнему ссылается на оператор new, принимающий один параметр. Для исправления этого несоответствия нужно добавиить следующий фрагмент

 
 
 
 
 
       #ifdef _DEBUG
 
       #define DEBUG_NEW new(__FILE__, __LINE__)
 
       #else
 
       #define DEBUG_NEW new
 
       #endif
 
       #define new DEBUG_NEW
 
 
 
 
 
 

Теперь все наши операторы new будут вызываться с тремя параметрами, причем недостающие параметры подставит препроцессор. Конечно, пустые переопределенные функции ни в чем нам не помогут, так что давайте добавим в них какой-нибудь код:

 
 
 
 
 
       #ifdef _DEBUG
 
       inline void * __cdecl operator new(unsigned int size,
 
                                          const char *file, int line)
 
       {
 
 	      void *ptr = (void *)malloc(size);
 
 	      AddTrack((DWORD)ptr, size, file, line);
 
 	      return(ptr);
 
       };
 
       inline void __cdecl operator delete(void *p)
 
       {
 
 	      RemoveTrack((DWORD)p);
 
 	      free(p);
 
       };
 
       #endif
 
 
 
 
 
 

Для полноты картины нужно переопределить операторы new[] и delete[], однако никаких существенных отличий здесь нет - творите!

Последний штрих - пишем функции AddTrack() и RemoveTrack(). Для создания списка используемых блоков памяти будем использовать стандартные средства STL:

 
 
 
 
 
       typedef struct {
 
 	      DWORD	address;
 
 	      DWORD	size;
 
 	      char	file[64];
 
 	      DWORD	line;
 
       } ALLOC_INFO;
 
 
 
       typedef list<ALLOC_INFO*> AllocList;
 
 
 
       AllocList *allocList;
 
 
 
       void AddTrack(DWORD addr,  DWORD asize,  const char *fname, DWORD lnum)
 
       {
 
 	      ALLOC_INFO *info;
 
 
 
 	      if(!allocList) {
 
 		      allocList = new(AllocList);
 
 	      }
 
 
 
 	      info = new(ALLOC_INFO);
 
 	      info->address = addr;
 
 	      strncpy(info->file, fname, 63);
 
 	      info->line = lnum;
 
 	      info->size = asize;
 
 	      allocList->insert(allocList->begin(), info);
 
       };
 
 
 
       void RemoveTrack(DWORD addr)
 
       {
 
 	      AllocList::iterator i;
 
 
 
 	      if(!allocList)
 
 		      return;
 
 	      for(i = allocList->begin(); i != allocList->end(); i++)
 
 	      {
 
 		      if((*i)->address == addr)
 
 		      {
 
 			      allocList->remove((*i));
 
 			      break;
 
 		      }
 
 	      }
 
       };
 
 
 
 
 
 

Перед самым завершением программы наш список allocList содержит ссылки на блоки памяти, котороые не были освобождены. Все, что нужно сделать - вывести эту информацию куда-нибудь. В нашем проекте мы выведем список неосвобожденных участков памяти в окно вывода отладочных сообщений Visual C++:

 
 
 
 
 
    void DumpUnfreed()
 
    {
 
 	   AllocList::iterator i;
 
 	   DWORD totalSize = 0;
 
 	   char buf[1024];
 
 
 
       if(!allocList)
 
 	      return;
 
 
 
       for(i = allocList->begin(); i != allocList->end(); i++) {
 
 	      sprintf(buf, "%-50s:\t\tLINE %d,\t\tADDRESS %d\t%d unfreed\n",
 
 		      (*i)->file, (*i)->line, (*i)->address, (*i)->size);
 
 	      OutputDebugString(buf);
 
 	      totalSize += (*i)->size;
 
       }
 
       sprintf(buf, "--------------------------------------------------\n");
 
       OutputDebugString(buf);
 
       sprintf(buf, "Total Unfreed: %d bytes\n", totalSize);
 
       OutputDebugString(buf);
 
    };
 
 
 
 
 
 

Надеюсь, этот проект сделает ваши баг-листы короче, а программы устойчивее. Удачи!



<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS