]> git.draconx.ca Git - cdecl99.git/blobdiff - t/errmemwrap.c
libcdecl: Fix TLS cleanup on Windows.
[cdecl99.git] / t / errmemwrap.c
diff --git a/t/errmemwrap.c b/t/errmemwrap.c
new file mode 100644 (file)
index 0000000..85541e1
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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);
+}