diff options
author | Joe Thornber <thornber@redhat.com> | 2010-08-09 10:56:01 +0000 |
---|---|---|
committer | Joe Thornber <thornber@redhat.com> | 2010-08-09 10:56:01 +0000 |
commit | 52e1564fddf769d481ef8bc89a01c680777bcbbc (patch) | |
tree | 9ebf3a6c825029c278ec2300ab2bec33f9b7fc36 /unit-tests | |
parent | fae2c492595add56718d45efaee36438e4279600 (diff) | |
download | lvm2-52e1564fddf769d481ef8bc89a01c680777bcbbc.tar.gz lvm2-52e1564fddf769d481ef8bc89a01c680777bcbbc.tar.xz lvm2-52e1564fddf769d481ef8bc89a01c680777bcbbc.zip |
[MM] Make valgrind aware of the pool allocators
./configure with --enable-valgrind-pool to enable this.
Diffstat (limited to 'unit-tests')
-rw-r--r-- | unit-tests/mm/Makefile.in | 31 | ||||
-rw-r--r-- | unit-tests/mm/TESTS | 1 | ||||
-rwxr-xr-x | unit-tests/mm/check_results | 31 | ||||
-rw-r--r-- | unit-tests/mm/pool_valgrind_t.c | 183 |
4 files changed, 246 insertions, 0 deletions
diff --git a/unit-tests/mm/Makefile.in b/unit-tests/mm/Makefile.in new file mode 100644 index 00000000..1b3499ed --- /dev/null +++ b/unit-tests/mm/Makefile.in @@ -0,0 +1,31 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004 Red Hat, Inc. All rights reserved. +# +# This file is part of LVM2. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU General Public License v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ +VPATH = @srcdir@ + +SOURCES=\ + pool_valgrind_t.c + +TARGETS=\ + pool_valgrind_t + +include $(top_builddir)/make.tmpl +DM_LIBS = -ldevmapper $(LIBS) + +pool_valgrind_t: pool_valgrind_t.o + $(CC) $(CFLAGS) -o $@ pool_valgrind_t.o $(LDFLAGS) $(DM_LIBS) + diff --git a/unit-tests/mm/TESTS b/unit-tests/mm/TESTS new file mode 100644 index 00000000..3bf31544 --- /dev/null +++ b/unit-tests/mm/TESTS @@ -0,0 +1 @@ +valgrind pool awareness:valgrind ./pool_valgrind_t 2>&1 | ./check_results diff --git a/unit-tests/mm/check_results b/unit-tests/mm/check_results new file mode 100755 index 00000000..a7b0975a --- /dev/null +++ b/unit-tests/mm/check_results @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby1.9 + +require 'pp' + +patterns = [ + /Invalid read of size 1/, + /Invalid write of size 1/, + /Invalid read of size 1/, + /still reachable: [0-9,]+ bytes in 3 blocks/ + ] + +lines = STDIN.readlines +pp lines + +result = catch(:done) do + patterns.each do |pat| + loop do + throw(:done, false) if lines.size == 0 + + line = lines.shift + if line =~ pat + STDERR.puts "matched #{pat}" + break; + end + end + end + + throw(:done, true) +end + +exit(result ? 0 : 1) diff --git a/unit-tests/mm/pool_valgrind_t.c b/unit-tests/mm/pool_valgrind_t.c new file mode 100644 index 00000000..b430a9c1 --- /dev/null +++ b/unit-tests/mm/pool_valgrind_t.c @@ -0,0 +1,183 @@ +#include "libdevmapper.h" + +#include <assert.h> + +/* + * Checks that valgrind is picking up unallocated pool memory as + * uninitialised, even if the chunk has been recycled. + * + * $ valgrind --track-origins=yes ./pool_valgrind_t + * + * ==7023== Memcheck, a memory error detector + * ==7023== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. + * ==7023== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info + * ==7023== Command: ./pool_valgrind_t + * ==7023== + * first branch worked (as expected) + * ==7023== Conditional jump or move depends on uninitialised value(s) + * ==7023== at 0x4009AC: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t) + * ==7023== Uninitialised value was created by a client request + * ==7023== at 0x4E40CB8: dm_pool_free (in /home/ejt/work/lvm2/libdm/ioctl/libdevmapper.so.1.02) + * ==7023== by 0x4009A8: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t) + * ==7023== + * second branch worked (valgrind should have flagged this as an error) + * ==7023== + * ==7023== HEAP SUMMARY: + * ==7023== in use at exit: 0 bytes in 0 blocks + * ==7023== total heap usage: 2 allocs, 2 frees, 2,104 bytes allocated + * ==7023== + * ==7023== All heap blocks were freed -- no leaks are possible + * ==7023== + * ==7023== For counts of detected and suppressed errors, rerun with: -v + * ==7023== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) + */ + +#define COUNT 10 + +static void check_free() +{ + int i; + char *blocks[COUNT]; + struct dm_pool *p = dm_pool_create("blah", 1024); + + for (i = 0; i < COUNT; i++) + blocks[i] = dm_pool_alloc(p, 37); + + /* check we can access the last block */ + blocks[COUNT - 1][0] = 'E'; + if (blocks[COUNT - 1][0] == 'E') + printf("first branch worked (as expected)\n"); + + dm_pool_free(p, blocks[5]); + + if (blocks[COUNT - 1][0] == 'E') + printf("second branch worked (valgrind should have flagged this as an error)\n"); + + dm_pool_destroy(p); +} + +/* Checks that freed chunks are marked NOACCESS */ +static void check_free2() +{ + struct dm_pool *p = dm_pool_create("", 900); /* 900 will get + * rounded up to 1024, + * 1024 would have got + * rounded up to + * 2048 */ + char *data1, *data2; + + assert(p); + data1 = dm_pool_alloc(p, 123); + assert(data1); + + data1 = dm_pool_alloc(p, 1024); + assert(data1); + + data2 = dm_pool_alloc(p, 123); + assert(data2); + + data2[0] = 'A'; /* should work fine */ + + dm_pool_free(p, data1); + + /* + * so now the first chunk is active, the second chunk has become + * the free one. + */ + data2[0] = 'B'; /* should prompt an invalid write error */ + + dm_pool_destroy(p); +} + +static void check_alignment() +{ + /* + * Pool always tries to allocate blocks with particular alignment. + * So there are potentially small gaps between allocations. This + * test checks that valgrind is spotting illegal accesses to these + * gaps. + */ + + int i, sum; + struct dm_pool *p = dm_pool_create("blah", 1024); + char *data1, *data2; + char buffer[16]; + + + data1 = dm_pool_alloc_aligned(p, 1, 4); + assert(data1); + data2 = dm_pool_alloc_aligned(p, 1, 4); + assert(data1); + + snprintf(buffer, sizeof(buffer), "%c", *(data1 + 1)); /* invalid read size 1 */ + dm_pool_destroy(p); +} + +/* + * Looking at the code I'm not sure allocations that are near the chunk + * size are working. So this test is trying to exhibit a specific problem. + */ +static void check_allocation_near_chunk_size() +{ + int i; + char *data; + struct dm_pool *p = dm_pool_create("", 900); + + /* + * allocate a lot and then free everything so we know there + * is a spare chunk. + */ + for (i = 0; i < 1000; i++) { + data = dm_pool_alloc(p, 37); + memset(data, 0, 37); + assert(data); + } + + dm_pool_empty(p); + + /* now we allocate something close to the chunk size ... */ + data = dm_pool_alloc(p, 1020); + assert(data); + memset(data, 0, 1020); + + dm_pool_destroy(p); +} + +/* FIXME: test the dbg_malloc at exit (this test should be in dbg_malloc) */ +static void check_leak_detection() +{ + int i; + struct dm_pool *p = dm_pool_create("", 1024); + + for (i = 0; i < 10; i++) + dm_pool_alloc(p, (i + 1) * 37); +} + +/* we shouldn't get any errors from this one */ +static void check_object_growth() +{ + int i; + struct dm_pool *p = dm_pool_create("", 32); + char data[100]; + void *obj; + + memset(data, 0, sizeof(data)); + + dm_pool_begin_object(p, 43); + for (i = 1; i < 100; i++) + dm_pool_grow_object(p, data, i); + obj = dm_pool_end_object(p); + + dm_pool_destroy(p); +} + +int main(int argc, char **argv) +{ + check_free(); + check_free2(); + check_alignment(); + check_allocation_near_chunk_size(); + check_leak_detection(); + check_object_growth(); + return 0; +} |