Ключевые слова:memory, performance, gpt, debug, (найти похожие документы)
Автор: Alex Ott <alexott@gmail.com>
Date: Mon, 7 Nov 2010 17:02:14 +0000 (UTC)
Subject: Поиск утечек памяти при помощи GPT, Valgrind, Exmap, dmalloc и Electric Fence
Оригинал: http://alexott.net/ru/writings/prog-checking/Google Performance ToolsGoogle Performance Tools (GPT) - набор утилит, которые позволяют
проводить анализ производительности программ, а также анализировать
выделение памяти программами и производить поиск утечек памяти.
Установка
GPT может работать практически на всех Unix-совместимых операционных
системах -- Linux, FreeBSD, Solaris, Mac OS X (Darwin), включая
поддержку разных процессоров (^1) -- x86, x86_64 и PowerPC. Кроме того,
tcmalloc можно скомпилировать также и для MS Windows, что позволит
искать утечки памяти в программах, разработанных для этой ОС.
# 1. Имеются правда некоторые особенности при сборке на 64-битных
# системах, о которых вы можете прочитать в файле INSTALL, входящем в
# архив с исходными текстами системы.
Установка на Unix-совместимые системы достаточна проста -- пакет
использует стандартную инфраструктуру autotools, так что вам для
установки необходимо лишь скачать исходные тексты с сайта проекта и
выполнить последовательность команд:
./configure
make
make install
которые приведут к настройке, сборке и установке пакета. Вы можете
установить пакет в нужное вам место, задав аргументы команде
./configure.
Для установки пакета на MS Windows вам необходимо скачать с
сайта проекта специальную версию, которая содержит файлы проекта для
сборки с помощью Visual C++. Дополнительную информацию можно найти в
файле README.windows, входящем в состав исходных текстов.
Основы работы с GPT
Google Performance Tools состоят из двух библиотек:
tcmalloc (Thread-Caching Malloc)
tcmalloc -- очень быстрая реализация malloc (быстрее чем malloc
в glibc 2.3). С помощью данной библиотеки можно анализировать
выделение памяти в программе, а также производить поиск утечек
памяти. Про внутреннее устройство tcmalloc можно прочитать на
[6]сайте проекта.
profiler
данная библиотека реализует анализ производительности
выполняемого кода.
Обе библиотеки могут без ограничений использоваться с многопотоковыми программами.
Использовать эти библиотеки можно двумя способами -- указав имя
библиотеки при линковке вашего кода (используя флаг -l), или загрузив
ее с помощью LD_PRELOAD. Разработчики рекомендуют по возможности
использовать первый метод, а второй метод использовать только если вы
не имеете доступа к исходному коду.
Стоит отметить, что подключение библиотек не означает автоматического
выполнения функций профилирования процессора или памяти. Включение этих
функций определяется наличием определенных переменных среды, которые
будут описаны в соответствующих разделах. В процессе работы библиотеки
могут создавать текстовые файлы, которые содержат собранную информацию.
Для преобразования этой информации в более удобную для анализа форму, в
поставку пакета входит программа pprof, краткое описание которой вы
найдете в одном из следующих разделов.
Разработчик может также использовать функции GPT в своих программах для
того, чтобы выполнять проверку только определенных частей программ, или
для запрещения "известных" утечек памяти. Про использование каждой из
компонент GPT можно прочитать в соответствующем разделе [7]документации
данного пакета.
Поиск утечек памяти
Поиск утечек памяти с помощью tcmalloc очень прост -- надо слинковать
программу с этой библиотекой, и запустить ее вот так:
# HEAPCHECK=normal ./your-program
или вот так (без линковки):
# LD_PRELOAD=/usr/lib/libtcmalloc.so.0.0.0 HEAPCHECK=normal ./your-program
и после выполнения программы, она выдаст отчет о найденных утечках
памяти, например вот так:
# LD_PRELOAD=/usr/lib/libtcmalloc.so.0.0.0 HEAPCHECK=normal ./test-hashes 1000000
HeapChecker: Starting check "_main_"
HeapChecker: Ignoring 12 reachable objects of 275 bytes
.... Вывод программы .....
HeapChecker: Checking for whole-program memory leaks
HeapChecker: Ending check "_main_"
HeapChecker: Ignoring 32 reachable objects of 1679 bytes
HeapChecker: Heap memory leaks of 1000 bytes and/or 1 allocations detected by check "_main_".
HeapChecker: To investigate leaks manually use e.g.
cd /home/ott/projects/cpp-tests; /usr/bin/pprof ./test-hashes "/tmp/test-hashes.2977._main_-end.heap" \
--inuse_objects --lines --edgefraction=1e-10 --nodefraction=1e-10 --gv 2>/dev/null
HeapChecker: Below is this pprof's output:
1 100.0% 100.0% 1 100.0% main test-hashes.cpp:106
0 0.0% 100.0% 1 100.0% 0xb7c8d450 ??:0
0 0.0% 100.0% 1 100.0% _start ??:0
HeapChecker: crashing because of leaks
2977 segmentation fault (core dumped) LD_PRELOAD=/usr/lib/libtcmalloc.so.0.0.0 HEAPCHECK=normal ./test-hashes
Заметьте, что при поиске утечек памяти отчет выводится сразу (вы можете
изменить параметры анализа, поскольку данные записываются во временный
файл). Пользователь также может с помощью дополнительных переменных
среды изменить поведение библиотеки и получить дополнительные данные о
работе программы (но об этом ниже).
Переменная среды HEAPCHECK указывает уровень проверок, которые будут
применяться в процессе работы. Определено 4 значения для данной
переменной -- minimal, normal, strict и draconian -- от самых простых,
до все более изощренных проверок, использование которых конечно влияет
на скорость работы анализируемой программы. Кроме этого, есть еще два
режима: as-is -- когда пользователь с помощью опций может задать список
выполняемых проверок, и local -- когда проверки выполняются только для
кода, который явно отмечен для проверки (путем добавления вызовов
функций GPT в исходный код программы).
При нахождении утечки памяти (как в нашем примере) библиотека аварийно
завершает программу, но при этом выдает на экран стек вызовов функций,
приведших к появлению утечки памяти. В нашем примере, утечка памяти
происходит в функции main, на 106 строке кода в файле с исходным
текстом (test-hashes.cpp).
В процессе работы библиотека учитывает значения следующих переменных
среды (эти переменные определены для всех уровней проверок):
HEAP_CHECK_REPORT
(true или false, по умолчанию: true), определяет -- выводить
отчет на экран или нет
HEAP_CHECK_STRICT_CHECK
(true или false, по умолчанию: true), определяет функцию,
которая будет использоваться для проверок -- SameHeap или NoLeaks
HEAP_CHECK_IDENTIFY_LEAKS
(true или false, по умолчанию: false), позволяет получить адреса
"потерянных" объектов
HEAP_CHECK_TEST_POINTER_ALIGNMENT
(true или false, по умолчанию: false), проверяет все утечки
памяти на предмет того, не могли ли они возникнуть из-за
использования невыравненных указателей
PPROF_PATH
определяет путь к программе pprof
HEAP_CHECK_DUMP_DIRECTORY
определяет путь для временных файлов. используемых при работе
библиотеки.
Дополнительную информацию о библиотеке и об использовании режимов as-is
и local можно найти в данном документе.
Анализ потребления памяти
Кроме нахождения утечек памяти, библиотека tcmalloc позволяет
производить анализ потребления памяти программами. Для выполнения
данной задачи, библиотека отслеживает все выделения и освобождения
блоков памяти. К отслеживаемым функциям выделения памяти относятся
malloc, calloc, realloc и new.
Процедура использования библиотеки для анализа потребления памяти
сходна с процедурой поиска утечек памяти, но отличается именами
переменных среды. При работе в режиме анализа потребления памяти,
библиотека выводит результаты в файл, указанный в переменной среды
HEAPPROFILE, наличие которой и заставляет библиотеку работать в данном
режиме. Например:
LD_PRELOAD=/usr/lib/libtcmalloc.so.0.0.0 HEAPPROFILE=gpt-heapprofile.log ./your-program
Данные собранные в процессе работы библиотеки, после завершения работы
становятся доступными для анализа с помощью утилиты pprof, описанной
ниже. Данные обработанные этой утилитой можно получить как в текстовом,
так и в графическом виде. Эти данные включают в себя данные об
выделении памяти функциями программы и отношение между различными
функциями, выделяющими память.
С помощью дополнительных переменных среды можно изменять поведение
библиотеки, и например, заставить ее также анализировать вызовы mmap в
дополнение к стандартным функциям выделения памяти (это происходит если
переменную среды HEAP_PROFILE_MMAP установить в значение true).
Описание дополнительных переменных среды, а также примеры анализа
данных, собранных библиотекой, вы сможете найти в [11]документации.
Анализ производительности программ
Использование профайлера очень просто -- надо лишь слинковать программу
с библиотекой profiler, и указать имя файла куда будут выводиться
собранные данные в переменной среды CPUPROFILE, например вот так:
# CPUPROFILE=/tmp/cpuprofile.log ./your-program
или можно вот так (без линковки библиотеки) -- если вам нужно провести
всего один анализ, или вы не имеете доступа к исходному коду программы:
# LD_PRELOAD=/usr/lib/libprofiler.so.0.0.0 CPUPROFILE=/tmp/cpuprofile.log ./your-program
Кроме CPUPROFILE вы можете использовать и другие переменные среды для
управления поведением данной библиотеки, например, CPUPROFILE_FREQUENCY
задает количество проверок, выполняемых библиотекой каждую секунду.
После выполнения программы, собранные результаты оказываются в
указанном вами файле, и вы можете провести их анализ. Для выполнения
анализа используется программа pprof. Данные можно получать, как в
текстовом:
23 2.4% 42.6% 309 32.4% std::less::operator
так и в графическом виде (это только часть графика, выводимого pprof):
При выводе результатов в виде текста, утилита выдает список функций,
для каждой из которых указывается следующая информация (в колонке с
соответствующим номером):
1. количество проверок, проведенных в данной функции
2. процентное отношение количества проверок для данной функции к
общему количеству проверок
3. процентное отношение количества проверок, проведенных до этого
времени к общему количеству проверок
4. количество проверок, проведенных в данной функции и всех функций,
вызванных из нее
5. процентное отношение количества проверок для данной функции и всех
функций вызванных из нее к общему количеству проверок
6. имя функции
При выводе в виде графика, кроме этой информации, показываются еще и
зависимости между вызовами функции.
Более подробно об использовании cpu profiler и об анализе собранных
результатов вы можете прочитать на [12]сайте проекта, а про
использование утилиты pprof читайте в следующем разделе.
Утилита pprof
Утилита pprof предназначена для анализа данных, собранных во время
сбора сведений о потреблении памяти или производительности программы. В
общем, командная строка для запуска этой утилиты выглядит следующим
образом:
pprof [необязательные опции] имя_программы имя_файла_с_результатами
Опции запуска утилиты можно разделить на общие, и специфичные для
конкретных режимов работы. Общие опции позволяют указать формат
выводимой информации, и степень подробности выводимой информации --
опции --addresses, --lines, --functions (по умолчанию) или --files
определят то, какой уровень подробности будет использоваться --
физический адрес, строка кода, функция или файл с исходным текстом.
Утилита может выводить информацию как в текстовом (опция --text), так и
в графическом виде (опции --gif, --ps, --pdf и другие). Можно также
заставить ее вывести результаты в виде аннотированного исходного кода
(опция --list), или дизассемблированного машинного кода (опция
--disasm). Если утилита не получает опций, указывающий формат выводимой
информации, то она переходит в режим интерактивной работы, где
определены команды, соответствующие опциям командной строки (только без
знаков --).
При проведении анализа данных, собранных во время анализа потребления
памяти, определены опции, которые позволяют отображать количество
используемых и/или выделенных объектов (--inuse_objects или
--alloc_objects), объем выделенной памяти (--alloc_space) или
использовать байты в качестве единицы измерения, вместо мегабайт, по
умолчанию (опция --show_bytes).
При проведении анализа производительности программы, могут
использоваться опции, определяющие подробность выводимой информации.
Например, можно исключить из вывода объекты, соответствующие заданному
регулярному выражению (--ignore), или можно управлять степенью
детализации выводимых графиков (опции --nodefraction, --nodefraction и
--edgefraction).
Полное описание утилиты pprof можно найти в справочной странице,
поставляемой вместе с исходными текстами, и устанавливаемой вместе с
утилитой. А большее количество примеров можно найти на [13]страницах,
описывающих использование компонентов GPT.
Valgrind
Valgrind хорошо известен как мощное средство поиска ошибок работы с
памятью. Но кроме этого, в его составе имеется некоторое количество
дополнительных утилит, предназначенных для профилирования программ,
анализа потребления памяти и поиска ошибок связанных с синхронизацией в
многопоточных программах.
Данная статья содержит краткое описание принципов работы с valgrind и
использования различных его модулей. Данное описание соответствует
valgrind версии 3.3. Дополнительную информацию о работе с valgrind вы
можете найти на его сайте, который содержит руководства разного
уровня сложности, начиная от достаточно легкого Quick Start, и
заканчивая подробными руководством пользователя и техническим описанием системы.
Архитектура Valgrind
Valgrind имеет модульную архитектуру, и состоит из ядра, которое
выполняет эмуляцию процессора, а конкретные модули выполняют сбор и
анализ информации, полученной во время выполнения кода на эмуляторе.
Valgrind работает под управлением ОС Linux на процессорах x86, amd64,
ppc32 и ppc64 (стоит отметить, что ведуться работы по переносу Valgrind
и на другие ОС), при этом существуют некоторые ограничения, которые
потенциально могут повлиять на работу исследуемых программ.
Хорошее описание архитектуры Valgrind и принципов его работы
можно найти в статье Valgrind: A Framework for Heavyweight Dynamic
Binary Instrumentation
В поставку valgrind входят следующие модули-анализаторы:
memcheck
основной модуль, обеспечивающий обнаружение утечек памяти, и
прочих ошибок, связанных с неправильной работой с областями
памяти -- чтением или записью за пределами выделенных регионов и
т.п.
cachegrind
анализирует выполнение кода, собирая данные о (не)попаданиях в
кэш, и точках перехода (когда процессор неправильно
предсказывает ветвление). Эта статистика собирается для всей
программы, отдельных функций и строк кода
callgrind
анализирует вызовы функций, используя примерно ту же методику,
что и модуль cachegrind. Позволяет построить дерево вызовов
функций, и соответственно, проанализировать узкие места в работе
программы.
massif
позволяет проанализировать выделение памяти различными частями
программы
helgrind
анализирует выполняемый код на наличие различных ошибок
синхронизации, при использовании многопоточного кода,
использующего POSIX Threads.
Имеется еще некоторое количество модулей, но они считаются
экспериментальными и не отличаются особой стабильностью. Кроме того,
пользователь может создавать свои модули, выполняющие анализ
выполняемого кода.
Начало работы с valgrind
В настоящее время valgrind входит в состав практически всех
дистрибутивов Linux, и только в редких случаях требуется его установка
вручную, поэтому я пропущу описание этого процесса.
Интересующиеся, могут найти описание процесса сборки и установки
в руководстве пользователя valgrind.
Работа с valgrind достаточно проста -- его поведение полностью
управляется опциями командной строки, а также не требует специальной
подготовки программы, которую вы хотите проанализировать (Хотя все-таки
рекомендуется пересобрать программу с отладочной информацией и
отключенной оптимизацией используя флаги компиляции -g и -O0). Если
программа запускается командой "программа аргументы", то для ее запуска
под управлением valgrind, необходимо в начало этой командной строки
добавить слово valgrind, и указать опции, необходимые для его работы.
Например, так:
valgrind --leak-check=full --leak-resolution=med программа аргументы
что приведет к запуску нужной программы c заданными аргументами, и для
нее будет проведен поиск утечек памяти.
По умолчанию, valgrind запускает модуль memcheck, однако пользователь
может указать какой модуль должен выполняться с помощью опции --tool,
передав в качестве аргумента имя нужного модуля, например, вот так:
valgrind --tool=callgrind ./test
Стоит отметить, что часто используемые опции можно задать один раз,
используя глобальный файл конфигурации (~/.valgrindrc), так что вам не
придется их набирать при каждом запуске valgrind.
Общие опции запуска программы
Некоторые опции командной строки являются общими для всех модулей. К
наиболее часто используемым опциям можно отнести:
--quiet
(или -q) подавляет вывод лишней информации, приводя к выводу
только информации об ошибках.
--verbose
(или -v) заставляет valgrind выводить подробную информацию о
своей работе.
--log-file
позволяет задать имя файла в который будет выводиться отчет о
работе. В заданном имени могут использоваться специальные
шаблоны, куда будут подставляться различные значения, например,
идентификатор процесса (шаблон %p).
--log-socket
позволяет задать адрес и порт на который будет передаваться
отчет о работе.
--log-fd
позволяет указать дескриптор файла, в который будет выводиться
отчет о работе (по умолчанию это число 2 -- стандартный вывод
сообщений об ошибках).
--track-fds
(yes или no, по умолчанию no) заставляет valgrind выдать список
открытых дескрипторов файлов при окончании работы.
--trace-children
(yes или no, по умолчанию no) разрешает трассировку процессов,
запущенных анализируемой программой с помощью системного вызова
exec.
--time-stamp
(yes или no, по умолчанию no) приводит к выдаче временных меток
в отчет о работе (время отсчитывается от начала работы
программы).
Для задания опций используется стандартная форма. Некоторые
опции могут иметь одно-буквенные сокращения, и если они имеют
аргументы, то аргументы задаются через пробел после указания
соответствующей опции. Для опций, с полным названием, таких как --log-file,
аргументы указываются сразу после опции, используя знак = в качестве
разделителя имени и значения.
Опции управления обработкой ошибок
Пользователь valgrind имеет достаточно большой набор опций,
предназначенных для управления процессом обработки ошибок -- начиная от
опций управления форматом вывода, и заканчивая опциями, задающими
размер стека.
По умолчанию, valgrind при печати сообщения об ошибке выдает стек
вызова функций, которые привели к появлению данной ошибки. По умолчанию
глубина вложенности функций равна 12, но это значение можно изменить с
помощью опции --num-callers. При этом стоит отметить, что увеличение
этого параметра приведет к некоторому замедлению работы valgrind.
Пользователь также может управлять тем, сколько и каких ошибок будет
выведено в отчет. Для этого имеется опция --error-limit (yes или no, по
умолчанию yes), которая позволяет ограничить отчет выводом 1000
различных ошибок. Если пользователь не ограничивает вывод ошибок, то
это также сказывается на производительности.
Кроме того, пользователь может управлять тем, какие ошибки будут
выдаваться в отчет, а какие нет. Это делается с помощью задания
специальных директив (suppressions), которые записываются в файлы,
имена которых можно передать с помощью опции --suppressions. В поставке
valgrind есть файл (обычно это /usr/lib/valgrind/default.supp), в
котором перечислены известные ошибки glibc, но кроме того, пользователь
может изготовить собственный файл, для чего можно использовать опцию
--gen-suppressions, которая будет запрашивать пользователя, нужно ли
сгенерировать директиву для данной ошибки, или нет.
Пользователь также имеет возможность запуска отладчика при нахождении
ошибок. Для этого существует опция --db-attach (yes или no, по
умолчанию no), при использовании которой у пользователя будет
запрашиваться разрешение на запуск отладчика. Опции для запуска
отладчика могут быть указаны с помощью опции --db-command, но значений
по умолчанию вполне достаточно для большинства случаев.
Поиск утечек памяти
Valgrind приобрел популярность в первую очередь за свои возможности по
нахождению утечек памяти в программах. За этот реализацию этих функций
отвечает модуль memcheck, для которого определены отдельные опции,
управляющие процессом проверки.
--leak-check
включает (значение yes, summary или full) или отключает
(значение no) функцию обнаружения утечек памяти. Стоит отметить,
что при использовании значения summary, memcheck выдает лишь
краткую информацию об утечках памяти, тогда как при других
значениях, кроме сводной информации, будет выдаваться еще и
информация о месте, в котором происходит эта утечка памяти.
--leak-resolution
(возможные значения low, med или high) указывает способ
сравнения стека вызовов функций. При значениях low и med, в
сравнении используются два или четыре последних вызова,
соответственно, а при high, сравнивается полный стек вызова. Эта
опция влияет лишь на способ представления результатов поиска
ошибок.
--undef-value-errors
(yes или no) определяет, будут ли показывать ошибки об
использовании не инициализированных значений.
Прочие опции используются значительно реже, и в случае необходимости вы
можете найти их описание в руководстве пользователя.
Интерпретация полученных результатов
Существует несколько видов ошибок, обнаруживаемых модулем memcheck.
Ошибки чтения и записи за пределами выделенной памяти (и еще несколько
видов ошибок) выдаются сразу, в процессе работы программы. А ошибки,
ведущие к утечкам памяти, выдаются valgrind'ом после завершения работы
анализируемой программы. Формат выдачи этих ошибок немного отличается,
поэтому они будут описаны по отдельности.
Каждая строка в выводе valgrind имеет префикс вида
==12345==
где число обозначает идентификатор запущенного процесса.
Ошибки работы с памятью
В ходе своей работы, модуль memcheck определяет несколько видов ошибок
работы с памятью:
* чтение или запись по неправильным адресам памяти -- за границами
выделенных блоков памяти и т.п.
* использование не инициализированных значений, в том числе и для
переменных выделяемых на стеке
* ошибки освобождения памяти, например, когда блок памяти уже был
освобожден в другом месте
* использование "неправильной" функции освобождения памяти, например
использование delete для памяти, выделенной с помощью new []
* передача некорректных параметров системным вызовам, например
указание неправильных указателей для операций чтения из буфера,
указанного пользователем
* пересечение границ блоков памяти при использовании операций
копирования/перемещения данных между двумя блоками памяти
Для этих ошибок данные выдаются по мере их обнаружения, и обычно они
выглядят следующим образом:
Mismatched free() / delete / delete []
at 0x40043249: free (vg_clientfuncs.c:171)
by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149)
by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60)
by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44)
Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd
at 0x4004318C: operator new[](unsigned int) (vg_clientfuncs.c:152)
by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314)
by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416)
by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272)
В первой строке приводится описание соответствующей ошибки, а затем
идет стек вызова функций, приведших к появлению данной ошибки. В том
случае, где это необходимо (как в нашем примере), выдается также адрес
блока памяти и место где этот блок памяти был выделен.
Нахождение утечек памяти
При окончании работы программы valgrind выдает сводную таблицу,
описывающую количество найденных ошибок, а также выделение памяти в
программе, например:
ERROR SUMMARY: 2569904 errors from 493 contexts (suppressed: 17962 from 9)
malloc/free: in use at exit: 85,066,939 bytes in 313,004 blocks.
malloc/free: 10,552,914 allocs, 10,239,910 frees, 565,747,810 bytes allocated.
For counts of detected errors, rerun with: -v
searching for pointers to 313,004 not-freed blocks.
checked 117,623,772 bytes.
И в самом конце отчета, выдается сводная таблица по каждому из типов
ошибок работы с памятью:
LEAK SUMMARY:
definitely lost: 2,260 bytes in 47 blocks.
indirectly lost: 1,680 bytes in 66 blocks.
possibly lost: 2,703,124 bytes in 13,791 blocks.
still reachable: 82,359,875 bytes in 299,100 blocks.
suppressed: 0 bytes in 0 blocks.
Definitely lost означает, что valgrind нашел область памяти, на которую
нет указателей, т.е. программист не освободил память, при выходе
указателя за область видимости. Possibly lost показывает, что найден
указатель, указывающий на часть области памяти, но valgrind не уверен в
том, что указатель на начало области памяти до сих пор существует (это
может происходить в тех случаях, когда программист вручную управляет
указателями). Still reachable обычно означает, что valgrind нашел
указатель на начало не освобожденного блока памяти, что во многих
случаях связано с выделением глобальных переменных и т.п. вещей. Обычно
эта информация показывается только при указании опции --show-reachable
со значением yes.
Между двумя этими таблицами выдаются данные по каждой из найденных
ошибок работы с памятью, вида:
756 bytes in 27 blocks are definitely lost in loss record 1,077 of 1,267
at 0x4022AB8: malloc (vg_replace_malloc.c:207)
by 0x7C485DA: __libc_res_nsend (res_send.c:425)
by 0x7C47276: __libc_res_nquery (res_query.c:171)
by 0x7C47B5B: __res_nquery (res_query.c:223)
by 0x834A618: LookUp::LookUp(std::string&) (LookUp.cpp:83)
by 0x8637C29: ClientThread::Connect(LogFileEntry&) (ClientThread.cpp:359)
by 0x86394D5: ClientThread::Run() (ClientThread.cpp:215)
by 0x80FD839: Thread::StartRunLoop(void*) (Thread.cpp:315)
by 0x4163FD9: start_thread (pthread_create.c:297)
by 0x43843AD: clone (in /usr/lib/debug/libc-2.7.so)
Первой строкой идет описание ошибки, вместе с указанием номера блока в
списке потенциально потерянных блоков памяти, а также размером
"потерянного" блока памяти. "Важность" ошибки соответствует описанию в
итоговой таблице. После строки описания, приводится стек вызовов
функций, которые привели к возникновению "потерянного" блока памяти.
Этот список достаточно подробен для того, чтобы обнаружить точное место
возникновения данной утечки памяти.
Полезные советы при работе с memcheck
Отсутствие информации об исходном коде
При анализе программ, которые используют подгружаемые модули
может возникнуть ситуация, когда valgrind не может выдать
информацию о коде, приведшем к выделению "потерянных" блоков
памяти. Это происходит из-за того, что в момент, когда valgrind
собирает информацию о выделении и использовании памяти, модуль
может быть уже выгружен, и valgrind не может найти отладочную
информацию. Это приводит к тому, что при выводе отчета такие
места будут отмечены знаками ???, что означает отсутствие
отладочной информации. Чтобы избежать этого, необходимо не
использовать вызовы dlclose для выгрузки модуля до завершения
программы (но это, соответственно, потребует изменение исходного
кода программы).
Профилирование программ
Профилирование программ может осуществляться с помощью двух модулей --
callgrind и cachegrind. Каждый из них собирает разную информацию. При
этом нельзя полагаться на результаты работы только одного из модулей,
лучше проводить поиск "узких" мест в программах на основе анализа
вывода каждого из модулей.
cachegrind
Модуль cachegrind проводит сбор статистики по попаданию в кэш первого и
второго уровней процессора при выполнении операций чтения и записи
данных и инструкций программ, а также статистику по работе модуля
предсказания ветвлений в программах. По умолчанию, сбор статистики о
предсказании ветвления инструкций (branch prediction) не проводится, и
если вы хотите получить эти данные, то вы должны указать опцию
--branch-sim со значением yes. Кроме этого, пользователь имеет
возможность указания дополнительных опций, например, задающих размеры
кэшей и т.п.
Результаты собранные данным модулем по умолчанию выводятся в файл с
именем cachegrind.out.<pid> (pid -- идентификатор процесса). Если вы
хотите использовать другое имя файла, то можете воспользоваться опцией
--cachegrind-out-file.
После завершения программы, valgrind выдаст таблицу с суммарными
данными, собранными во время выполнения программы, например:
I refs: 1,873,023
I1 misses: 2,222
L2i misses: 2,057
I1 miss rate: 0.11%
L2i miss rate: 0.10%
D refs: 808,914 (578,685 rd + 230,229 wr)
D1 misses: 9,785 ( 9,212 rd + 573 wr)
L2d misses: 5,119 ( 4,757 rd + 362 wr)
D1 miss rate: 1.2% ( 1.5% + 0.2% )
L2d miss rate: 0.6% ( 0.8% + 0.1% )
L2 refs: 12,007 ( 11,434 rd + 573 wr)
L2 misses: 7,176 ( 6,814 rd + 362 wr)
L2 miss rate: 0.2% ( 0.2% + 0.1% )
в которой перечислены данные по выборке инструкций и данных
процессором. А в файл cachegrind.out (достаточно большой даже для очень
простых программ), попадут детальные данные, которые можно использовать
для поиска "узких" мест в программах. Удобным средством анализа
является программа kcachegrind, но и в поставке valgrind есть программа
cg_annotate, которая позволяет проводить анализ производительности
программ.
В руководстве пользователя valgrind есть описание формата
данных, выводимых модулем cachegrind, так что пользователь может писать
и свои анализаторы.
Для получения данных, в качестве параметров программы cg_annotate
указывают имя файла с результатами, собранными cachegrind, а также
(опционально) список файлов с исходными текстами, которые будут
аннотированы по результатам работы cg_annotate. Чтобы не указывать все
файлы с исходными текстами вручную, cg_annotate принимает опцию --auto
со значением yes, и автоматически ищет нужные файлы (с помощью опции -I
можно указать каталоги, в которых должен производиться поиск файлов).
cg_annotate выводит на экран аннотированные исходные тексты, в которых
для каждой функции (и строки кода, в зависимости от опций) указывается
количество операций чтения или записи, а также другая статистика по
работе программы. Используя эту информацию, разработчик получает
возможность оптимизировать места, наиболее сильно влияющие на работу
программы.
callgrind
Данный модуль позволяет собрать информацию о дереве вызова функций в
программе. По умолчанию он собирает данные о количестве выполненных
инструкций, зависимостях между вызывающей и вызываемой функциями и
количество вызовов конкретных функций. Кроме того, можно включить
эмуляцию кэшей, аналогичную cachegrind, что позволит собрать данные о
доступе к памяти.
Данные собранные модулем выводятся в файл callgrind.out.<pid>, который
затем может быть проанализирован с помощью программ kcachegrind или
callgrind_annotate (входящей в поставку valgrind).
callgrind_annotate выводит на экран данные о выполнении различных
функций, и может представлять их в различном виде, в зависимости от
указанных опций. Также как и для cg_annotate, можно указать опцию
--auto, чтобы избежать указания файлов с исходными текстами вручную.
По умолчанию, callgrind выводит информацию один раз, в конце выполнения
программы. Но пользователи, которым это нужно, могут использовать
программу callgrind_control из поставки valgrind для получения
промежуточных данных по запросу, или периодически.
Анализ выделения памяти в программе
Для анализа выделения памяти в программах используется модуль massif.
Он собирает сведения не только о размерах блоков, выделяемых
программой, но также и о том, сколько дополнительной памяти потребуется
для хранения служебной информации.
После завершения программы под управлением massif, valgrind выдает
краткую сводку использования памяти, а подробные данные выводятся в
файл massif.out.<pid>. Для анализа этих данных может использоваться
программа ms_print, входящая в поставку valgrind. Эта программа может
выдавать данные в виде графиков, демонстрирующих выделение памяти в
программе в процессе работы, например вот так:
MB
12.46^ #... . ...
| #::: : :::
| :#::: : :::
| .@ :#::: : :::
| . :@ :#::: : :::
| .: :@ :#::: : ::::
| ::: :@ :#::: : ::::
| : ::: :@ :#::: : ::::
| . : ::: :@ :#::: : ::::
| . : : ::: :@ :#::: : ::::
| ,. .,..,.. ,.., . ,.: : : ::: :@ :#::: : ::::
| . :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::,
| . :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| .:: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| . :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| ,: : :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| . :@: : :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| ,:: :@: : :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| ,: @:: :@: : :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
| : @: @:: :@: : :::: :: :@: :@::@:: @::@ : @:: : : ::: :@ :#::: : ::::@
0 +----------------------------------------------------------------------->Gi
0 3.539
Number of snapshots: 56
Detailed snapshots: [3, 5, 9, 19, 22, 25, 28, 31, 33, 42, 44 (peak), 54]
Пользователь может использовать дополнительные опции massif для
управления частотой снятия снапшотов, их количеством, списком функций,
для которых будет производиться анализ (можно, например, отслеживать
только new или malloc) и т.п.
Поиск ошибок синхронизации
За поиск этого класса ошибок отвечает модуль helgrind. Он позволяет
найти ошибки синхронизации в программах на языках C, C++ & Fortran,
использующих POSIX Thread API. Helgrind помогает обнаружить следующие
классы ошибок:
* потенциальные блокировки (deadlocks), возникающие из-за
неправильного порядка выставления блокировок
* повреждение данных (data races) из-за неправильных, или
отсутствующих блокировок на доступ к памяти
* неправильное использование функций POSIX API. Этот класс ошибок
включает в себя разные ошибки, например рекурсивное блокирование не
рекурсивного мутекса, освобождение памяти, хранящей блокировку
(мутекс) и т.д.
Helgrind позволяет найти эти ошибки за счет отслеживания состояния
каждого из байтов памяти, используемой программой, а также за счет
мониторинга различных событий, связанных с выполнением нитей (threads)
-- их создания и завершения, получение и освобождения блокировок и т.п.
Формат вывода информации немного похож на формат вывода ошибок модулем
memcheck:
Possible data race during write of size 4 at 0x42B6530
at 0x41E5027: _IO_file_sync@@GLIBC_2.1 (fileops.c:923)
by 0x41D9B5F: fflush (iofflush.c:43)
by 0x40C6941: __gnu_cxx::stdio_sync_filebuf<.... >::sync() (stdio_sync_filebuf.h:156)
by 0x40D2B93: std::basic_streambuf<... >::pubsync() (streambuf:255)
by 0x40C8573: std::ostream::flush() (ostream.tcc:514)
by 0x40C863A: std::basic_ostream<... >& std::flush<...>(std::basic_ostream<...>&) (ostream:520)
by 0x40CD025: std::basic_ostream<... >& std::endl<... >(std::basic_ostream<... >&) (ostream:499)
by 0x40C74A0: std::ostream::operator<<(std::ostream& (*)(std::ostream&)) (ostream.tcc:72)
by 0x804A208: f1(int) (test-threads.cpp:19)
by 0x804A534: void boost::_bi::list1<... >::operator()<...>(.....) (bind.hpp:232)
by 0x804A578: boost::_bi::bind_t<...>::operator()() (bind_template.hpp:20)
by 0x804A592: boost::detail::function::void_function_obj_invoker0<...>::invoke(...&) (function_template.hpp:158)
Old state: shared-readonly by threads #2, #3
New state: shared-modified by threads #2, #3
Reason: this thread, #3, holds no consistent locks
Location 0x42B6530 has never been protected by any lock
В данном примере helgrind указывает на возможное повреждение данных при
одновременном выводе данных на экран несколькими нитями исполнения.
Кроме стека вызова функций, приводящего к ошибке, также выдается
состояние памяти до и после возникновения ошибки (old и new state), а
также причина возникновения ошибки (в нашем случае -- отсутствие
блокировок для данного участка памяти).
Для управления поведением модуля определено некоторое количество опций,
описание которых вы можете найти в руководстве пользователя, но
значений по умолчанию обычно достаточно для нормальной проверки
пользовательских программ.
Дополнительные программы для работы с valgrind
Для упрощения работы с valgrind, имеется несколько внешних программ,
выполняющих ту или иную задачу.
* Программа alleyoop позволяет работать с valgrind, используя
графический интерфейс пользователя. Она позволяет выбрать нужную
утилиту, и запустить valgrind с нужными опциями, а затем
проанализировать вывод valgrind, и обеспечить навигацию по коду,
который вызывает ошибки. В настоящее время поддерживается работа с
модулями memcheck, helgrind и cachegrind.
* Для визуализации данных, полученных от модулей callgrind и
cachegrind, существует программа -- kcachegrind, которая
отображает полученные данные, и позволяет выполнять навигацию по
исходному коду программы. Программа позволяет отображать собранные
данные различными способами -- в виде таблиц, карт и графов вызова
функций. Пример отображения этих данных вы можете видеть на
рисунке.
ExmapExmap -- это утилита, которая позволяет провести анализ реального
использования оперативной памяти запущенными процессами. Для получения
этой информации используется модуль для ядра Linux, информация из
которого может быть получена пользовательской программой.
Для файлов с отладочной информацией, можно получать информацию
детализированную на уровне отдельных объектов программы (symbol-level),
а для остальных программ информация детализируется на уровне секций
(section-level).
Для анализа собранных данных существует несколько программ. В поставку
exmap входит программа gexmap, которая использует графический интерфейс
пользователя для отображения информации. Кроме того, существует
программа exmap-console, которая позволяет получать информацию от
модуля ядра, используя командно-строковые утилиты.
Установка
В некоторых дистрибутивах Linux, этот пакет можно установить из
репозитория, а для остальных пользователей надо лишь скачать исходные
тексты с сайта exmap, распаковать их и выполнить пару команд:
make
для сборки модуля ядра и и программы gexmap
sudo insmod kernel/exmap.ko
для загрузки модуля в ядро
После выполнения этих команд, пользователь может начать использовать
программы-анализаторы для получения информации о запущенных процессах.
gexmap
В поставку exmap входит графическая утилита gexmap, которая
используется для графического представления данных, собранных модулем
ядра. Пример использования этой программы вы можете увидеть на
скриншоте.
Как видно на рисунке, информация разбивается на несколько частей:
* в первом окне отображается сколько памяти использует то или иное
приложение
* во втором окне можно просмотреть информацию об использовании памяти
тем или иным компонентом выбранной программы -- выделение областей
памяти на "куче" (heap), информация по использованию памяти
динамическими библиотеками и т.д.
* третье окно представляет информацию о выбранном компоненте с
разбивкой по секциям файла в формате ELF
* ну а в четвертом окне, соответственно, приведена информация об
объектах в выбранной секции файла в формате ELF
Таким образом, пользователь может получать информацию о потреблении
памяти программой и/или отдельным компонентом программы.
exmap-console
Использование графического интерфейса не всегда удобно для
разработчика. Поэтому, была разработана отдельная программа
exmap-console, которая реализует командно-строковую утилиту для
получения информация от модуля ядра, а также программу для удаленного
доступа к модулю ядра.
Установка данного пакета достаточно проста -- необходимо скачать
исходные тексты с домашней страницы проекта, распаковать и
выполнить стандартную последовательность (./configure ; make ; make
install) для сборки и установки программы.
После сборки пользователю становятся доступны несколько команд:
exmap
командно-строковая утилита, которая выполняет обращение к модулю
ядра, и выдает полученную информацию в текстовом виде. Эта
программа может использоваться в интерактивном режиме. Кроме
того, с помощью этой же программы осуществляется доступ к
информации на удаленном сервере, на котором запущена программа
exmapserver.
exmapserver
предоставляет возможность удаленного доступа к информации
собранной модулем ядра.
exmapd
используется для периодического запуска exmap со сбором
информации в указанном каталоге.
Подробную информацию об использовании этих программ вы можете найти в
справочных страницах, которые поставляются в составе пакета.
dmallocdmalloc -- библиотека для выявления проблем работы с памятью --
утечек памяти, обращение к областям памяти за границами выделенных
блоков (также как и Electric Fence), а также сбор различной
статистики по выделению памяти. Данная библиотека переопределяет
стандартные функции выделения и освобождения памяти. Библиотека
написана в портабельном стиле, и может быть использована на Linux,
Solaris и других Unix-совместимых системах, включая MS Windows (в среде
Cygwin). Библиотека может использоваться для выявления проблем и в
многопоточных программах.
На мой взгляд, в настоящее время использование данной библиотеки не
особо оправдано, поскольку имеются более мощные альтернативы --
Valgrind и Google Performance Tools.
Установка
Также как и многие пакеты для Unix, dmalloc собирается достаточно
просто -- необходимо скачать исходные тексты с сайта, распаковать и
выполнить три команды:
./configure
make
make install
Если вы будете использовать dmalloc с многопоточными программами, то
вам нужно будет передать параметр --enable-threads скрипту configure.
Использование
Также как и другие пакеты, предназначенные для поиска ошибок работы с
памятью, использовать dmalloc очень просто -- вам нужно слинковать
библиотеку с программой, или загрузить ее с помощью LD_PRELOAD.
Стоит отметить, что существует две версии библиотеки: для программ на
языке C -- libdmalloc, и для программ на C++ -- libdmallocthcxx.
Вторая библиотека кроме поддержки стандартных malloc & free, также
поддерживает операторы C++ для работы с памятью -- new, new [] и т.п.
Для получения детальной информации об файлах с исходными
текстами и конкретных строках, вызвавших появление ошибок, пользователь
может скомпилировать свою программу подключив заголовочный файл
dmalloc.h. Об этом можно подробней прочитать в документации.
Библиотеки предназначенные для работы с многопоточных
программами имеют суффикс th, например: libdmallocth.so.4
Аналогично другим пакетам, libdmalloc также получает информацию о
настройках из переменных среды. Некоторым отличием является то, что в
состав пакета входит командно-строковая утилита dmalloc, которую можно
использовать для установки конкретных переменных, не указывая их имен и
не вдаваясь в особенности синтаксиса командного процессора. В
типовой ситуации, dmalloc используется следующим образом:
Пользователь может также задать опции, которые будут использоваться
при каждом запуске dmalloc. Для этого надо указать необходимые опции
в файл настройки ~/.dmallocrc.
eval `dmalloc -b options debug_level`
LD_PRELOAD=libmalloc.so.4 ./your-program
наличие команды eval необходимо, поскольку если просто вызвать dmalloc,
то он выведет на экран команды устанавливающую переменную среды
DMALLOC_OPTIONS, а не установит их. В зависимости от
используемого командного процессора dmalloc необходимо указать
конкретную опцию: -b для процессоров, совместимых по синтаксису с sh --
bash, zsh и другие, и -c для использующих csh-совместимый синтаксис.
В документации приводится несколько примеров команд для
командного процессора, которые позволят пользователю не указывать явно
команду eval.
Кроме того, пользователь должен указать уровень подробности с которой
будет проводиться проверка (debug level). Существует несколько
предопределенных значений (low, runtime, medium и high) которые задают
предопределенные наборы проверок (от минимальных до максимальных), но
пользователь может задать и собственные наборы проверок, используя
опцию -d утилиты dmalloc для задания числа, описывающего нужные
проверки, или опцию -p, которая позволяет задать проверки используя их
имена, например:
eval `dmalloc -d 0 -l log_file -p log-non-free -p check-fence -p check-funcs`
В данном случае, опция -d обнуляет выставленные флаги, а затем с
помощью опций -p формируется новый набор флагов проверок. Полный
список доступных проверок можно получить передав dmalloc опции -DV, что
приведет к выводу названий проверок, их кратких описаний, а также
числовых значений, им соответствующих.
Стоит отметить, что при последовательных вызовах dmalloc происходит
сохранение значений, которые не изменяются текущим вызовом.
Пользователь может явно указать на то, что не указанные значения были
удалены, используя опцию -r, или сохранены, если задана опция -k.
Среди других опций, передаваемых dmalloc, наиболее часто используемой
является опция -l (или --logfile), которая позволяет указать имя файла
в который будет выводиться отчет.
Анализ результатов работы
После завершения работы анализируемой программы в указанном файле мы
получим информацию об ошибках, найденных во время работы. В приведенном
ниже примере, программа предпринимала попытку освободить уже
освобожденный блок памяти, что и было указано в предпоследней строке
отчета:
В данном примере, тестовая программа была скомпилирована с
подключением файла dmalloc.h, чтобы библиотека получила данные о номерах строк в файле. В том
случае, если вы не можете пересобрать исходные тексты, то вы можете
получить информацию о номерах строк используя gdb, как это описано в
документации.
..... описание параметров, переданных libdmalloc
1204541422: 3: ERROR: free: cannot locate pointer in heap (err 22)
1204541422: 3: error details: finding address in heap
1204541422: 3: pointer '0xb7fc8ff0' from 'test-double-free.c:7' prev access 'unknown'
Если в программе имеются ошибки, приводящие к утечкам памяти, то в
отчете появятся строки, подобные приведенным в примере:
1204542221: 3: Dumping Not-Freed Pointers Changed Since Start:
1204542221: 3: not freed: '0xb7fdffc8|s1' (10 bytes) from 'test-memleak.c:6'
1204542221: 3: total-size count source
1204542221: 3: 10 1 test-memleak.c:6
1204542221: 3: 10 1 Total of 1
Помимо вывода данных о найденных ошибках, libdmalloc выводит данные о
выделении памяти, количестве вызовов конкретных функций и т.п. вещах,
например:
1204542469: 3: Dumping Chunk Statistics:
1204542469: 3: basic-block 4096 bytes, alignment 8 bytes
1204542469: 3: heap address range: 0xb7f01000 to 0xb7f0a000, 36864 bytes
1204542469: 3: user blocks: 1 blocks, 4074 bytes (11%)
1204542469: 3: admin blocks: 8 blocks, 32768 bytes (89%)
1204542469: 3: total blocks: 9 blocks, 36864 bytes
1204542469: 3: heap checked 0
1204542469: 3: alloc calls: malloc 2, calloc 0, realloc 0, free 1
1204542469: 3: alloc calls: recalloc 0, memalign 0, valloc 0
1204542469: 3: alloc calls: new 0, delete 0
1204542469: 3: current memory in use: 10 bytes (1 pnts)
1204542469: 3: total memory allocated: 20 bytes (2 pnts)
1204542469: 3: max in use at one time: 10 bytes (1 pnts)
1204542469: 3: max alloced with 1 call: 10 bytes
1204542469: 3: max unused memory space: 22 bytes (68%)
1204542469: 3: top 10 allocations:
1204542469: 3: total-size count in-use-size count source
1204542469: 3: 10 1 10 1 test-memleak.c:6
1204542469: 3: 10 1 0 0 test-memleak.c:4
1204542469: 3: 20 2 10 1 Total of 2
Electric FenceElectric Fence -- библиотека предназначенная для поиска ошибок
работы с памятью в программах, использующих стандартные функции
библиотеки языка C -- malloc, calloc, realloc и т.п. Данная библиотека
написана Bruce Perens в 1990-х годах и является широко известным
средством для поиска ошибок, хотя в последние годы у нее появились
очень мощные конкуренты в лице Valgrind и Google Performace Tools.
С помощью Electric Fence вы можете производить поиск двух типов ошибок:
* доступ к уже освобожденной памяти
* доступ к памяти за границами выделенных блоков
Для выполнения этих задач Electric Fence использует механизмы защиты
участков виртуальной памяти, предоставляемых операционной
системой -- для ее работы требуется наличие функций mprotect &
mmap.
Работа библиотеки была протестирована на Linux, Solaris, AIX,
HP-UX и еще некоторых операционных системах. В принципе, библиотека
должна работать на всех POSIX-совместимых системах, реализующих функции
mmap & mprotect.
Работа с библиотекой
Использование библиотеки достаточно просто -- необходимо слинковать
вашу программу с библиотекой efence или загрузить ее с помощью
LD_PRELOAD, например:
# ulimit -c unlimited
# LD_PRELOAD=/usr/lib/libefence.so.0.0 ./your_program
Нельзя использовать Electric Fence вместе с другими
библиотеками, которые предназначены для поиска ошибок работы с памятью.
Первая строка необходима для того, чтобы разрешить генерацию
посмертного дампа состояния программы, что по умолчанию запрещено на
большинстве систем.
Если код пытается получить доступ к памяти за пределами выделенного
блока, или осуществить доступ к освобожденному блоку памяти, то это
приводит к возникновению аппаратной ошибки и аварийному завершению
программы (segmentation fault). После этого вы можете использовать
отладчик для того, чтобы посмотреть стек вызова функций приведший к
возникновению ошибки. Например, рассмотрим выполнение следующей
программы, содержащую двойное освобождение памяти:
#include <stdlib.h>
int main(int argc, char** argv) {
char* mem=(char*)(malloc(10));
free(mem);
free(mem);
return 0;
}
Скомпилируем программу с отладочной информацией и запустим ее под
управлением Electric Fence:
# LD_PRELOAD=/usr/lib/libefence.so.0.0 ./test-double-free
Electric Fence 2.1 Copyright (C) 1987-1998 Bruce Perens.
ElectricFence Aborting: free(b7d31ff4): address not from malloc().
5928 illegal hardware instruction (core dumped) LD_PRELOAD=/usr/lib/libefence.so.0.0 ./test-double-free
После завершения программы в текущем каталоге вы найдете файл core,
который и содержит посмертный дамп состояния программы. С помощью
отладчика заглянем в полученный дамп чтобы найти место, в котором
возникла ошибка:
# gdb ./test-double-free core
..... пропущенные строки с сообщениями о загрузке библиотек ....
Core was generated by `./test-double-free'.
Program terminated with signal 4, Illegal instruction.
#0 0xb7eea410 in __kernel_vsyscall ()
(gdb) bt
#0 0xb7eea410 in __kernel_vsyscall ()
#1 0xb7da2366 in kill () from /lib/tls/i686/cmov/libc.so.6
#2 0xb7ee5c54 in EF_Abort () from /usr/lib/libefence.so.0.0
#3 0xb7ee50a2 in free () from /usr/lib/libefence.so.0.0
#4 0x080483da in main () at test-double-free.c:6
С помощью команды bt отладчика gdb мы получаем стек вызова функций,
который нам показывает, что ошибка возникла в строке 6 файла с исходным
текстом test-double-free.c. Остается исправить эту ошибку и снова
запустить программу на выполнение.
Управление поведением библиотеки
В большинстве случаев для использования библиотеки достаточно
стандартных настроек. Но если необходимо, то пользователь может
изменить поведение библиотеки с помощью определенных переменных среды:
EF_PROTECT_BELOW
При значении этой переменной равном 1 включается режим проверки
доступа к областям памяти, которые находятся до выделенного
блока (по умолчанию проверяется доступ к памяти за верхней
границей выделенного блока)
EF_PROTECT_FREE
При ненулевом значении, Electric Fence прекращает возвращать
освобождаемую память в пул свободной памяти, откуда она снова
может быть выделена программе. Стоит отметить, что при работе в
данном режиме, программе может понадобиться значительно большее
количество свободной памяти.
EF_ALLOW_MALLOC_0
При ненулевом значении этой переменной, Electric Fence разрешает
выделение блоков памяти с длиной 0, что по умолчанию приводит к
возникновению ошибки.
EF_ALIGNMENT
Устанавливает значение, которое будет использоваться для
выравнивания границ выделяемых блоков памяти. По умолчанию это
значение равно sizeof(int). Установка нестандартных значений
может быть полезна для обнаружения некоторых видов ошибок, но
перед тем как это делать прочитайте раздел "Word-alignment and
Overrun Detection" в справочной странице libefence.3, входящей в
состав пакета.
EF_FILL
позволяет указать число от 0 до 255, которое будет
использоваться как заполнитель для выделяемых областей памяти
Использование EF_PROTECT_BELOW очень полезно для проверки того, не
происходит ли доступ к областям памяти, которые находятся до
выделенного блока. Обычно проверка программы происходит следующим
образом - сначала исправляются все ошибки, возникающие при
использовании стандартных настроек, а затем, включается режим
EF_PROTECT_BELOW и все проверки выполняются снова.
==6504== Memcheck, a memory error detector
==6504== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==6504== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==6504== Command: src/fuck
==6504==
valgrind: Fatal error at startup: a function redirection
valgrind: which is mandatory for this platform-tool combination
valgrind: cannot be set up. Details of the redirection are:
valgrind:
valgrind: A must-be-redirected function
valgrind: whose name matches the pattern: strlen
valgrind: in an object with soname matching: ld-linux-x86-64.so.2
valgrind: was not found whilst processing
valgrind: symbols from the object with soname: ld-linux-x86-64.so.2
valgrind:
valgrind: Possible fixes: (1, short term): install glibc's debuginfo
valgrind: package on this machine. (2, longer term): ask the packagers
valgrind: for your Linux distribution to please in future ship a non-
valgrind: stripped ld.so (or whatever the dynamic linker .so is called)
valgrind: that exports the above-named function using the standard
valgrind: calling conventions for this platform.
valgrind:
valgrind: Cannot continue -- exiting now. Sorry.