]> git.draconx.ca Git - cdecl99.git/blob - t/errmemwrap.c
Port to use getline.h from dxcommon.
[cdecl99.git] / t / errmemwrap.c
1 /*
2  * Allocation wrapper for test purposes.
3  *
4  * Copyright © 2024 Nick Bowler
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <assert.h>
24
25 #ifndef TEST_MALLOC_HOOK
26 #  define TEST_MALLOC_HOOK 1
27 #endif
28 #include "cdecl-internal.h"
29 #undef malloc
30
31 static union test_alloc {
32         union test_alloc *p;
33         max_align_t a;
34         size_t v;
35 } *alloc_head;
36
37 enum {
38         ALLOC_NEXT_PTR,
39         ALLOC_STATE,
40         ALLOC_SIZE,
41         ALLOC_PAYLOAD
42 };
43
44 #define ALLOC_STATE_ALLOCATED 0x7143u
45 #define ALLOC_STATE_FREED     0xdeadu
46
47 /*
48  * Hook for testing allocation behaviour of the library, enabling a basic
49  * verification in test cases that the library does not leak memory.
50  *
51  * The library sources must be recompiled with -DTEST_MALLOC_HOOK to
52  * make use of this functionality.
53  *
54  * In this implementation, allocated memory is never freed, instead just
55  * marked with an indication that the memory is no longer live.
56  */
57 void *test_realloc_hook(void *p, size_t n)
58 {
59         union test_alloc *old_alloc = p, *alloc;
60
61         if (old_alloc) {
62                 old_alloc = &old_alloc[-ALLOC_PAYLOAD];
63                 if (old_alloc[ALLOC_STATE].v != ALLOC_STATE_ALLOCATED) {
64                         printf("Bail out! %p is not a live allocation!\n", p);
65                         exit(99);
66                 }
67
68                 old_alloc[ALLOC_STATE].v = ALLOC_STATE_FREED;
69                 printf("# %p freed\n", p);
70         }
71
72         if (!n)
73                 return NULL;
74
75         n = (n + sizeof *alloc - 1) / sizeof *alloc;
76         alloc = malloc((n + ALLOC_PAYLOAD) * sizeof *alloc);
77         if (!alloc)
78                 return NULL;
79
80         alloc[ALLOC_NEXT_PTR].p = alloc_head;
81         alloc_head = alloc;
82
83         alloc[ALLOC_STATE].v = ALLOC_STATE_ALLOCATED;
84         alloc[ALLOC_SIZE].v  = n * sizeof *alloc;
85
86         if (old_alloc) {
87                 n = old_alloc[ALLOC_SIZE].v;
88                 memcpy(&alloc[ALLOC_PAYLOAD], &old_alloc[ALLOC_PAYLOAD], n);
89         }
90
91         p = &alloc[ALLOC_PAYLOAD];
92         printf("# %p allocated\n", p);
93         return p;
94 }
95
96 /*
97  * Returns the total number of allocations that have not yet been freed.
98  */
99 size_t test_live_allocations(void)
100 {
101         union test_alloc *a;
102         size_t ret = 0;
103
104         for (a = alloc_head; a; a = a[ALLOC_NEXT_PTR].p) {
105                 void *p = &a[ALLOC_PAYLOAD];
106
107                 switch (a[ALLOC_STATE].v) {
108                 case ALLOC_STATE_ALLOCATED:
109                         printf("# %p still live\n", p);
110                         ret++;
111                         break;
112                 case ALLOC_STATE_FREED:
113                         break;
114                 default:
115                         printf("Bail out! detected %p state corruption\n", p);
116                         exit(99);
117                 }
118         }
119
120         return ret;
121 }
122
123 /*
124  * Function called from output.c but not needed for error messaging
125  * (only current user of this file)
126  */
127 const char *cdecl__token_name(unsigned token)
128 {
129         printf("Bail out! stub cdecl__token_name called\n");
130         exit(99);
131 }