Memory leaks (ламерские будни)

May 05, 2008 23:53

  • Всё началось с безумного количества мемори ликов COM (ATL), которые лились в output по выгрузке приложения
  • DevPartner (BoundsChecker) показал что-то в большом количеством не слишком внятного: какие-то утечки в _bstr_t и т.п. При этом вёл себя совершенно неприлично: всё норовил упасть и уронить студию.
  • Ко всякому классу, реализующему интерфейс было добавлен мембер вида

    class HostComClass
    {
    .....................
    private:
    class watcher_t
    {
    public:
    watcher_t() { OutputDebugString("started HostComClass"); }
    ~watcher_t() { OutputDebugString("finished HostComClass"); }
    } watcher;
    };

    В результате оказалось, что для части классов не зовутся деструкторы watcher_t. Насколько я понимаю, это говорит о наличии циклических ссылок
  • Гугл мне сказал, что есть такой дефайн _ATL_DEBUG_INTERFACES, который отслеживает вызовы AddRef | Release. Я добавил эту штуку к себе в проект и он начал падать с Access Violation, что согласно документации говорило о "наличии почти наверняка rerence-counting баге в клиентском коде". Падение происходило в строке вида BEGIN_COM_MAP(HostComClass). Тогда взял определение макроса BEGIN_COM_MAP и подставил его вместо самого макроса. Запустил. Упало теперь на другом макросе внутри BEGIN_COM_MAP. Такой макрос _ATL_DECLARE_GET_UNKNOWN(HostComClass). Расписал и его (определяет функцию GetUnknown()). Запустил. Упало на GetUnknown(). По выходу из него (после первого входа)
  • Пытался ставить брейк-поинты на AddRef и Release, но это темплейтные штуки в ATL. В результате образовывалось мильон брейк-поинтов (по одному на каждый инстанс), что просто затормозило студию до полной остановки. Поэтому брейкпоинты нужно ставить не на строчку, а на функцию. Почему-то не всегда это удаётся (текст нужно брать из содержимого стека при пошаговом дебаге). Alt-F9; Ctrl-B
    ATL::CComObjectCached >::QueryInterface(const _GUID &, void * *)
    ATL::CComObjectCached >::AddRef()
    ATL::CComObjectCached >::Release()

  • день ушёл на весь этот праздник. Чего-то удалось добиться, но ликов пока много


В общем, немного выводов
  • Конструкция watcher_t (можно включать по #ifdef, чтобы не загаживать output). Особенно интересен первый, кто не удаляется
  • Ставить грмотные брейкпоинты на AddRef, Release, QueryInterface. Особенно интересно наблюдать за первым, которого мы выцепили в предыдущем пункте
  • _ATL_DEBUG_INTERFACES (в моем случае бесполезно оказалось, но в принципе можно было бы делать наблюдения, как в предыдущем пункте, но совершая меньшее количество телодвижений)
  • Посмотреть, в исследуемом COM-классе данные-мемберы. Попробовать убрать в pimpl данные, которые могут включать интерфейсы. Правильнее даже наверное сказать Lazy pimple. довольно бессмысленно занятие, как теперь кажется

links, memory, atl, com, debugging, cpp, workaround

Previous post Next post
Up