/* unit-alloc-limits.c - test alloc-limits.c
*
****************************************************************
* Copyright (C) 2000 Thomas Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/bugs/panic.h"
#include "hackerlab/bugs/test-coverage.h"
#include "hackerlab/os/time.h"
#include "hackerlab/char/str.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/cmd/main.h"
static t_uchar * program_name = "unit-alloc-limits";
static t_uchar * usage = "[options]";
static t_uchar * version_string = "1.0";
#define OPTS(OP, OP2) \
OP (opt_help_msg, "h", "help", 0, \
"Display a help message and exit.") \
OP (opt_version, "V", "version", 0, \
"Display a release identifier string") \
OP2 (opt_version, 0, 0, 0, "and exit.") \
OP (opt_iterations, "i", "iterations=n", 1, \
"Perform `n' test operations.") \
OP (opt_reseed, "r", "reseed", 0, \
"Reseed the random number generator from the time of day.") \
OP2(opt_reseed, 0, 0, 0, \
"Print the new seed value.") \
OP (opt_seed, "s", "seed=n", 1, \
"Seed the random number generator with the number `n'.") \
OP (opt_threshold, "t", "threshold=n", 1, \
"Set the GC threshold.") \
OP (opt_failure_pt, "f", "failure-point=n", 1, \
"Set the allocation failure point.")
enum options
{
OPTS (OPT_ENUM, OPT_IGN)
};
struct opt_desc opts[] =
{
OPTS (OPT_DESC, OPT_DESC)
{-1, 0, 0, 0, 0}
};
static unsigned int
rnd (int bound)
{
return ((unsigned int)random ()) % bound;
}
static t_uchar ** allocated = 0;
static size_t * allocated_size = 0;
static t_uchar * aside = 0; /* memory being realloced -- should not be GCed */
static size_t aside_size = 0;
static size_t threshold = 4096;
static size_t failure_pt = 8192;
static size_t amt_used = 0;
static alloc_limits limits;
#define TEST_COVERAGE_LIST(MACRO) \
TEST_COVERAGE_ ## MACRO (alloc_gc); \
TEST_COVERAGE_ ## MACRO (alloc_no_gc); \
TEST_COVERAGE_ ## MACRO (alloc_fail); \
TEST_COVERAGE_ ## MACRO (alloc_no_fail); \
TEST_COVERAGE_ ## MACRO (realloc_gc); \
TEST_COVERAGE_ ## MACRO (realloc_no_gc); \
TEST_COVERAGE_ ## MACRO (realloc_fail); \
TEST_COVERAGE_ ## MACRO (realloc_no_fail)
TEST_COVERAGE_LIST(DECL);
static void
check_test_engine (void)
{
int x;
size_t accounted;
accounted = 0;
for (x = ar_size ((void *)allocated) - 1; x >= 0; --x)
accounted += allocated_size[x];
accounted += aside_size;
if (accounted != amt_used)
panic ("test engine error");
}
static void
free_memory (int pos)
{
check_test_engine ();
if ((pos < 0) || (pos >= ar_size ((void *)allocated)))
panic ("bad pos in free_memory");
lim_free (limits, allocated[pos]);
amt_used -= allocated_size[pos];
allocated[pos] = allocated[ar_size((void *)allocated) - 1];
allocated_size[pos] = allocated_size[ar_size((void *)allocated) - 1];
ar_pop_uchar_star (&allocated);
ar_pop_size_t (&allocated_size);
check_test_engine ();
}
static size_t want;
static int free_memory_fn_expected;
static int free_memory_fn_called;
static int can_free_memory;
static void
free_memory_fn (void * closure, size_t amt)
{
if (!free_memory_fn_expected)
panic ("unexpected call to free_memory_fn");
free_memory_fn_called = 1;
if (closure != (void *)0xac1dc0de)
panic ("bad closure in free_memory_fn");
if (want != amt)
panic ("bad amt passed to free_memory_fn");
if (can_free_memory)
while (ar_size ((void *)allocated) && (lim_in_use (limits) + amt >= lim_threshold (limits)))
free_memory (0);
}
static void
alloc_test (int force_gc, int force_fail)
{
t_uchar * answer;
if (force_gc)
{
if (amt_used < threshold)
want = threshold - amt_used + 1;
else
want = 1;
}
else
{
want = rnd (threshold - sizeof (size_t));
if (want == 0)
want = 1;
}
if (force_fail)
can_free_memory = 0;
else
can_free_memory = rnd (2);
free_memory_fn_expected = ((want + amt_used) >= threshold);
free_memory_fn_called = 0;
if (free_memory_fn_expected)
TEST_COVERED (alloc_gc);
else
TEST_COVERED (alloc_no_gc);
answer = lim_malloc (limits, want);
if (free_memory_fn_expected != free_memory_fn_expected)
panic ("free_memory_fn_expected != free_memory_fn_called");
if (!answer)
{
TEST_COVERED (alloc_fail);
if (!lim_failure_pt (limits) || (want + lim_in_use (limits) < lim_failure_pt (limits)))
panic ("allocation below failure point failed");
return;
}
else
{
TEST_COVERED (alloc_no_fail);
if (lim_failure_pt (limits) && (lim_in_use (limits) > lim_failure_pt (limits)))
panic ("allocation succeeded above failure point");
}
ar_push_uchar_star (&allocated, answer);
ar_push_size_t (&allocated_size, want);
amt_used += want;
}
static void
realloc_test (int force_gc, int force_fail)
{
int pos;
size_t will_be;
void * answer;
while (!ar_size ((void *)allocated))
alloc_test (0, 0);
pos = rnd (ar_size ((void *)allocated));
aside = allocated[pos];
aside_size = allocated_size[pos];
allocated[pos] = allocated[ar_size((void *)allocated) - 1];
allocated_size[pos] = allocated_size[ar_size((void *)allocated) - 1];
ar_pop_uchar_star (&allocated);
ar_pop_size_t (&allocated_size);
if (force_gc)
{
if (amt_used < threshold)
will_be = aside_size + (threshold - amt_used) + 1;
else
will_be = aside_size;
}
else
{
will_be = rnd (threshold);
if (will_be == 0)
will_be = 1;
}
if (force_fail)
can_free_memory = 0;
else
can_free_memory = rnd (2);
free_memory_fn_expected = ((will_be - aside_size + amt_used) >= threshold);
free_memory_fn_called = 0;
if (free_memory_fn_expected)
TEST_COVERED (realloc_gc);
else
TEST_COVERED (realloc_no_gc);
want = will_be - aside_size;
answer = lim_realloc (limits, aside, will_be);
if (free_memory_fn_expected != free_memory_fn_expected)
panic ("free_memory_fn_expected != free_memory_fn_called");
if (!answer)
{
TEST_COVERED (realloc_fail);
if (!lim_failure_pt (limits) || (will_be < aside_size) || (want + lim_in_use (limits) < lim_failure_pt (limits)))
panic ("allocation below failure point failed");
ar_push_uchar_star (&allocated, aside);
ar_push_size_t (&allocated_size, aside_size);
aside = 0;
aside_size = 0;
return;
}
else
{
TEST_COVERED (realloc_no_fail);
if (lim_failure_pt (limits) && (lim_in_use (limits) > lim_failure_pt (limits)))
panic ("allocation succeeded above failure point");
}
ar_push_uchar_star (&allocated, answer);
ar_push_size_t (&allocated_size, will_be);
amt_used += (will_be - aside_size);
aside = 0;
aside_size = 0;
}
static void
free_test (void)
{
while (!amt_used)
alloc_test (0, 0);
free_memory (0);
}
static void
init_test (void)
{
limits = make_alloc_limits ("test limits",
threshold,
failure_pt,
0,
free_memory_fn,
(void *)0xac1dc0de);
}
static void
run_tests (int iter)
{
enum test_cases
{
test_alloc,
test_realloc,
test_free,
n_tests
};
init_test ();
while (iter--)
{
check_test_engine ();
switch (rnd (n_tests))
{
default:
panic ("bogus random number");
case test_alloc:
alloc_test (0, 0);
break;
case test_realloc:
realloc_test (0, 0);
break;
case test_free:
free_test ();
break;
}
}
}
int
main (int argc, char * argv[])
{
int errn;
int o;
struct opt_parsed * option;
unsigned int iter;
option = 0;
iter = 10000;
while (1)
{
o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, version_string, 0, opt_help_msg, opt_none, opt_version);
if (o == opt_none)
break;
switch (o)
{
default:
safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
panic ("internal error parsing arguments");
usage_error:
opt_usage (2, argv[0], program_name, usage, 1);
panic_exit ();
bogus_arg:
safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string);
goto usage_error;
case opt_iterations:
if (cvt_decimal_to_uint (&errn, &iter, option->arg_string, str_length (option->arg_string)))
goto bogus_arg;
break;
case opt_reseed:
{
unsigned long t;
t = time(0);
printfmt (&errn, 1, "RNG seed == %lu\n", t);
srandom (t);
}
break;
case opt_seed:
{
unsigned long t;
if (cvt_decimal_to_ulong (&errn, &t, option->arg_string, str_length (option->arg_string)))
goto bogus_arg;
printfmt (&errn, 1, "RNG seed == %lu\n", t);
srandom (t);
break;
}
case opt_threshold:
{
unsigned long t;
if (cvt_decimal_to_ulong (&errn, &t, option->arg_string, str_length (option->arg_string)))
goto bogus_arg;
threshold = (size_t) t;
break;
}
case opt_failure_pt:
{
unsigned long t;
if (cvt_decimal_to_ulong (&errn, &t, option->arg_string, str_length (option->arg_string)))
goto bogus_arg;
failure_pt = (size_t) t;
break;
}
}
}
run_tests (iter);
alloc_test (1, 0);
alloc_test (1, 1);
realloc_test (1, 0);
realloc_test (1, 1);
TEST_COVERAGE_LIST(CHECK);
safe_printfmt (1, "%d iterations completed\n", iter);
exit (0);
}
syntax highlighted by Code2HTML, v. 0.9.1