X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/9786185611c072036751fdfcc1731158a0c2d20c..898aa30bfb038ffeca54d6dea95f0f80fbc08f7f:/t/errmemwrap.c diff --git a/t/errmemwrap.c b/t/errmemwrap.c new file mode 100644 index 0000000..85541e1 --- /dev/null +++ b/t/errmemwrap.c @@ -0,0 +1,131 @@ +/* + * Allocation wrapper for test purposes. + * + * Copyright © 2024 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#ifndef TEST_MALLOC_HOOK +# define TEST_MALLOC_HOOK 1 +#endif +#include "cdecl-internal.h" +#undef malloc + +static union test_alloc { + union test_alloc *p; + max_align_t a; + size_t v; +} *alloc_head; + +enum { + ALLOC_NEXT_PTR, + ALLOC_STATE, + ALLOC_SIZE, + ALLOC_PAYLOAD +}; + +#define ALLOC_STATE_ALLOCATED 0x7143u +#define ALLOC_STATE_FREED 0xdeadu + +/* + * Hook for testing allocation behaviour of the library, enabling a basic + * verification in test cases that the library does not leak memory. + * + * The library sources must be recompiled with -DTEST_MALLOC_HOOK to + * make use of this functionality. + * + * In this implementation, allocated memory is never freed, instead just + * marked with an indication that the memory is no longer live. + */ +void *test_realloc_hook(void *p, size_t n) +{ + union test_alloc *old_alloc = p, *alloc; + + if (old_alloc) { + old_alloc = &old_alloc[-ALLOC_PAYLOAD]; + if (old_alloc[ALLOC_STATE].v != ALLOC_STATE_ALLOCATED) { + printf("Bail out! %p is not a live allocation!\n", p); + exit(99); + } + + old_alloc[ALLOC_STATE].v = ALLOC_STATE_FREED; + printf("# %p freed\n", p); + } + + if (!n) + return NULL; + + n = (n + sizeof *alloc - 1) / sizeof *alloc; + alloc = malloc((n + ALLOC_PAYLOAD) * sizeof *alloc); + if (!alloc) + return NULL; + + alloc[ALLOC_NEXT_PTR].p = alloc_head; + alloc_head = alloc; + + alloc[ALLOC_STATE].v = ALLOC_STATE_ALLOCATED; + alloc[ALLOC_SIZE].v = n * sizeof *alloc; + + if (old_alloc) { + n = old_alloc[ALLOC_SIZE].v; + memcpy(&alloc[ALLOC_PAYLOAD], &old_alloc[ALLOC_PAYLOAD], n); + } + + p = &alloc[ALLOC_PAYLOAD]; + printf("# %p allocated\n", p); + return p; +} + +/* + * Returns the total number of allocations that have not yet been freed. + */ +size_t test_live_allocations(void) +{ + union test_alloc *a; + size_t ret = 0; + + for (a = alloc_head; a; a = a[ALLOC_NEXT_PTR].p) { + void *p = &a[ALLOC_PAYLOAD]; + + switch (a[ALLOC_STATE].v) { + case ALLOC_STATE_ALLOCATED: + printf("# %p still live\n", p); + ret++; + break; + case ALLOC_STATE_FREED: + break; + default: + printf("Bail out! detected %p state corruption\n", p); + exit(99); + } + } + + return ret; +} + +/* + * Function called from output.c but not needed for error messaging + * (only current user of this file) + */ +const char *cdecl__token_name(unsigned token) +{ + printf("Bail out! stub cdecl__token_name called\n"); + exit(99); +}