Отладчик malloc
Отладка приложений, в которых возникают проблемы с распределением памяти с помощью подсистемы malloc очень часто требует больших усилий и не всегда дает желаемые результаты. Это связано с тем, что чаще всего возникновение самой ошибки и ее видимое проявление разнесены по времени.
Сложность процесса выделения областей памяти еще более усугубляет проблему: одновременно выделяются и освобождаются тысячи областей, доступ к которым осуществляется одновременно и часто асинхронно, причем все это происходит в многонитевой среде, в которой требуется надежная и эффективная синхронизация.
Именно из-за сложности среды наши отладочные средства ориентированы на как можно более раннее обнаружение возникающих ошибок. В этом случае разработчику проще будет найти раздел исходного кода, в котором содержится ошибка.
Для работы с malloc существует множество отладочных средств. Некоторые из них могут работать в сочетании с другими инструментами отладки и с любыми стратегиями выделения памяти, другие же выполняют более специфические задачи. Многие отладочные средства требуют для своей работы дополнительных ресурсов, помимо тех, что выделены для самого процесса. Сам разработчик решает, когда необходимо выделить эти дополнительные ресурсы.
Производительность
Средства отладки не рассчитаны на постоянное применение. Хотя они
спроектированы таким образом, чтобы минимизировать снижение производительности
системы, такое снижение при широком применении средств отладки все же может
быть весьма существенным. В частности, не рекомендуется задавать переменную
MALLOCDEBUG=catch_overflow в файле
/etc/environment, потому что скорее всего стабильная
работа системы будет нарушена (например, из-за постоянных обращений к
пространству подкачки). Средства отладки следует применять только в тех
случаях, когда это действительно необходимо.
Поскольку работа отладчика заключается в проведении постоянных проверок в ходе работы программы, применение средств отладки снизит производительность подсистемы malloc, но не до такой степени, что дальнейшая работа приложений будет невозможна. После устранения ошибки средства отладки лучше выключить для восстановления нормальной производительности подсистемы malloc.
Дисковая и оперативная память
При включении средств отладки или задании переменной
catch_overflow подсистеме malloc требуется
существенно больше памяти для работы.
При задании переменной catch_overflow каждый запрос malloc
увеличивается на длину unsigned long, помноженную на 4096 + 2, а затем
округляется до ближайшего большего значения, кратного PAGESIZE. Хотя
catch_overflow может потребовать слишком много памяти при работе с
очень большими программами, отладка большинства приложений не сильно скажется
на расходе памяти. При отладке больших приложений рекомендуется указать опции
debug_range и functionset для
catch_overflow. Это заметно снизит расход памяти, поскольку
программа будет отлаживаться по частям.
Каждое выделение памяти процессом записывается в протокол отладки. Если ограничить число сохраняемых указателей стека, то эта дополнительная нагрузка на память может быть снижена.
Если отлаживаемая программа часто вызывает функции выделения памяти подсистемы malloc, то при включенных средствах отладки это может привести к серьезному расходу памяти и сделать невозможным нормальное выполнение программы в пределах одного сегмента. В таких случаях рекомендуется увеличить объем памяти, доступный программе, с помощью команды ulimit или команды ld с опцией -bmaxdata.
ulimit -d unlimited
ulimit -s unlimited Для резервирования всех 8 сегментов для 32-разрядного процесса укажите для опции
-bmaxdata значение -bmaxdata:0x80000000.
После отключения отладчика можно восстановить исходные значения ulimit и -bmaxdata.
Команда ulimit и опция -bmaxdata подробно описаны в разделе Поддержка больших программ.
В некоторых случаях применение средств отладки не оправдано. Поскольку отладчик отводит как минимум отдельную страницу памяти для каждого вызова malloc(), расход памяти у программ, выделяющих множество небольших участков памяти, может очень сильно возрасти. Недостаток памяти или пространства подкачки при выполнении таких программ может привести к новым сбоям. Эти сбои не имеют отношения ни к программам как таковым, ни к средствам отладки malloc.
- Запустите X-сервер, не задавая переменную catch_overflow.
- Откройте окно терминала (например, dtterm, xterm или aixterm).
- Задайте нужные переменные среды в окне терминала и включите catch_overflow.
- Запустите клиент X из этого окна терминала.
Включение отладчика malloc
По умолчанию функция отладки выделения памяти выключена, но ее можно включить и настроить, задав соответствующее значение переменной среды MALLOCDEBUG. Если требуется указать несколько параметров, их можно перечислить через запятую (,). Опции, которые нужно указывать совместно, должны быть совместимы друг с другом.
Средства отладки выделения памяти
Обнаружение переполнения буфера
Ошибки управления памятью иногда бывают связаны с тем, что программа пытается записать больше данных, чем позволяет размер выделенного буфера. Такая операция не влечет за собой немедленных последствий, и ошибка возникает только на этапе, когда потребуются данные, которые хранились в ошибочно занятой (и часто принадлежащей другой программе) области памяти.
Опция отладки catch_overflow позволяет отслеживать ситуации
ошибочной совместной записи и чтения одних и тех же областей памяти, а также
операции повторного освобождения и выделения одних и тех же областей памяти с
помощью функции malloc. Если отладчик обнаруживает
ошибку, то выполняется функция abort или передается сигнал нарушения
сегментации SIGSEGV. Как правило, при обнаружении ошибки
приложение немедленно останавливается, и создается файл core.
catch_overflow влияет на следующие стратегии и параметры выделения памяти: - Стратегия выделения памяти по умолчанию
- Стратегия выделения памяти Watson
- Опция malloc нескольких куч
- Опция malloc кэша нитей
- Опция malloc Disclaim
Опцию отладки catch_overflow можно включить, указав значение
переменной среды MALLOCDEBUG=catch_overflow. При этом будет включено
обнаружение чтения и записи за пределами выделенного буфера памяти.
align
По умолчанию функция malloc выравнивает возвращаемый
указатель по границе ячейки размером в 2 слова. Это требуется для соответствия
стандартам, а также для поддержки программ, которые не могут работать без
выравнивания (например, использующим компоненты DCE). Однако вследствие ошибки
в реализации опции catch_overflow программа может записать данные,
выходящие за пределы буфера, на величину меньшую, чем значение выравнивания по
границе. Такие ошибки не обнаруживаются с помощью catch_overflow. В
связи с этим с помощью опции align подсистемы
malloc можно изменить выравнивание по умолчанию, чтобы
уменьшить или вообще свести к нулю число байт, которое может быть записано за
пределы буфера, не будучи при этом обнаружено. Такое нестандартное выравнивание
должно быть указано как степень двойки в пределах от 0 до 4096 включительно
(например, 0,1,2,4,...). Значения 0 и 1 интерпретируются одинаково и означают
отсутствие выравнивания, то есть любое обращение к памяти за границами
выделенной области приведет к нарушению сегментации (SEGFAULT).
align действует только при включенной опции
catch_overflow и в противном случае игнорируется.
Для включения нестандартного выравнивания задайте переменную среды MALLOCDEBUG следующим образом: MALLOCDEBUG=catch_overflow,align:nгде n - показатель выравнивания.catch_overflow. Здесь размер -
это размер выделяемой области памяти в байтах, а n - показатель выравнивания: ((((размер / n) + 1) * n) - размер) % nMALLOCDEBUG=align:2,catch_overflowcatch_overflow выход за границу выделенной
области будет обрабатываться следующим образом: - Если в запросе на выделение памяти указано четное количество байт, malloc выделит ровно столько байт, сколько указано в запросе. В этом случае приложению будет запрещено выходить за границу выделенной области.
- Если в запросе будет указано нечетное количество байт, то размер выделенной области будет на 1 байт больше запрошенного для обеспечения выравнивания. Приложение сможет выходить за границу исходной области на 1 байт.
override_signal_handling
catch_overflow могут возникать следующие
виды ошибок: - Ошибки при обращении к памяти (например, попытки чтения или записи за границами выделенной памяти) - в этом случае выдается сигнал нарушения сегментации (SIGSEGV) и создается файл core.
- Прочие ошибки (например, попытки повторно освободить область памяти) - в этом случае выдается сообщение об ошибке и вызывается функция abort, которая завершает процесс с помощью сигнала SIGIOT.
Если вызывающая программа блокирует или перехватывает сигналы SIGSEGV и SIGIOT, то сообщение
об ошибке выдано не будет. Для таких случаев предусмотрена
опция override_signal_handling, которая позволяет исправить эту
ситуацию без перекомпиляции программы.
catch_overflow указана опция
override_signal_handling, то при каждом вызове функций подсистемы
malloc будут выполнены следующие действия: - Отключаются все обработчики сигналов программы для SIGIOT и SIGSEGV.
- Задается стандартный обработчик сигналов SIGIOT и SIGSEGV (обработчик SIG_DFL).
- Сигналы SIGIOT и SIGSEGV разблокируются.
Если обработчик сигналов приложения изменит действие для сигнала SIGSEGV между вызовами функций распределения памяти, а затем выполнит недопустимую операцию обращения к памяти, то сообщение об ошибке не будет выдано и при включенной опции catch_overflow, при этом приложение не будет завершено, а файл дампа не будет создан.
- Опция
override_signal_handlingнеэффективна при работе с приложениями с несколькими нитями, поскольку в этом режиме используется функция sigprocmask, а многие многонитевые приложения пользуются функцией pthread_sigmask. - Если нить программы вызывает функцию sigwait, не включая SIGSEGV и SIGIOT в набор
сигналов, и в режиме
catch_overflowотладчик обнаружит ошибку в такой нити, то нить зависнет, поскольку в этом режиме могут генерироваться только сигналы SIGSEGV и SIGIOT. - Если в функцию ядра будет передан недопустимый указатель, то в ней возникнет ошибка. Как правило, в таких ситуациях функции ядра возвращают errno=EFAULT. Если программа не проверяет коды возврата системных вызовов, то такие ошибки могут проходить незамеченными.
debug_range
По умолчанию при включенной опции catch_overflow проверка выхода
за границы области памяти выполняется для каждой операции выделения памяти. Если при этом указана также опция debug_range , то будут обработаны только
запросы на выделение памяти, попадающие между заданными пользователем минимальным и максимальным
значениями размера.
В противном случае проверка
выхода за границы выделения памяти проводиться не будет. Данная опция позволяет контролировать использование
дополнительных ресурсов памяти для режима catch_overflow, указывая
диапазон применимости режима.
debug_range действует только при включенной опции
catch_overflow. Для того чтобы ее включить, укажите: MALLOCDEBUG=catch_overflow,debug_range:мин:максгде мин - минимальное и
макс - максимальное значение диапазона, в котором
требуется контролировать выход за границы области памяти. Если минимальное
значение равно 0, то контроль будет применяться для всех запросов, меньших
максимального значения. Если максимальное значение равно 0, то контроль будет
применяться для всех запросов, больших минимального значения.Ограничения
В силу особенностей реализации каждая выделенная область памяти занимает как
минимум одну страницу. В связи с этим опция debug_range позволяет
снизить затраты памяти, связанные с включением опции catch_overflow,
но не устраняет их совсем.
Если функция realloc вызывается с запросом на выделение памяти в указанном диапазоне значений, то проверка выхода за границы области памяти будет проводиться даже в том случае, если предыдущий запрос не попадал в этот диапазон. Обратное также справедливо.
override_signal указана вместе с опцией
debug_range, то сигналы SIGIOT и SIGSEGV переопределяются при всех
операциях выделения памяти.functionset
В силу особенностей реализации каждая выделенная область памяти занимает как
минимум одну страницу. В связи с этим опция functionset позволяет
снизить затраты памяти, связанные с включением опции catch_overflow,
но не устраняет их совсем.
Если функция realloc вызывается из функции, указанной в списке, то проверка выхода за границы области памяти будет проводиться даже в том случае, если предыдущий запрос выполнялся функцией, не входящей в этот список. Обратное также справедливо.
override_signal указана вместе с опцией
functionset, то сигналы SIGIOT и SIGSEGV переопределяются при всех
операциях выделения памяти.При сохранении списка функций опции functionset не проверяется,
существуют ли эти функции.
allow_overreading
Если программа пытается прочитать данные из области, лежащей за концом
выделенного участка памяти, то по умолчанию при включенной опции
catch_overflow происходит ошибка нарушения сегментации и создается
файл core. Однако пользователь может включить опцию catch_overflow,
чтобы обнаруживать более опасные ошибки, чем просто выход за границы
выделенного участка памяти. Если указать опцию allow_overreading, то
при включенной опции catch_overflow выход за границы выделенного
участка памяти будет игнорироваться, и можно будет обнаружить другие, более
опасные ошибки.
allow_overreading действует только при включенной опции
catch_overflow. Для того чтобы ее включить, укажите: MALLOCDEBUG=catch_overflow,allow_overreading,postfree_checking
Ограничения
Опция postfree_checking расходует очень много памяти. Для
программ с большими требованиями к памяти применение опции
postfree_checking может оказаться невозможным.
Функция трассировки malloc
Отладочная опция трассировки malloc позволяет осуществлять трассировку вызова функций подсистемы malloc с помощью системного трассировщика.
Протокол malloc
Отладочная опция протокола malloc служит для получения информации об активных областях памяти, выделенных подсистемой malloc.
report_allocations
Опция report_allocations служит для обнаружения утечек памяти в
программе. Опция report_allocations использует информацию из базы данных трассировки malloc для получения сведений об
активных областях памяти, используемых программой. Каждое
выделение памяти заносится в протокол malloc. При освобождении выделенного
участка памяти запись о нем удаляется из базы данных протокола malloc. При
завершении процесса список активных областей памяти выводится в stderr, и в нем
перечисляются области памяти, которые были выделены какими-либо процедурами, но
не были освобождены.
report_allocations работает только при включенном протоколе
malloc. Поэтому при включении опции report_allocations протокол
malloc включается автоматически. Для того чтобы включить опцию
report_allocations, укажите: MALLOCDEBUG=report_allocationsvalidate_ptrs
По умолчанию функции подсистемы malloc не проверяют
передаваемые им указатели на адресацию допустимой (т.е. выделенной ранее)
области памяти. Если какой-то из указателей неверен, может произойти серьезный
сбой кучи. При включенной опции validate_ptrs функции подсистемы
malloc будут проверять передаваемые им указатели на
допустимость. Если указатель окажется недопустимым (например, будет адресовать
область данных, которая не выделялась ранее с помощью
malloc), то будет показано сообщение об ошибке с
указанием причин ошибки, а затем будет вызвана функция abort и создан файл
дампа. Опция validate_ptrs работает как опция verbose. Опция validate_ptrs игнорируется, если включена опция postfree_checking.
validate_ptrs, укажите: MALLOCDEBUG=validate_ptrsОбнаружение malloc
Отладочная опция обнаружения malloc позволяет находить ошибки во внутренних структурах данных подсистемы malloc для каждого вызова функций подсистемы malloc.
verbose
Подопция опции обнаружения malloc.
checkarena
Подопция опции обнаружения malloc.
output
По умолчанию отладочные опции malloc направляют вывод в stderr.
Это не всегда бывает удобно. Опция output служит для указания другого канала вывода информации.
Вывод можно направить в stderr, stdout или в любой файл.
output, укажите: MALLOCDEBUG=output:<имя-файла>continue
При обнаружении ошибки многие отладочные опции malloc вызывают функцию
abort(). Это не всегда бывает удобно.
Опция continue позволяет подсистеме malloc
продолжить работу после обнаружения синхронной ошибки, вместо того чтобы
прервать выполнение процесса. Сообщения об ошибках по-прежнему будут выводиться
в соответствующие каналы.
continue, укажите: MALLOCDEBUG=continueMalloc debug fill
Malloc debug fill - это опция заполнения памяти, выделенной для отладки при помощи malloc(), применяемая вместе с пользовательским шаблоном.
В качестве шаблона должна быть указана строка с максимальной длиной 128 символов (например, export MALLOCDEBUG=fill:”abc” установит для выделяемой памяти шаблон “abc”). Если шаблон не указан, то данная опция игнорируется.
Для того чтобы ее включить, укажите:
MALLOCDEBUG=fill:шаблон
Шаблоном могут быть восьмеричные и шестнадцатеричные числа, указанные в виде строки. Например, шаблон “\101” - восьмеричное число, соответствующее символу ‘A’, а шаблон “\x41” - его шестнадцатеричный аналог.
Если указано недопустимое восьмеричное число, которое не может быть сохранено в 1 байте, то вместо него будет сохранено число \377 (максимальное допустимое восьмеричное число).