/* 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); }