Merge pull request #13 from cryb-to/cryb-test

Add a minimal test for the cryb-test allocator and improve leak detection.
This commit is contained in:
Dag-Erling Smørgrav 2017-05-11 00:32:36 +02:00 committed by GitHub
commit 47d81e3dcc
7 changed files with 193 additions and 18 deletions

View file

@ -75,7 +75,7 @@ void t_printv_hex(const uint8_t *, size_t);
void t_printv(const char *, ...) void t_printv(const char *, ...)
CRYB_PRINTF(1, 2); CRYB_PRINTF(1, 2);
#ifdef _IONBF /* proxy for <stdio.h> */ #if CRYB_TEST_HAVE_STDIO
/* /*
* Convenience functions for temp files * Convenience functions for temp files
*/ */
@ -146,6 +146,7 @@ size_t t_malloc_snapshot(void *, size_t);
#if CRYB_TEST_HAVE_STDIO #if CRYB_TEST_HAVE_STDIO
void t_malloc_printstats(FILE *); void t_malloc_printstats(FILE *);
#endif #endif
unsigned long t_malloc_outstanding(void);
extern struct t_test t_memory_leak; extern struct t_test t_memory_leak;
/* /*
@ -163,7 +164,7 @@ void t_assertion_failed(const char *, const char *, unsigned int,
t_assertion_failed(__func__, __FILE__, \ t_assertion_failed(__func__, __FILE__, \
__LINE__, "%s", #exp); \ __LINE__, "%s", #exp); \
} while (0) } while (0)
CRYB_END CRYB_END
#endif #endif

View file

@ -251,6 +251,16 @@ t_main(t_prepare_func t_prepare, t_cleanup_func t_cleanup,
size_t nt; size_t nt;
int opt; int opt;
/*
* Enable memory leak detection by default if and only if the heap
* is empty at the start of main(), since otherwise we have no
* reason to trust that it will be empty after we've run the tests
* and cleaned up what we could.
*/
leaktest = t_malloc_outstanding() ?
t_str_is_true(getenv("CRYB_LEAKTEST")) :
!t_str_is_false(getenv("CRYB_LEAKTEST"));
/* make all unintentional allocation failures fatal */ /* make all unintentional allocation failures fatal */
t_malloc_fatal = 1; t_malloc_fatal = 1;
@ -266,13 +276,6 @@ t_main(t_prepare_func t_prepare, t_cleanup_func t_cleanup,
else else
t_progname = argv[0]; t_progname = argv[0];
/* check for leaks, unless doing coverage analysis */
#if CRYB_COVERAGE
leaktest = t_str_is_true(getenv("CRYB_LEAKTEST"));
#else
leaktest = !t_str_is_false(getenv("CRYB_LEAKTEST"));
#endif
/* parse command line options */ /* parse command line options */
while ((opt = getopt(argc, argv, "Llv")) != -1) while ((opt = getopt(argc, argv, "Llv")) != -1)
switch (opt) { switch (opt) {
@ -314,11 +317,15 @@ t_main(t_prepare_func t_prepare, t_cleanup_func t_cleanup,
} }
free(t_plan); free(t_plan);
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
/* check for memory leaks */
if (leaktest) { if (leaktest) {
if (t_verbose) if (t_verbose)
t_malloc_printstats(stderr); t_malloc_printstats(stderr);
t_run_test(&t_memory_leak, nt) ? ++pass : ++fail; t_run_test(&t_memory_leak, nt) ? ++pass : ++fail;
} }
/* result */
t_printv("%d out of %zd tests passed\n", pass, nt); t_printv("%d out of %zd tests passed\n", pass, nt);
exit(fail > 0 ? 1 : 0); exit(fail > 0 ? 1 : 0);
} }

View file

@ -293,7 +293,7 @@ t_malloc_bucket(size_t size)
* Core malloc() logic: select the correct backend based on the requested * Core malloc() logic: select the correct backend based on the requested
* allocation size and call it. * allocation size and call it.
*/ */
void * static void *
t_malloc(size_t size) t_malloc(size_t size)
{ {
@ -534,22 +534,33 @@ t_malloc_printstats(FILE *f)
nmapalloc, nmapfree, nmapalloc - nmapfree); nmapalloc, nmapfree, nmapalloc - nmapfree);
} }
/*
* Return number of outstanding allocations
*/
unsigned long
t_malloc_outstanding(void)
{
struct bucket *b;
unsigned int shift;
unsigned long n;
n = nmapalloc - nmapfree;
for (shift = BUCKET_MIN_SHIFT; shift <= BUCKET_MAX_SHIFT; ++shift) {
b = &buckets[shift];
n += b->nalloc - b->nfree;
}
return (n);
}
/* /*
* Test that fails if we leaked memory * Test that fails if we leaked memory
*/ */
static int static int
t_malloc_leaked(char **desc, void *arg CRYB_UNUSED) t_malloc_leaked(char **desc, void *arg CRYB_UNUSED)
{ {
struct bucket *b;
unsigned int shift;
unsigned long nleaked; unsigned long nleaked;
nleaked = 0; nleaked = t_malloc_outstanding();
for (shift = BUCKET_MIN_SHIFT; shift <= BUCKET_MAX_SHIFT; ++shift) {
b = &buckets[shift];
nleaked += b->nalloc - b->nfree;
}
nleaked += nmapalloc - nmapfree;
if (nleaked > 0) if (nleaked > 0)
(void)asprintf(desc, "%lu allocation(s) leaked", nleaked); (void)asprintf(desc, "%lu allocation(s) leaked", nleaked);
else else

2
t/.gitignore vendored
View file

@ -23,6 +23,7 @@
/t_hmac_sha512 /t_hmac_sha512
/t_hmac_sha512_openssl /t_hmac_sha512_openssl
/t_mac /t_mac
/t_malloc
/t_md2 /t_md2
/t_md4 /t_md4
/t_md4_openssl /t_md4_openssl
@ -57,4 +58,5 @@
/t_strlcat /t_strlcat
/t_strlcmp /t_strlcmp
/t_strlcpy /t_strlcpy
/t_test
/t_wstring /t_wstring

View file

@ -234,6 +234,14 @@ TESTS += t_rand
t_rand_LDADD = $(libt) $(librand) t_rand_LDADD = $(libt) $(librand)
endif CRYB_RAND endif CRYB_RAND
# libcryb-test
if CRYB_TEST
TESTS += t_test
t_test_LDADD = $(libt) $(libtest)
TESTS += t_malloc
t_malloc_LDADD = $(libt) $(libtest)
endif CRYB_TEST
check_PROGRAMS = $(TESTS) check_PROGRAMS = $(TESTS)
endif CRYB_TEST endif CRYB_TEST

85
t/t_malloc.c Normal file
View file

@ -0,0 +1,85 @@
/*-
* Copyright (c) 2017 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "cryb/impl.h"
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <cryb/test.h>
/*
* This test isn't very meaningful, but it does exercise the allocator.
* It's a little difficult to create meaningful tests when the code you're
* testing is the same as the code that runs your tests.
*/
static int
t_malloc_fill(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED)
{
void *head, **next;
unsigned int i;
int ret, stmf;
ret = 1;
stmf = t_malloc_fatal;
t_malloc_fatal = 0;
head = NULL;
for (i = 0; (next = malloc(sizeof *next)) != NULL; ++i) {
*next = head;
head = next;
}
t_printv("%u successful allocations\n", i);
for (; (next = head) != NULL; --i) {
head = *next;
free(next);
}
ret &= t_compare_u(0, i);
t_malloc_fatal = stmf;
return (ret);
}
static int
t_prepare(int argc, char *argv[])
{
(void)argc;
(void)argv;
t_add_test(t_malloc_fill, NULL, "fill a bucket");
return (0);
}
int
main(int argc, char *argv[])
{
t_main(t_prepare, NULL, argc, argv);
}

61
t/t_test.c Normal file
View file

@ -0,0 +1,61 @@
/*-
* Copyright (c) 2016 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "cryb/impl.h"
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <cryb/test.h>
static int
t_test_version(char **desc CRYB_UNUSED, void *arg CRYB_UNUSED)
{
return (t_compare_str(PACKAGE_VERSION, cryb_test_version()));
}
static int
t_prepare(int argc, char *argv[])
{
(void)argc;
(void)argv;
t_add_test(t_test_version, NULL, "version");
return (0);
}
int
main(int argc, char *argv[])
{
t_main(t_prepare, NULL, argc, argv);
}