Ищем утечки памяти

Будем искать через mdb. Что бы иметь возможность это сделать, нужно что бы приложение было запущено в режиме debug. Если в самом приложении отсутствует такая возможность, тогда нужно включить его в окружении и потом запустить приложение:

$ export LD_PRELOAD=libumem.so
$ export UMEM_DEBUG=default
$ export UMEM_LOGGING=transaction

Переменная окружения UMEM_DEBUG может принимать такие значения:

  • adi_precise

Enable precise mode ADI trapping where memory write errors are caught a few instructions after the exact instruction is executed. This has an overall performance overhead, but is more accurate at determining the exact point that an ADI fault occurred. The default is to use disrupting mode traps. For more information, see the adi(7) manual page.

  • audit[=frames]

Этот параметр позволяет записывать информацию аудита, включая идентификатор потока, отметку времени с высоким разрешением и трассировку стека для последнего действия (выделения или освобождения) при каждом выделении. Если включено ведение журнала транзакций (см. UMEM_LOGGING), эта информация аудита также регистрируется.

Параметр frames устанавливает количество кадров стека, записанных в структуре аудита. Верхняя граница для фреймов определяется реализацией. Если запрашивается большее значение, вместо него используется верхняя граница.

Если frame не указан или не является целым числом, используется значение по умолчанию 15.

Эта опция также включает опцию guards

  • contents[=count]

Если аудит и ведение журнала содержимого (см. UMEM_LOGGING) включены, то регистрируются первые байты счетчика каждого буфера, когда они освобождаются. Если буфер короче, чем число байтов, то он регистрируется полностью.

Если счетчик не указан или не является целым числом, используется значение по умолчанию 256.

  • default

Эта опция эквивалентна одновременно включенным audit,contents,guards

  • guards

Эта опция позволяет заполнять выделенные и освобожденные буферы специальными шаблонами, чтобы помочь обнаружить использование неинициализированных данных и ранее освобожденных буферов. Она также включает 8-байтовую красную зону после каждого буфера, который содержит 0xfeedfacefeedfaceULL.

Когда объект освобождается, он заполняется 0xdeadbeef. Когда объект выделен, шаблон 0xdeadbeef проверяется и заменяется на 0xbaddcafe. Красная зона проверяется каждый раз, когда буфер выделяется или освобождается.

Для кэшей с конструкторами или деструкторами или с обоими типами umem_cache_alloc (3MALLOC) и umem_cache_free (3MALLOC) соответственно применяют конструктор и деструктор кэша вместо кэширования созданных объектов. Наличие assert (3C) в деструкторе, проверяющем, что буфер находится в сконструированном состоянии, может использоваться для обнаружения любых объектов, возвращенных в неправильном состоянии. Смотрите umem_cache_create (3MALLOC) для деталей.

  • verbose

Библиотека записывает описания ошибок в стандартную ошибку перед прерыванием. Эти сообщения не локализованы.

Переменная окружения UMEM_LOGGING может принимать такие значения (если нужно несколько, просто перечисляем их через запятую):

  • transaction[=size]

Если установлен параметр audit (см. UMEM_DEBUG), в этот журнал заносятся структуры аудита из предыдущих транзакций.

  • contents[=size]

Если установлена опция audit, содержимое объектов записывается в этот журнал по мере их освобождения.

Если опция отладки contents не была установлена, то сохраняется 256 байтов каждого освобожденного буфера.

  • fail[=size]

Записи заносятся в этот журнал для каждого неудачного размещения.

После того, как описали опции debug’a, вернёмся собственно к самому процессу поиска утечек.

Если процесс уже запущен, то присоединяемся к нему и запускаем встроенную команду findleeks:

# mdb -p `pgrep sstored`
> ::findleaks -d
    mem_alloc_16 leak: 71875583 buffers, 16 bytes each, 1150009328 bytes total
               ADDR          BUFADDR        TIMESTAMP             THREAD
                               CACHE          LASTLOG           CONTENTS
    1ffd1f4cccc190   1ffd1f4cca8c70   247bbaad78a0cf                  9
    1ffd1f8d5d0028                0                0
                     libumem.so.1`umem_cache_alloc+0x158
                     libumem.so.1`umem_alloc+0xc0
                     libumem.so.1`malloc+0xd4
                     sstored`malloc+0x4c
                     libc.so.1`strdup+0xc 
                     sspc_proc_scan+0x198
                     sspc_update+0x8c
                     libc.so.1`_lwp_start

Так же можно запустить и непосредственно из mdb (для анализа core-файлов, тоже применяется конструкция mdb core.970):

# /usr/bin/mdb ./my_leaky_program
> ::sysbp _exit
> ::run
mdb: stop on entry to _exit
mdb: target stopped at:
libc.so.1`exit+0x14:    ta        8
mdb: You've got symbols!
mdb: You've got symbols!
Loading modules: [ ld.so.1 libumem.so.1 libc.so.1 ]
> ::findleaks
CACHE     LEAKED   BUFCTL CALLER
0002c508       1 00040000 main+4
----------------------------------------------------------------------
Total       1 buffer, 24 bytes
> 00040000::bufctl_audit
ADDR  BUFADDR    TIMESTAMP THR  LASTLOG CONTENTS    CACHE     SLAB     NEXT
DEPTH
00040000 00039fc0 3e34b337e08ef   1 00000000 00000000 0002c508 0003bfb0 00000000
5
libumem.so.1`umem_cache_alloc+0x13c
libumem.so.1`umem_alloc+0x60
libumem.so.1`malloc+0x28
main+4
_start+0x108

Как видим, утечки памяти есть. Мы даже попробовали проанализировать «глубже», через bufctl_audit.

Если по какой-то причине, там пусто, можно дополнительно запустить ::umem_verify, ::umalog, ::walk leak, ::walk leakbuf

> ::umem_verify
Cache Name                      Addr     Cache Integrity     
umem_magazine_1                 fe826010 clean
umem_magazine_3                 fe826390 clean
umem_magazine_7                 fe826710 clean
umem_magazine_15                fe826a90 clean
umem_magazine_31                fe827010 clean
umem_magazine_47                fe827390 clean
umem_magazine_63                fe827710 clean
umem_magazine_95                fe827a90 clean
umem_magazine_143               fe828010 clean
umem_slab_cache                 fe828390 clean
umem_bufctl_cache               fe828710 clean
umem_bufctl_audit_cache         fe828a90 clean
umem_alloc_1152 8098a90 1 corrupt buffer

Для просмотра всех доступных команд для debug’a памяти, можно воспользоваться

> ::dmods -l libumem.so.1

Из наиболее интересных, я бы отметил следующие:

  • umem_verify — повреждена ли память, которая была выделена под конкретное приложение
  • umalog — повреждена ли память, которая была выделена под конкретное приложение и в данный момент она освобождена

Так же хотелось бы рассмотреть опции самого findleaks:

  • «-d» — детальный вывод каждой утечки
  • «-f» — сбросить кеш
  • «-v» — расширенный вывод
  • «-U» — вывод потенциальных утечек MMAP

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *