Арнольд Роббинс - Linux программирование в примерах
Давайте посмотрим на Valgrind в действии. Помните ch15-badmem.c? (См. раздел 15.5.2.2 «Electric Fence».) Опция -b записывает в память, находящуюся вне выделенного malloc() блока. Вот что сообщает Valgrind:
$ valgrind ch15-badmem1 -b
1 ==8716== Memcheck, a.k.a. Valgrind, a memory error detector for x86-linux.
2 ==8716== Copyright (C) 2002-2003, and GNU GPL'd, by Julian Seward.
3 ==8716== Using valgrind-20030725, a program supervision framework for x86-linux.
4 ==8716== Copyright (C) 2000-2003, and GNU GPL'd, by Julian Seward.
5 ==8716== Estimated CPU clock rate is 2400 MHz
6 ==8716== For more details, rerun with: -v
7 ==8716==
8 p = <not 30 bytes>
9 ==8716== Invalid write of size 1
10 ==8716== at 0x8048466: main (ch15-badmem1.c:18)
11 ==8716== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
12 ==8716== by 0x8048368: (within /home/arnold/progex/code/ch15/ch15-badmem1)
13 ==8716== Address 0x4104804E is 12 bytes after a block of size 30 alloc'd
14 ==8716== at 0x40025488: malloc (vg_replace_malloc.с:153)
15 ==8716== by 0x8048411: main (ch15-badmem1.c:11)
16 ==8716== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
17 ==8716== by 0x8048368: (within /home/arnold/progex/code/ch15/ch15-badmem1)
18 ==8716==
19 ==8716== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
20 ==8716== malloc/free: in use at exit: 30 bytes in 1 blocks.
21 ==8716== malloc/free: 1 allocs, 0 frees, 30 bytes allocated.
22 ==8716== For a detailed leak analysis, rerun with: --leak-check=yes
23 ==8716== For counts of detected errors, rerun with: -v
(Были добавлены номера строк в выводе, чтобы облегчить обсуждение.) Строка 8 является выводом программы; остальные от Valgrind в стандартную ошибку. Сообщение об ошибке находится в строках 9–17. Она указывает, сколько байтов было записано неверно (строка 9), где это случилось (строка 10), и показывает трассировку стека. Строки 13–17 описывают, откуда была выделена память. Строки 19–23 подводят итоги.
Опция -f программы ch15-badmem1 освобождает выделенную память, а затем записывает в нее через висячий указатель. Вот что сообщает Valgrind в этом случае:
$ valgrind ch15-badmem1 -f
==8719== Memcheck, a.k.a. Valgrind, a memory error detector for x86-linux.
...
p = <not 30 bytes>
==8719== Invalid write of size 1
==8719== at 0x8048498: main (ch15-badmem1.с:21)
==8719== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
==8719== by 0x8048368: (within /home/arnold/progex/code/ch15/ch15-badmem1)
==8719== Address 0x41048024 is 0 bytes inside a block of size 30 free'd
==8719== at 0x40025722: free (vg_replace_malloc.с:220)
==8719== by 0x8048491: main (ch15-badmem1.c:20)
==8719== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
==8719== by 0x8048368: (within /home/arnold/progex/code/ch15/ch15-badmem1)
...
На этот раз в отчете указано, что запись была осуществлена в освобожденную память и что вызов free() находится в строке 20 ch15-badmem1.c.
При вызове без опций ch15-badmem1.c выделяет и использует память, но не освобождает ее. О таком случае сообщает опция —leak-check=yes:
$ valgrind --leak-check=yes ch15-badmem1
1 ==8720== Memcheck, a.k.a. Valgrind, a memory error detector for x86-linux.
...
8 p = <not 30 bytes>
9 ==8720==
10 ==8720== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
11 ==8720== malloc/free: in use at exit: 30 bytes in 1 blocks.
12 ==8720== malloc/free: 1 allocs, 0 frees, 30 bytes allocated.
...
16 ==8720==
17 ==8720== 30 bytes in 1 blocks are definitely lost in loss record 1 of 1
18 ==8720== at 0x40025488: malloc (vg_replace_malloc.c:153)
19 ==8720== by 0x8048411: main (ch15-badmem1.c:11)
20 ==8720== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
21 ==8720== by 0x8048368: (within /home/arnold/progex/code/ch15/ch15-badmem1)
22 ==8720==
23 ==8720== LEAK SUMMARY:
24 ==8720== definitely lost: 30 bytes in 1 blocks.
25 ==8720== possibly lost: 0 bytes in 0 blocks.
26 ==8720== still reachable: 0 bytes in 0 blocks.
27 ==8720== suppressed: 0 bytes in 0 blocks.
28 ==8720== Reachable blocks (those to which a pointer was found) are not shown.
29 ==8720== To see them, rerun with: --show-reachable=yes
Строки 17–29 предоставляют отчет об утечке; эта память была выделена в строке 11 ch15-badmem1.с.
Помимо отчетов о неправильном использовании динамической памяти, Valgrind может диагностировать использование неинициализированной памяти. Рассмотрим следующую программу, ch15-badmem3.c:
1 /* ch15-badmem3.c --- плохое обращение с нединамической памятью */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 int main(int argc, char **argv)
7 {
8 int a_var; /* Обе не инициализированы */
9 int b_var;
10
11 /* Valgrind не отметит это; см. текст. */
12 a_var = b_var;
13
14 /* Использование неинициализированной памяти; это отмечается. */
15 printf("a_var = %dn", a_var);
16
17 return 0;
18 }
При запуске Valgrind выдает этот (сокращенный) отчет:
==29650== Memcheck, a.k.a. Valgrind, a memory error detector for x86-linux.
...
==29650== Use of uninitialised value of size 4
==29650== at 0x42049D2A: _IO_vfprintf_internal (in /lib/i686/libc-2.2.93.so)
==29650== by 0x420523C1: _IO_printf (in /lib/1686/libc-2.2.93.so)
==29650== by 0x804834D: main (ch15-badmem3.с:15)
==29650== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
==29650==
==29650== Conditional jump or move depends on uninitialised value(s)
==29650== at 0X42049D32: _IO_vfprintf_internal (in /lib/i686/libc-2.2.93.so)
==29650== by 0x420523C1: _IO_printf (in / lib/i686/libc-2.2.93.so)
==29650== by 0x804834D: main (ch15-badmem3.c:15)
==29650== by 0x420158D3: __libc_start_main (in /lib/i686/libc-2.2.93.so)
...
a_var = 1107341000
==29650==
==29650== ERROR SUMMARY: 25 errors from 7 contexts (suppressed: 0 from 0)
==29650== malloc/free: in use at exit: 0 bytes in 0 blocks.
==29650== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==29650== For a detailed leak analysis, rerun with: --leak-check=yes
==29650== For counts of detected errors, rerun with: -v
В документации Valgrind объясняется, что копирование неинициализированных данных не выдает сообщений об ошибках. Оболочка memcheck отмечает состояние данных (неинициализированные) и отслеживает его при перемещениях данных. Таким образом, a_var считается неинициализированной, поскольку это значение было получено от b_var, которая была неинициализированной.
memcheck сообщает о проблеме лишь тогда, когда неинициализированное значение используется. Здесь это происходит в библиотеке С (_IO_vfprintf_internal()), которая должна преобразовать значение в строку, для этого, она проводит с этим значением вычисления.
К сожалению, хотя Valgrind может обнаружить использование неинициализированной памяти вплоть до уровня битов, он не может осуществлять проверки границ массивов для локальных и глобальных переменных. (Valgrind может осуществлять проверку границ для динамической памяти, поскольку он сам обрабатывает такую память, поэтому знает о начале и конце каждой области.)
В заключение, Valgrind является мощным инструментом отладки памяти. Он использовался в таких крупномасштабных, многопоточных производственных программах, как KDE 3, OpenOffice и веб-браузер Konqueror. Он конкурирует с несколькими коммерческими предложениями, а другая его версия была даже использована (совместно с эмулятором WINE[182]) для отладки программ, написанных для Microsoft Windows с использованием Visual С++! Вы можете получить Valgrind с его веб-сайта[183].
15.5.2.5. Другие отладчики malloc
Две статьи Cal Ericson в Linux Journal описывают mtrace и dmalloc, а также большинство других перечисленных ниже инструментов. Эти статьи Memory Leak Detection in Embedded Systems, выпуск 101[184], сентябрь 2002 г., и Memory Leak Detection in C++, выпуск 110[185], июнь 2003 г. Обе статьи доступны на веб-сайте Linux Journal.
Другие инструменты сходны по природе с описанными ранее.
ccmalloc
Замещающая malloc() библиотека, которая не нуждается в особой компиляции и может использоваться с С++. См. http://www.inf.ethz.ch/personal/biere/projects/ccmalloc.
malloc Марка Мораеса (Mark Moraes)
Старинная, но полнофункциональная библиотека замещения malloc(), предоставляющая возможности профилирования, трассировки и отладки. Вы можете получить ее с ftp://ftp.cs.toronto.edu/pub/moraes/malloc-1.18.tar.gz.
mpatrol
Пакет с большими возможностями настройки для отладки памяти и тестирования. См http://www.cbmamiga.demon.со.uk/mpatrol.
memwatch
Пакет, требующий использования специального заголовочного файла и опций времени компилирования. См. http://www.linkdata.se/sourcecode.html.
njamd
«Не просто еще один отладчик malloc» (Not Just Another Malloc Debugger). Эта библиотека не требует специальной компоновки с приложением; вместо этого она использует LD_PRELOAD для замены стандартных процедур. См. http://sourceforge.net/projects/njamd.
yamd
Похож на Electric Fence, но со многими дополнительными опциями. См. http://www3.hmc.edu/~neldredge/yamd.
Почти все из этих пакетов используют для точной настройки своего поведения переменные окружения. В таблице 15.1 на основе статей из Linux Journal сделана сводка различных пакетов.