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