Как проверить наличие утечек памяти в крупномасштабном приложении c++ Linux?
в настоящее время я работаю над крупномасштабным проектом приложения (написанным на c++), который начался с нуля некоторое время назад, и мы достигли момента, когда необходимо сделать облаву на утечки памяти.
приложение работает на Ubuntu Linux, имеет много мультимедийного контента и использует OpenGl, SDL и ffmpeg для различных целей, включая 3D-рендеринг, windows, воспроизведение аудио и фильмов. Вы можете думать об этом как о видеоигре, хотя это не так, но обязанности приложения можно было бы упростить, рассматривая его как видеоигру.
в настоящее время я немного невежествен в определении того, есть ли у нас все еще утечки памяти или нет. В прошлом мы уже идентифицировали некоторые из них и удалили. В эти дни, однако, приложение почти завершено, и тесты, которые мы провели, дают мне результаты, которые я не могу точно выяснить.
Первое, что я сделал, это попытался запустить приложение через Valgrind... к сожалению, тогда сбой приложения при запуске в среде valgrind. Сбой в "недетерминированном", так как он падает в разных местах. Поэтому я отказался от Valgrind, чтобы легко определить источник потенциальных утечек, и в конечном итоге использовал две команды Linux: free и top.
free используется для зондирования использования системной памяти во время работы приложения
верхняя часть используется с опцией '- p', чтобы проверить использование памяти процесса приложения в то время как бегущий.
верхняя и свободная форма вывода сбрасывается в файлы для последующей обработки. Я составил два графика с данными, которые связаны в нижней части вопроса.
тестовый случай очень прост: данные о памяти зондируются после того, как приложение уже запущено и ждет команды. Затем я запускаю последовательность команд, которая постоянно делает одно и то же. Ожидается, что приложение загрузит много мультимедийных данных в ОЗУ, а затем загрузите его.
к сожалению, график не показывает мне то, что я ожидал. Использование памяти растет через 3 различных шага, а затем останавливается. Память, по-видимому, никогда не освобождается, что намекало мне на огромную утечку памяти. это было бы прекрасно, поскольку это означало бы, что мы, скорее всего, не освобождаем память, съеденную средствами массовой информации.
но после первых трех шагов... использование памяти стабильно... там больше нет огромных ступеней... просто небольшое вверх и вниз, которые соответствуют ожидаемой загрузке и выгрузке данных. Неожиданным здесь является то, что данные, которые должны быть загружены/выгружены, составляют сотые доли мегабайт ОЗУ, а вместо этого вверх и вниз составляют всего несколько мегабайт (скажем, 8-10 МБ).
в настоящее время я довольно невежествен в интерпретации этих данных.
У кого-нибудь есть подсказки или предложения? Что я упускаю? Является ли метод, который я использую для проверки наличия макроскопических утечка памяти полностью неправильная? Знаете ли вы какой-либо другой (желательно бесплатный) инструмент, кроме Valgrind для проверки утечек памяти?
7 ответов
прежде всего...
и мы достигли точки, когда необходимо сделать облаву проверок на утечку памяти.
Это, на самом деле, это методологии. Правильность должна быть основная цель любой части программного обеспечения, а не запоздалой мысли.
я предположу, что теперь вы понимаете это и насколько проще было бы определить проблемы, если бы вы запускали инструментальный модульный тест при каждой фиксации.
Итак, что теперь делать ?
-
обнаружение во время выполнения:
- попробуйте заставить Valgrind работать, у вас, вероятно, есть некоторые экологические проблемы
- попробовать Асан, ThreadSan и MemSan; они не тривиальны для настройки под Linux, но о, так впечатляет!
- попробуйте инструментальные сборки:tcmalloc включает в себя куча-контролера
- ...
-
обнаружение времени компиляции:
- включить предупреждения (желательно с
-Werror
) (не относится к вашему вопросу) - используйте статический анализ, например из Clang, он может обнаружить непарные процедуры распределения
- ...
- включить предупреждения (желательно с
-
человеческий обнаружения:
- обзоры кода: убедитесь, что все ресурсы распределяются в классах RAII
- ...
Примечание: использование только классов RAII помогает удалить утечки памяти, но не помогает с висячими ссылками. К счастью, обнаружение висячих ссылок-это то, что делает ASan.
и как только вы исправили все проблемы, убедитесь, что это становится частью процесса. Изменения должны быть рассмотрены и проверены, всегда, так что тухлые яйца отбираются немедленно, а не уходить, чтобы вонять кодовой базой.
вместо того, чтобы отказаться от Valgrind, вы должны вместо этого работать с ними и попытаться
- избавиться от ошибок, с которыми вы столкнулись в Valgrind
- получите ваше приложение тщательно протестировано и отлажено с обновленным Valgrind.
говоря, что вы отказались от Valgrind, который на решение вашей проблемы на самом деле не помогает...
Valgrind-это инструмент, который мы все используем для проверки утечек памяти и проблем с потоками Линукс.
в конце концов, определенно лучше инвестировать время в выяснение "почему Valgrind не работает с моим приложением", а не искать альтернативные решения. Valgrind-проверенный и проверенный инструмент, но не идеальный. И это намного превосходит альтернативные методы.
страница Valgrind говорит, что лучше отправить ошибки Bugzilla, но на самом деле лучше спросить об этом https://lists.sourceforge.net/lists/listinfo/valgrind-users если кто-нибудь видел такие вопросы раньше и что делать в такой ситуации. Худший сценарий-они скажут вам подать ошибку в bugzilla или подать ее сами.
вы, вероятно, хотите посмотреть на отчет.
и вы просто можете начать с очень простых примеров, чтобы почувствовать, какие отчеты valgrind могут быть несколько подробными. Рассмотрим этот упрощенный пример, где valgrind точно, что и сколько отсутствует:
edd@max:/tmp$ cat valgrindex.cpp
#include <cstdlib>
int main() {
double *a = new double[100];
exit(0);
}
edd@max:/tmp$ g++ -o valgrindex valgrindex.cpp
edd@max:/tmp$ valgrind ./valgrindex
==15910== Memcheck, a memory error detector
==15910== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==15910== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==15910== Command: ./valgrindex
==15910==
==15910==
==15910== HEAP SUMMARY:
==15910== in use at exit: 800 bytes in 1 blocks
==15910== total heap usage: 1 allocs, 0 frees, 800 bytes allocated
==15910==
==15910== LEAK SUMMARY:
==15910== definitely lost: 0 bytes in 0 blocks
==15910== indirectly lost: 0 bytes in 0 blocks
==15910== possibly lost: 0 bytes in 0 blocks
==15910== still reachable: 800 bytes in 1 blocks
==15910== suppressed: 0 bytes in 0 blocks
==15910== Rerun with --leak-check=full to see details of leaked memory
==15910==
==15910== For counts of detected and suppressed errors, rerun with: -v
==15910== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
edd@max:/tmp$
результаты free
и top
не будет полезно для вас. Я сожалею, что вы приложили усилия для построения графиков для их результатов. Я дал хорошее объяснение, почему они бесполезны в подобной теме здесь:стабильность памяти приложения C++ в Linux.
Я также соглашусь с другими ответами здесь, что вы, вероятно, должны определить приоритет устранения неполадок, с которыми вы сталкиваетесь в Valgrind. Valgrind считается очень стабильным на данный момент, и я лично запускаю довольно сложные многопоточные мультимедиа SDL / OpenGL / etc. приложения через него без проблем. Гораздо более вероятно, что рабочая среда Valgrind подвергает вероятной нестабильности в вашем приложении. Сбой звучит как сбой состояния гонки потоков, хотя это также может быть повреждение кучи / памяти.
то, что вы можете попросить, это совет о том, как отлаживать приложение, которое сбой из рабочей среды Valgrind (что я не знаю ответ).
проблема с free и top в том, что они могут показать вам проблему, но они мало помогают в устранении проблемы. Из 100 или 1000 строк кода, которые выделяют память, какие из них протекают? Здесь помогает вальгринд.
Если это для компании с бюджетом для инструментов, вы можете посмотреть на purify или другие коммерческие инструменты.
для полноты я упомяну консервативный распределитель памяти для сбора мусора Бема (который работает для C и c++ код.) Вы можете отключить GC и использовать GC_Free (), и он станет инструментом обнаружения утечки. Или вы можете оставить GC включенным для автоматического освобождения памяти, когда она больше не используется.
все зависит от того, какой аллокатор, который вы используете. Распределители libc (malloc, calloc, realloc) и распределители C++ (new, delete), вероятно, используют трюк оптимизации, включающий не освобождение памяти обратно в ОС. Видите ли, если вы попросите у Мэллока немного памяти, используйте ее, а затем освободите, она не обязательно будет возвращена в ОС. Скорее, если я попрошу Мэллока о памяти, он (большинство лилий) получит гораздо больше, чем необходимо (из-за границ страницы). Так, в следующий раз. нужно больше памяти, Мэллок просто сидит. То же самое и со свободой. Память, вероятно, просто добавляется в пул памяти mallocs, из которого извлекаются более поздние выделения.
Итак, ваши приложения сначала несколько mallocs ставят память очень высоко, но затем пул достаточно велик, чтобы вместить будущие распределения.
в дополнение к использованию valgrind
вы также можете рассмотреть возможность использования консервативный GC Бема; вы, вероятно, хотите скомпилировать его и настроить в качестве детектора утечки памяти.
и вы можете даже осмелиться использовать GC Бема в качестве основного распределителя памяти.
кстати, глядя на /proc/1234/maps
может помочь вам (где 1234-это PID процесса).