Аргументом для скрипта является
pid процесса, котрый подозревается в утечке памяти. Листинг
memleak.d:
# cat ./memleak.d
#!/usr/sbin/dtrace -s
pid$1:libc.so.1:malloc:entry
{
self->trace = 1;
self->size = arg0;
}
pid$1:libc.so.1:malloc:return
/self->trace == 1/
{
printf("Ptr=0x%p Size=%d", arg1, self->size);
ustack();
self->trace = 0;
self->size = 0;
}
pid$1:libc.so.1:realloc:entry
{
self->trace = 1;
self->size = arg1;
self->oldptr = arg0;
}
pid$1:libc.so.1:realloc:return
/self->trace == 1/
{
printf("Ptr=0x%p Oldptr=0x%p Size=%d", arg1, self->oldptr, self->size);
ustack();
self->trace = 0;
self->size = 0;
}
pid$1:libc.so.1:calloc:entry
/self->trace == 1/
{
self->trace = 1;
self->size = arg1;
}
pid$1:libc.so.1:calloc:return
/self->trace == 1/
{
printf("Ptr=0x%p Size=%d", arg1, self->size);
ustack();
self->trace = 0;
self->size = 0;
}
pid$1:libc.so.1:free:entry
{
printf("Ptr=0x%p ", arg0);
}
Запустив для примера в одной консоли процесс архивирования домашних директорий в
/export/home с помощью
tar (pid 5418), в другой запустим D-скрипт:
# dtrace -s memleak.d 5418 > /tmp/tarlog
После того, как архив сделан, останвливаем скрипт и начинаем изучение лога
tarlog:
# cat /tmp/tarlog | more
CPU ID FUNCTION:NAME
23 45157 free:entry Ptr=0x44578
23 45152 malloc:return Ptr=0x3c5c8 Size=20
libc.so.1`malloc+0x64
libsec.so.1`acl_alloc+0x4
libsec.so.1`cacl_get+0x78
libsec.so.1`acl_get+0x14
tar`0x1d8bc
tar`0x14f6c
tar`0x1495c
tar`0x1495c
tar`0x1495c
tar`0x136c0
tar`main+0xb18
tar`_start+0x108
23 45152 malloc:return Ptr=0x44578 Size=48
libc.so.1`malloc+0x64
libsec.so.1`cacl_get+0x138
libsec.so.1`acl_get+0x14
tar`0x1d8bc
tar`0x14f6c
tar`0x1495c
tar`0x1495c
tar`0x1495c
tar`0x136c0
tar`main+0xb18
tar`_start+0x108
23 45157 free:entry Ptr=0x44578
23 45157 free:entry Ptr=0x3c5c8
23 45152 malloc:return Ptr=0x44578 Size=10240
-----
Листинг довольно большой и найти в нем строки для памяти, котрая была занята но не освободилась впоследствии, потребовало бы немало времени. Однако автор скрипта позаботился об этом, написав на
perl-e парсер лога. Вот его листинг:
# cat findleaks.pl
#!/usr/bin/perl
# findleaks.pl
use Data::Dumper;
my %hash = ();
while (<>) {
if ((/malloc:return Ptr=([^ ]*) Size=(.*)/) ||
(/calloc:return Ptr=([^ ]*) Size=(.*)/)) {
$hash{$1} = { size => $2 };
while (<>) {
last if /^$/;
$hash{$1}->{stack} .= $_;
}
}
elsif (/free:entry Ptr=([^ ]*)/) {
if (exists $hash{$1} and $hash{$1}) {
$hash{$1} = '';
}
}
elsif (/realloc:entry Ptr=([^ ]*) Oldptr=([^ ]*) Size=(.*)/) {
if ($1 eq $2) {
if (exists $hash{$1} and $hash{$1}) {
$hash{$1} = { size => $3 };
$hash{$1}->{stack} = '';
while (<>) {
last if /^$/;
$hash{$1}->{stack} .= $_;
}
}
} else {
$hash{$1} = '';
$hash{$2}= { size => $3 };
$hash{$2}->{stack} = '';
while (<>) {
last if /^$/;
$hash{$2}->{stack} .= $_;
}
}
}
}
foreach my $key (keys %hash) {
next if not $hash{$key}->{size};
print "Ptr=$key Size=", $hash{$key}->{size}, "\n";
print $hash{$key}->{stack}, "\n---------\n";
}
Запуск парсера выводит утечки памяти:
# findleaks.pl /tmp/tarlog | more
Ptr=0x445e8 Size=48
libc.so.1`malloc+0x64
libc.so.1`calloc+0x58
tar`0x1ad68
tar`0x1ae44
tar`0x1b16c
tar`0x145c4
tar`0x136c0
tar`main+0xb18
tar`_start+0x108
---------
Ptr=0x44578 Size=10240
libc.so.1`malloc+0x64
libc.so.1`calloc+0x58
tar`0x1502c
tar`0x1495c
tar`0x1495c
tar`0x1495c
tar`0x1495c
tar`0x136c0
tar`main+0xb18
tar`_start+0x108
---
Следует заметить, что подобные эксперименты по поиску
memory leaks можно проводить и при помощи библиотеки
libumem, Адам Ливенталь приводит пример ее использования:
$ LD_PRELOAD=libumem.so
$ export LD_PRELOAD
$ UMEM_DEBUG=default
$ export UMEM_DEBUG
$ /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
---
Можно пользоваться и тем и другим способом, но как мне кажется, DTrace для отслеживания процесса более удобен.