Это немного гипотетически и сильно упрощено, но ...
Предположим, что программа будет вызывать функции, написанные третьими сторонами. Эти стороны можно считать не враждебными, но нельзя считать «компетентными». Каждая функция будет принимать некоторые аргументы, иметь побочные эффекты и возвращать значение. У них нет состояния, пока они не запущены.
Цель состоит в том, чтобы гарантировать, что они не могут вызвать утечку памяти, путем регистрации всех mallocs (и т.п.) и последующего освобождения всего после выхода из функции.
Это возможно? Это практично?
p.s. Для меня важной частью является обеспечение того, чтобы никакие распределения не сохранялись, поэтому способы устранения утечек памяти без выполнения этого для меня бесполезны.
Разве вы не можете просто заставить их выделить всю свою память в стеке? Таким образом, будет гарантировано освобождение после выхода из функции.
Лучшим решением, чем попытки регистрировать маллоки, могло бы быть изолирование функций при их вызове - предоставить им доступ к фиксированному сегменту памяти, а затем освободить этот сегмент, когда функция завершит работу.
Неограниченное и некомпетентное использование памяти может быть столь же разрушительным, как и вредоносный код.
Вы можете запустить сторонние функции в отдельном процессе и закрыть процесс, когда вы закончите использовать библиотеку.
Во-первых, вы должны предоставить точки входа для malloc()
и free()
и других пользователей. Поскольку этот код уже скомпилирован (верно?), Вы не можете полагаться на #define
для перенаправления.
Затем вы можете реализовать их очевидным образом и зарегистрировать, что они пришли из определенного модуля, связав эти процедуры с этими модулями.
Самый быстрый способ - это нет регистрации вообще.. Если объем используемой ими памяти ограничен, почему бы не выделить заранее всю «кучу», которая им когда-либо понадобится, и не написать из нее распределитель? Затем, когда это будет сделано, освободите всю "кучу", и все готово! Вы можете распространить эту идею на несколько куч, если это будет сложнее.
Если вам действительно нужно «вести журнал», а не создавать свой собственный распределитель, вот несколько идей. Во-первых, используйте хеш-таблицу с указателями и внутренней цепочкой. Другой вариант - выделить дополнительное пространство перед каждым блоком и поместить туда свою собственную структуру, содержащую, скажем, индекс, в вашу «таблицу журналов», а затем сохранить свободный список записей таблицы журналов (в виде стека, чтобы получить бесплатный или вернуть свободный - O (1)). Это требует больше памяти, но должно быть быстрым.
Это практично? Я думаю, что это так, пока приемлемо скоростное попадание.
Поскольку вы беспокоитесь об утечках памяти и говорите о malloc / free, я предполагаю, что вы находитесь на C. Я также предполагаю, основываясь на вашем вопросе, что у вас нет доступа к исходному коду сторонней библиотеки.
Единственное, что я могу придумать, - это изучить потребление памяти вашим приложением до и после вызова, зарегистрировать сообщения об ошибках, если они разные, и убедить стороннего поставщика исправить любые обнаруженные вами утечки.
Вы не указываете операционную систему или среду, в этом ответе предполагается, что Linux, glibc и C.
Вы можете установить __malloc_hook, __free_hook и __realloc_hook, чтобы они указывали на функции, которые будут вызываться из malloc (), realloc () и free () соответственно. Существует справочная страница __malloc_hook, на которой показаны прототипы. Вы можете добавить выделение дорожек в эти хуки, а затем вернуться, чтобы позволить glibc обрабатывать выделение / освобождение памяти.
Похоже, вы хотите освободить любые живые выделения, когда возвращается сторонняя функция. Есть способы, чтобы gcc автоматически вставлял вызовы при каждом входе и выходе из функции с помощью -finstrument-functions, но я думаю, что это было бы неэлегантно для того, что вы пытаетесь сделать. Можете ли вы иметь собственный код для вызова функции в вашей библиотеке отслеживания памяти после вызова одной из этих сторонних функций? Затем вы можете проверить, есть ли какие-либо выделения, которые сторонняя функция еще не освободила.
Раньше я писал программную библиотеку на языке C, в которой была подсистема управления памятью, которая позволяла регистрировать выделение и освобождение памяти, а также вручную сопоставлять каждое выделение и освобождение. Это было полезно при поиске утечек памяти, но было сложно и требовало много времени. Количество журналов было огромным, и для понимания журналов потребовалось много времени.
При этом, если ваша сторонняя библиотека имеет обширные распределения, более чем непрактично отслеживать это через ведение журнала. Если вы работаете в среде Windows, я бы посоветовал использовать такой инструмент, как Purify [1] или BoundsChecker [2], который должен уметь обнаруживать утечки в ваших сторонних библиотеках. Инвестиции в инструмент должны окупиться за счет сэкономленного времени.
[1]: http://www-01.ibm.com/software/awdtools/purify/ Очистить
[2]: http://www.compuware.com/products/devpartner/visualc.htm BoundsChecker
Если у вас есть лишние деньги, подумайте об использовании Purify для отслеживания проблем. Он творит чудеса и не требует исходного кода или перекомпиляции. Существуют также другие доступные библиотеки malloc для отладки, которые дешевле. Я припоминаю одно имя - Electric Fence. Тем не менее, отладочные хуки, упомянутые Дентоном Джентри, тоже кажутся интересными.
Если вы слишком бедны для Purify, попробуйте Valgrind. Это намного лучше, чем было 6 лет назад, и намного легче погрузиться в него, чем Purify.
Microsoft Windows предоставляет (используйте SUA, если вам нужен POSIX), вполне возможно, самую продвинутую инфраструктуру кучи + (другой API, использующий кучу) на сегодняшний день.
ловушки отладки __malloc () и связанные с ними интерфейсы отладки CRT удобны для случаев, когда у вас есть исходный код для тестов, однако они часто могут пропускать выделение стандартными библиотеками или другим связанным кодом. Это ожидается, поскольку они являются инфраструктурой отладки кучи Visual Studio.
gflags - это очень полный и подробный набор возможностей отладки, который уже много лет включен в Windows. Наличие расширенной функциональности для вариантов использования только исходного кода и двоичного кода (поскольку это инфраструктура отладки кучи ОС).
Он может регистрировать полные трассировки стека (переформатирование символьной информации в операции постобработки) всех пользователей кучи для всех точек входа, изменяющих кучу, при необходимости поочередно. Кроме того, он может изменять кучу патологическими случаями, которые могут согласовывать распределение данных таким образом, чтобы защита страницы, предлагаемая системой виртуальных машин, была назначена оптимально (т. Е. Выделить запрошенный блок кучи в конце страницы, так что даже одно байтовое переполнение обнаруживается в момент переполнения.
umdh - это инструмент, который может помочь оценить статус в различных контрольных точках, однако данные постоянно накапливаются во время выполнения цели, o это не просто остановка отладки контрольной точки в традиционном контексте. Кроме того, ПРЕДУПРЕЖДЕНИЕ, последний раз я проверил, по крайней мере, общий размер кольцевого буфера, в котором хранится информация о стеке, для каждого запроса несколько невелик (64k записей (записи + стек)), поэтому вам может потребоваться быстро сбросить дамп для пользователей с большой кучей . Есть и другие способы доступа к этим данным, но umdh довольно прост.
ПРИМЕЧАНИЕ есть 2 режима;
РЕЖИМ 2, umdh [-d] {Файл1} [Файл2] [-f: Имя файла]
Я не знаю, какое безумие охватило разработчика, который решил чередовать спецификатор аргумента -p: foo и голый порядок аргументов, но это может немного запутать.
SDK для отладки работает с рядом других инструментов, memsnap - это инструмент, который, по-видимому, ориентирован на утечку памяти и тому подобное, но я не использовал его, ваш пробег может отличаться.
Выполнять gflags без аргументов для режима пользовательского интерфейса, + arg и / args также являются «режимами» разные.
В Linux я успешно использовал mtrace(3)
для журналирования распределения и освобождения. Его использование так же просто, как
mtrace()
, когда вам нужно начать трассировку (например, в верхней части main()
),MALLOC_TRACE
путь к файлу, в котором должна быть сохранена трассировка, и запустите программу.После этого выходной файл будет содержать что-то вроде этого (отрывок из середины, чтобы показать неудачное выделение):
@ /usr/lib/tls/libnvidia-tls.so.390.116:[0xf44b795c] + 0x99e5e20 0x49
@ /opt/gcc-7/lib/libstdc++.so.6:(_ZdlPv+0x18)[0xf6a80f78] - 0x99beba0
@ /usr/lib/tls/libnvidia-tls.so.390.116:[0xf44b795c] + 0x9a23ec0 0x10
@ /opt/gcc-7/lib/libstdc++.so.6:(_ZdlPv+0x18)[0xf6a80f78] - 0x9a23ec0
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668ee49] + 0x99c67c0 0x8
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668f14f] - 0x99c67c0
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668ee49] + (nil) 0x30000000
@ /lib/libc.so.6:[0xf677f8eb] + 0x99c21f0 0x158
@ /lib/libc.so.6:(_IO_file_doallocate+0x91)[0xf677ee61] + 0xbfb00480 0x400
@ /lib/libc.so.6:(_IO_setb+0x59)[0xf678d7f9] - 0xbfb00480
Да, valgrind тоже неплохо работает. Спасибо за напоминание.