Monday, March 8, 2010

Охота на утечки

Давно не сталкивался с утечками памяти, но на днях наш плюсовый демон начал течь. При чем valgrind.memcheck не показывает ничего внятного - сервер долго стартует, большую нагрузку дать не получается - watchdog, который проверяет на зависание сервера при такой нагрузке под валгриндом - резво прибивает сервер.. При выходе из под маленькой нагрузке - ликов нет.
Течет сильно - 7 гигов за 3 дня. Хотя раньше в рабочем режиме больше 1 гига не требовал (был запущен и по 2 недели без перезапусков).

Подумалось, что либо циклические ссылки, либо данные с какого то контейнера не удаляются.
Ну что же решил заюзать старый добрый google perf tools, что бы сделать снимок памяти в момент работы. Хм. Под ubuntu amd64 почему то только старая версия. И она виснет когда приложение разпаралеливается (хотя google perf tools - специально сделано для многопоточных приложений).
В дебагере видно, что все потоки висят на спинлоках.

Решил обновиться до последней версии - но компились версию с сайта не хотелось. Нашел пакеты на packages.debian.org - и на удивление всё установилось без проблем.
Но при входе в многопоточный режим работы демона - опять зависание.
Да и сам демон загружается под 15-20 минут - так как взял базу с продакшена, а там данных очень много.

Поискал какие то хорошие аллокаторы для отладки - того что мне надо было (google perf tools - heap peofiler, который бы не вис) не было.
Ну а может в valgrind.memcheck есть какие то ключи, что бы дампить состояние памяти не в конце работы программы, а в середине? Нет. Но... Есть valgrind.massif - который этим и занимается. Ура!

Безжалостно очистив базу от данных (так что бы хоть нагрузочные тесты могли работать, а они требуют настоящую игру и хоть немного валидных данных), стал ковырять valgrind.massif.
Запустил - прогнал нагрузочные тесты - всё работает, ничего не течет. Очень интерестно.
В общем тулзы мне никакого ответа не дали - наверно синтетическая нагрузка не провоцировала ошибку, влекущую утечку памяти. Надо включить моск;)
К сожалению с последней стабильной выливки очнь много кода поменялось и я тому виной: делал рефакторинг затронув почти половину всего кода;)

Думал задампить память и посмотреть что там, но разбираться в 7 гиговом файлике не хотелось. И тут - пришла безумная идея - анализировать статистику по демонам (благо текущих демона у нас 3 в кластере) которую мог собрать как с операционки, так и с внутреннего мониторинга демонов.

Совершенно случайно увидел пропорцию между количеством утекшей памяти и количеством выполненных команд одного типа. Команда была старой, но я вспомнил, что в эту выливку мы её стали намного активнее использовать. Проверил пропорцию на калькуляторе (питон в шеле;) - всё совпадает. Эта твать жрет память.

Вся проблема в том, что схожая команда использовалась только в биллинге, на неё нагрузочного теста не было. А недавно сделали схожую команду, которая использует схожие фичи что и первая команда, но выполняется в разы больше раз. Используется она сугубо в соц сетях и в нагрузочный тест тоже добавлена не была. Вот такая нехорошая ситуация.

Добавив в нагрузочный тест эту команду - за 10 секунд отожрал 50% памяти своего ноута. А дальше дело техники - даже не стал использовать massif - почитал код - нашел место где может не создаваться циклическая ссылка - сменил policy с Strong на Weak - сбилдил - запустил тест - ура, жучек умер.

Ну и какие я сделал выводы из убитого дня на дебаг? Собственно вот:

  1. Надо чаще прогонять тесты на утечки памяти
  2. Даже если утечка маленькая - в будущем код может начать использоваться активнее - тогда маленькая утечка превратится в сильную головную боль
  3. Надо иметь репрезентативную базу по функционалу аналогичную продакшену, но с меньшим объемом данных. Без этого отладка под дебагерами памяти будет практически невозможна
  4. Ещё раз убедился - отсутствие голых указателей не гарантирует отсутствие утечек (это касается как java и c#, так и если использовать умные указатели в С++)
  5. Когда ничего не помогает - случайность может решить всё
  6. Случайности не случайны (c) =) - не ища какие то закономерности в статистиках я бы не нашел их, хотя я и не надеялся что то найти

No comments:

Post a Comment