Показаны сообщения с ярлыком valgrind. Показать все сообщения
Показаны сообщения с ярлыком valgrind. Показать все сообщения

11/03/2011

Утечки памяти и ошибки доступа.

Ранее я уже писал о том, что можно искать утечки памяти в программе с помощью средств, которые предоставляет glibc. Описанный подход плох тем, что не позволяет искать ошибки доступа к памяти, такие как обращение по нулевому указателю, или обращение к уже освобожденной памяти.

В данном посте речь пойдет о mudflap.
mudflap - это технология проверки указателей, основанная не на бинарной инструментации, как valgrind или dr. Memory, а на инструментации времени компиляции. Если в кратце, во время компиляции программы в исходный код делаются вставки, которые обспечивают различные проверки указателей,к которым осуществляется доступ.

Теперь разберем работу mudflap на конкретном примере.

Для того, чтобы можно было использовать mudflap, нужа поддержка этой технологии в компиляторе и собственно сама библиотека, выполняющая проверки: gcc должен  быть скомпилирован с опцией --enable-libmudflap, и в системе должна присутствовать библиотека libmudflap.so.

В качестве примера рассмотрим программу, котороя пытается записать в уже освобожденную память:
#include <malloc.h>

int main (void)
{
  const int chunk_size = 18;
  const int count  = 10000;
  int j = 0;
  void * ptrs [count];

  /* выделяем 10 000 кусочков памяти по 18 байт.*/
  for (j = 0; j < count; j++) {
    ptrs [j] = malloc (chunk_size);
  }

  /* освобождаем все, кроме первого и последнего */

  for (j = 1; j < count - 2; j++)
    if (ptrs [j])
      free (ptrs [j]);

  /* записываем в освобожденную память*/
  *(char*)ptrs [10] = 10;

  /* просим glibc сделать "дефрагментацию" выделенной памяти */
  malloc_trim (1);

  return 0;
}


Результатом работы программы будет Segmentation Fault.
Это происходит потому что при попытке записи в освобожденную память (на самом деле она не освобождается, а помечается как неиспользуемая) программа портит служебную информацию glibc, а именно связный список, который используется в glibc для управления памятью.

Чтобы отловить ошибку в программе нужно пересобрать программы с mudflap инструментацией:

gcc -fmudflapth -lmudflap -Wall -Wextra -g -ggdb3 buggy.c
Запускаем:

./a.out


В результате работы программа выдает следующее:

*******

mudflap violation 1 (check/write): time=1320343428.778161 ptr=0xe85c10 size=1

pc=0x7fcc8d0dc4f8 location=`buggy.c:22:21 (main)'

      /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.3/libmudflap.so.0(__mf_check+0x38) [0x7fcc8d0dc4f8]

      ./a.out(main+0x301) [0x400da5]

      /lib64/libc.so.6(__libc_start_main+0xec) [0x7fcc8cd63e8c]

number of nearby objects: 0


Ясно видно где в исходном коде возникает проблема.

Для проверки, запустим программу под valgrind-ом, который выдает следующую информацию:
==26575== 

==26575== Invalid write of size 1

==26575==    at 0x4006E0: main (buggy.c:22)

==26575==  Address 0x51bc400 is 0 bytes inside a block of size 18 free'd

==26575==    at 0x4C27F6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)

==26575==    by 0x4006C8: main (buggy.c:19)


valgrind как и mudflap, точно показал место ошибки в программе:
buggy.c: 22

Резюмирая можно обозначить плюсы и минусы mudflap:
"+" :
1. Более низкий overhead по сравнению с valgrind.
2. Лучшая переносимость: доступен для платформ ARM и MIPS.
3. Из-за того, что инструментация происходи во время выполнения, может находить более сложные ошибки и более точно показывать их в коде.

"-":
1. Поддержка кода, написанного только на C/C++.
2. Привязка к компилятору GCC.
3. Необходимость перекомпиляции.

Таким образом, mudflap вполне удобный тул, который позволяет искать утечки в памяти не используя valgrind, что особенно полезно для встраиваемых систем (ARM и MIPS), где на исполнение кода существенно влияют накладные расходы на инструментацию.

Больше информации о mudflap можно посмотреть тут : http://gcc.gnu.org/wiki/Mudflap_Pointer_Debugging

4/01/2011

Valgrind и glibc в Gentoo.

Для корректной работы валгринда требуется, чтобы glibc была собрана с отладочной информацией, дабы он смог из нее вытащить нужные функции.
В Gentoo glibc умолчанию собирается без нее.
Чтобы эту проблему исправить, необходимо чтобы для glibc генерировалась отладочная информация.
Для этого, добавляем в файл /etc/portage/env/sys-libs/glibc
строчку: FEATURES="splitdebug"
И пересобираем libc.

После этого, в каталоге /usr/lib/ появится каталог debug, в котором будет лежать файлы типа lib*.debug, содержащиеся требуемую информацию.

3/16/2009

Поиск утечек памяти штатными средствами.

Если спросить у кого-нибудь, как обнаружить в программе утечки памяти, то этот кто-нибудь скорее всего вас отправить на сайт www.valgrind.org. Конечно valgrind очень мощная и удобная утилита, но что делать, когда нет возможности использовать valgrind? Скажем нехватка памяти, или не поддерживаемая им архитектура?
На помощь нам приходит glibc и функции mtrace и muntrace. Функция mtrace устанавливает обработчики для malloc, realloc, и free. А функция muntrace убирает установленные обработчики.
Для того чтобы установить обработчики, нужно вызвать mtrace скажем в начале программы. Затем нужно определить переменную окружения MALLOC_TRACE в которой нужно определить путь к текстовому файлу, где mtrace сохранит результаты работы.
Рассмотрим пример:

1 #include <mcheck.h>
2 #include <stdlib.h>
3
4 int main (int argc, char *argv[])
5 {
6 int *i = NULL;
7
8 mtrace ();
9 i = (int*)malloc (sizeof (int));
10
11 return 0;
12 }

В функции утечка памяти: в строке 9 она выделяется, но далее нигде не освобождается.
Компилируем программу с отладочной инорфмацией, если мы хотим увидеть не только адрес функции, но еще и номер строки:
gcc -ggdb3 -o main main.c

Устанавливаем в переменной окружения
MALLOC_TRACE путь к выходному файлу:
export MALLOC_TRACE=/tmp/out.txt

Запускаем программу:
./main

После запуска можно посмотреть на созданный файл:
cat /tmp/out.txt

Содержимое его будет примерно таким:

= Start
@ ./main:[0x40057e] + 0x602460 0x4

Не очень понятно, что тут и зачем =).
Однако вместе с libc ставится и скрипт на языке perl, который выводит информацию об утечке памяти в более человеческом виде. Запускаем в консоли:
mtrace ./main /tmp/out.txt
И получаем вывод:

Memory not freed:
-----------------
Address Size Caller
0x0000000000602460 0x4 at /tmp/main.c:9

P.S. Не забываете очищать выделенную память, а если вы не забываете ее очищать, то и не забывайте ее ранее выделять ;-)