LINUX.ORG.RU

Утечки памяти в C++


0

0

Поделитесь пожалуйста, чем вы отлавливаете утечки памяти в программах на C/C++? Я вот попробовал valgrind, но его сообщения меня, честно говоря, ставят в абсолютный тупик :-( Утечки у меня в программе есть 100% (видно через ps по ходу ее работы), только вот ругань идет абсолютно невнятная, то на libc, то еще на что-нибудь... Мне сложновато разобраться в этом жутком потоке информации, может есть программы пусть и по-тупее, но попроще? Или как-нибудь это можно сделать своими хитростями? В общем, help me please, не знаю что и делать...

anonymous

А чья прога ваша или чужая ? Может легче переписать.
С утечками трудно. 

anonymous
()
Ответ на: комментарий от anonymous

Прога моя. Переписать ее сейчас нереально, она весьма большая :-( В принципе, виноваты во всем этом только я и моя наивность: гром грянул внезапно, а креститься нужно было раньше :-) Я вот думаю, надо переопределить new и delete для отладки и вести в них тотальный учет по какому адресу, сколько и каким образом (например, new []) выделялась и освобождалась память в каждом классе, а потом выводить статистику.

anonymous
()
Ответ на: комментарий от anonymous

разберись с valgrind и не парь людям мозги.
Есть еще mpatrol, там как раз переопределено.
Если ты внимательно посмотришь в сообщения valgrind, то увидишь кроме всякий libc и ссылки на свои файлы с указанем функции и строки.
valgrind очень хорошая и простая прога.

kavich
()
Ответ на: комментарий от kavich

>Если ты внимательно посмотришь в сообщения valgrind, то увидишь кроме всякий libc и ссылки на свои файлы с указанем функции и строки.

Да, естественно я видел такие строки. Но они начинаются после длинного потока ругани на libc и libGL (от NVidia; да, я читал FAQ и знаю о возможных проблемах, но там кое-что другое). Концы того, что касается моей программы, тоже уходят в libGL и этот поток ругани бесконечен. Потом мне сообщают, что зафиксировано более 30000 (!) потенциальных ошибок и говорят "досвиданья". Приду домой -- покажу куски лога. То есть, до информации о *реальных* проблемах в *моей* программе докопаться почти невозможно.

Я не парю никому мозги, а лишь пытаюсь докопаться до истины :-)

В общем, мне нужно отладить именно мою программу, а не обсасывать подключенные к ней библиотеки, эмулируя процессорные инструкции (как это делается в valgrind). Я хочу знать где и сколько было выделено и освобождено памяти мной. Вот я и спрашиваю, как это делают другие.

anonymous
()
Ответ на: комментарий от anonymous

>Я хочу знать где и сколько было выделено и освобождено памяти мной. Вот я и спрашиваю, как это делают другие. 

Для учета баланса new/delete моих собственых объектов я использую следующий код ... 
-------------Trace_Ptr.hpp--------------------
#ifndef _Trace_Ptr_hpp_
#define _Trace_Ptr_hpp_

using namespace std;

#include <typeinfo> 
#include <iostream>

#define TRACE_POINTER          virtual public TracePointer
#define trace_ptr_for_each(T)  for(TracePointer* tp=T->GetFirst();tp;tp=tp->GetNext())

class TracePointer {

  static int           num_ptr;
  static TracePointer* first_ptr;
  static TracePointer* last_ptr;
  static long          num_new;
  static long          num_delete;
         TracePointer* prev_ptr;
         TracePointer* next_ptr; 

protected:
  
  static long   all_objects_size;  
  
  void          SetFirst(TracePointer*);
  void          SetLast(TracePointer*);
  void          SetNext(TracePointer*);
  void          SetPrev(TracePointer*);
  
public:
                TracePointer(); 
  virtual      ~TracePointer();
  int           GetNumPointers();
  long          GetAllObjectsSize();
  long          GetNumCallConstructors();
  long          GetNumCallDestructors();
  
  TracePointer* GetFirst();
  TracePointer* GetLast();
  TracePointer* GetNext();
  TracePointer* GetPrev();
  TracePointer& operator = (TracePointer& tp);
};

#endif // Trace_Ptr

-------------Trace_Ptr.cpp--------------------
#include "libUTL/Trace_Ptr.hpp"

/* +++ TracePointer conctructor +++ */
TracePointer::TracePointer() { 
    if(!first_ptr) {
       first_ptr = last_ptr = this;
       prev_ptr  = next_ptr = 0L;
    } else {
       prev_ptr = last_ptr;
       last_ptr = this;
       prev_ptr->SetNext(this);
       next_ptr = 0L;
    }
    num_new++;
    num_ptr++;
}

/* --- TracePointer dectructor --- */
TracePointer::~TracePointer() {
   
    if(prev_ptr) {
       prev_ptr->SetNext(next_ptr);
    }
       
    if(next_ptr) {
       next_ptr->SetPrev(prev_ptr);
    }
    
    if(this == first_ptr) {
        if(next_ptr)
           first_ptr = next_ptr;
    }
    
    if(this == last_ptr) {
       if(prev_ptr)
          last_ptr = prev_ptr;
    }
    
    
    num_delete++;
    num_ptr--;
    
    if(!num_ptr)
       last_ptr = first_ptr  = 0L;  
}

int TracePointer::GetNumPointers() {
    return num_ptr;
}

long TracePointer::GetAllObjectsSize() {
    return all_objects_size;
}

TracePointer* TracePointer::GetFirst() {
 return first_ptr;
}

TracePointer* TracePointer::GetLast() {
 return last_ptr;
}

TracePointer* TracePointer::GetNext() {
 return next_ptr;  
}

TracePointer* TracePointer::GetPrev() {
 return prev_ptr;  
}

void TracePointer::SetFirst(TracePointer* btp) {
 first_ptr = btp;
}

void TracePointer::SetLast(TracePointer* btp) {
 last_ptr = btp;
}

void TracePointer::SetNext(TracePointer* btp) {
 next_ptr = btp;
}

void TracePointer::SetPrev(TracePointer* btp) {
 prev_ptr = btp;
}

long TracePointer::GetNumCallConstructors() {
 return num_new;
}

long TracePointer::GetNumCallDestructors() {
 return num_delete;
}


int  TracePointer::num_ptr=0;
long TracePointer::all_objects_size=0;

TracePointer* TracePointer::first_ptr=0L;
TracePointer* TracePointer::last_ptr=0L;
long TracePointer::num_new=0L;
long TracePointer::num_delete=0L;

-------------------------------------------------------

Как это используется:

Каждый собственный класс наследуем от TRACE_POINTER
например:

class  TextFrame :            public FXText,
virtual public FXPacker 
#if DEBUGLEVEL == 3 
, TRACE_POINTER
#endif // DEBUGLEVEL
{
public:
....
};

---------------------------------------------------------

После чего в процессе работы имеем автоматически поддерживаемый двунаправленный (что вообще говоря излишество) связанный список
объектов Trace_Ptr, который можно распарсить примерно таким
вот кодом:
----------------------------
trace_ptr_for_each([указатель_на_любой_объект_содержащий_Trace_ptr]) {
 ....
 sprintf(Buff,"%s",typeid(*tp).name());
  ....
 DemangleBuff = __cxiv1::__cxa_demangle(Buff,DemangleBuff,.....);
}

----------------------------

Конечно тупой new char[100] таким кодом не поймать но лики для _собственных_ объектов, коих у меня бывает по несколько тыщ ловились на ура - код писался несколько лет назад а потом успешно таскался из проекта в проект ....


PS: А по мелочам действительно рулит valgrind (у меня кстати с его использованием проблемы из за эмуляции исполнения и низкой скорости - на реальной числодробильной задаче он не годится - помрешь ждать результат)
 

sS ★★★★★
()

Если течет чистый С++ (без malloc free), то отца русской димократии натолкнет на мысль _очень_ простой тул LeakTracer... Я с ним разобрался к концу первого месяца миграции на линукс. ;)

tarle
()
Ответ на: комментарий от tarle

Действительно, LeakTracer рульная вещь! Только когда я стал QT использовать, он мне уже не помог :( Так что в таком случае лучше советую тоже valgrind.

RomanU
()
Ответ на: комментарий от anonymous

Насчет хреновой работы с NVidia драйверами. Нужно просто заменить libGL.so и libGL.so.1 на старую не инвидевскую версию, а libGL.so.1.0.хххх - убрать подальше. Это радикальный способ, но он должен работать всегда. У меня (SuSE 9.0) достаточно скопировать libGL.so.1 в /usr/local/lib и все программы будут линковаться с ней.

P.S. нвидивский инсталлер копирует старые libGL.so и libGL.so.1 в /usr/lib/GL.

Evgeny_I
()
Ответ на: комментарий от anonymous

> Я хочу знать где и сколько было выделено и освобождено памяти мной. Вот я и спрашиваю, как это делают другие.

Другие делают это как обычно: путем чтения документации.

Чтобы не мешали сообщения о том что твориться внутри драйверов от nvidia нужно написать suppress файл для valgrind'а в котором будут перечислены все сообщения которые не надо показывать. Как писать этот файл написано в документации valgrind'а. Я когда-то так сделал и не знал никаких проблем пока не сменил видеокарту на ATI...

asso_w
()
Ответ на: комментарий от anonymous

Не знаю что и посоветовать. Личный опыт такой: разбирался с чужими исходниками. правда в структурной программе.
Определил базовые элементы, посмотрел где сколько выделяется и 
как удаляется - сразу нашел ошибку именно там.
Дальше пополз вверх по иерархии.
Особое внимание нужно уделять на выделение памяти под массив
данных, соответственно удаление. Чаще всего там ошибки.
Еще ошибки при неточном выделении под количество элементов.
По поводу программ сказать ничего не могу. Пока мне слава богу
с ними не приходилось возиться.
А что делает прога падает в core ?
Вообще не рекомендуют переопределять new и delete.
Может лучше создать макрос с отладочной печатью сколько выделяется
 и сколько удаляется и создать 2 глобальные переменные счетчики,
 которые будут увеличиваться и уменьшаться, еще неплохо было-бы
в него задавать имя класса::функции. И вставить его там 
где new/delete.
Хотел написать примерчик с выделением/удалением памяти
под данные **p_p_ch, но думаю что Вы сами знаете хорошо.
Еще могут быть проблемы если Вы сами писали такие классы как
String, где может происходить увеличение памяти.
Или классы используемые в исключениях.
Проверьте правильность работы функций  " operator=() "
Еще может быть такая штука: вы возвращаете указатель на 
выделеную память работаете с ним и забываете про него. 
Я поделился опытом при отладке драйверов и приложений без 
дебагера и средств отладки, как Вы просили.
 Извините если что не так.

anonymous
()

Всем огромное спасибо! Теперь у меня достаточно информации для переваривания, надеюсь лики в моих программах останутся в прошлом :-)

2 sS: Спасибо за код, просто и со вкусом :-) А вот у меня вопрос про __cxiv1::__cxa_demangle(). Подозреваю, что это нечто непортабельное или даже gcc-only? Где можно почитать об этом __cxiv1?

anonymous
()
Ответ на: комментарий от anonymous

2 anonymous (*) (19.05.2004 16:33:00): Спасибо за развернутый ответ. В принципе, я всегда внимательно следил за вышеописанными местами в программе, но человеку свойственно ошибаться. Сейчас мне приходится искать утечки в очень большом объеме кода, поэтому без автоматизации процесса уже не обойтись никак. В общем, всем советую следить за утечками с *самого* начала разработки, иначе однажды вам придется так же тяжко как мне ;-)

anonymous
()
Ответ на: комментарий от anonymous

>2 sS: Спасибо за код, просто и со вкусом :-) А вот у меня вопрос про __cxiv1::__cxa_demangle(). Подозреваю, что это нечто непортабельное или даже gcc-only? Где можно почитать об этом __cxiv1?

http://gcc.gnu.org/onlinedocs/libstdc++/18_support/howto.html#6

Это не портабельно в сторону старых версий. icc 8.х и gcc 3.1+ это умеют. Раньше я пользовался собственным парсером для каждого из используемых компиляторов ... на самом деле этот кусок кода нужен только для того чтобы красиво вывести список объектов - а лики можно ловить и БЕЗ деманглинга ....

sS ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.