1 /* Memory allocation used during tests.
2
3 Copyright 2001-2003, 2006-2023 Free Software Foundation, Inc.
4 Contributed by the AriC and Caramba projects, INRIA.
5
6 This file is part of the GNU MPFR Library.
7
8 The GNU MPFR Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12
13 The GNU MPFR Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23 /* Note: this file originally came from GMP's tests/memory.c
24 (some features have been added). */
25
26 #define MPFR_NEED_INTMAX_H
27 #include "mpfr-test.h"
28
29 /* Each block allocated is a separate malloc, for the benefit of a redzoning
30 malloc debugger during development or when bug hunting.
31
32 Sizes passed when reallocating or freeing are checked (the default
33 routines don't care about these).
34
35 Memory leaks are checked by requiring that all blocks have been freed
36 when tests_memory_end() is called. Test programs must be sure to have
37 "clear"s for all temporary variables used. */
38
39 /* Note about error messages
40 -------------------------
41 Error messages in MPFR are usually written to stdout. However, those
42 coming from the memory allocator need to be written to stderr in order
43 to be visible when the standard output is redirected, e.g. in the tests
44 of I/O functions (like tprintf). For consistency, all error messages in
45 this file should be written to stderr. */
46
47 struct header {
48 void *ptr;
49 size_t size;
50 struct header *next;
51 };
52
53 /* The memory limit can be changed with the MPFR_TESTS_MEMORY_LIMIT
54 environment variable. This is normally not necessary (a failure
55 would mean a bug), thus not recommended, for "make check". But
56 some test programs can take arguments for particular tests, which
57 may need more memory. This variable is exported, so that such
58 programs may also change the memory limit. */
59 size_t tests_memory_limit = DEFAULT_MEMORY_LIMIT;
60
61 static struct header *tests_memory_list;
62 static size_t tests_total_size = 0;
MPFR_LOCK_DECL(mpfr_lock_memory)63 MPFR_LOCK_DECL(mpfr_lock_memory)
64
65 static void *
66 mpfr_default_allocate (size_t size)
67 {
68 void *ret;
69 ret = malloc (size);
70 if (MPFR_UNLIKELY (ret == NULL))
71 {
72 fprintf (stderr, "[MPFR] mpfr_default_allocate(): "
73 "can't allocate memory (size=%" MPFR_INTMAX_FSPEC "u)\n",
74 (mpfr_uintmax_t) size);
75 fflush (NULL);
76 abort ();
77 }
78 return ret;
79 }
80
81 static void *
mpfr_default_reallocate(void * oldptr,size_t old_size,size_t new_size)82 mpfr_default_reallocate (void *oldptr, size_t old_size, size_t new_size)
83 {
84 void *ret;
85 ret = realloc (oldptr, new_size);
86 if (MPFR_UNLIKELY(ret == NULL))
87 {
88 fprintf (stderr, "[MPFR] mpfr_default_reallocate(): "
89 "can't reallocate memory (old_size=%" MPFR_INTMAX_FSPEC
90 "u new_size=%" MPFR_INTMAX_FSPEC "u)\n",
91 (mpfr_uintmax_t) old_size, (mpfr_uintmax_t) new_size);
92 fflush (NULL);
93 abort ();
94 }
95 return ret;
96 }
97
98 static void
mpfr_default_free(void * blk_ptr,size_t blk_size)99 mpfr_default_free (void *blk_ptr, size_t blk_size)
100 {
101 free (blk_ptr);
102 }
103
104 /* Return a pointer to a pointer to the found block (so it can be updated
105 when unlinking). */
106 /* FIXME: This is a O(n) search, while it could be done in nearly
107 constant time with a better data structure! */
108 static struct header **
tests_memory_find(void * ptr)109 tests_memory_find (void *ptr)
110 {
111 struct header **hp;
112
113 for (hp = &tests_memory_list; *hp != NULL; hp = &((*hp)->next))
114 if ((*hp)->ptr == ptr)
115 return hp;
116
117 return NULL;
118 }
119
120 /*
121 static int
122 tests_memory_valid (void *ptr)
123 {
124 return (tests_memory_find (ptr) != NULL);
125 }
126 */
127
128 static void
tests_addsize(size_t size)129 tests_addsize (size_t size)
130 {
131 tests_total_size += size;
132 if (tests_total_size > tests_memory_limit)
133 {
134 /* The total size taken by MPFR on the heap is more than 4 MB:
135 either a bug or a huge inefficiency. */
136 fprintf (stderr, "[MPFR] tests_addsize(): "
137 "too much memory (%" MPFR_INTMAX_FSPEC "u bytes)\n",
138 (mpfr_uintmax_t) tests_total_size);
139 fflush (NULL);
140 abort ();
141 }
142 }
143
144 void *
tests_allocate(size_t size)145 tests_allocate (size_t size)
146 {
147 struct header *h;
148
149 MPFR_LOCK_WRITE(mpfr_lock_memory);
150
151 if (size == 0)
152 {
153 fprintf (stderr, "[MPFR] tests_allocate(): "
154 "attempt to allocate 0 bytes\n");
155 fflush (NULL);
156 abort ();
157 }
158
159 tests_addsize (size);
160
161 h = (struct header *) mpfr_default_allocate (sizeof (*h));
162 h->next = tests_memory_list;
163 tests_memory_list = h;
164
165 h->size = size;
166 h->ptr = mpfr_default_allocate (size);
167
168 MPFR_UNLOCK_WRITE(mpfr_lock_memory);
169
170 return h->ptr;
171 }
172
173 /* Note: the double cast (mpfr_uintmax_t) (uintptr_t) below allows to avoid a
174 pointer-to-int-cast warning with GCC. The AC_TYPE_UINTPTR_T Autoconf macro
175 must be used to define uintptr_t if not available.
176 Note that pointers may be larger than uintmax_t, even in practice[*];
177 however, since this is just used in error messages, the loss of
178 information may be acceptable (but we should probably use %p).
179 [*] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2889.htm
180 */
181 void *
tests_reallocate(void * ptr,size_t old_size,size_t new_size)182 tests_reallocate (void *ptr, size_t old_size, size_t new_size)
183 {
184 struct header **hp, *h;
185
186 MPFR_LOCK_WRITE(mpfr_lock_memory);
187
188 if (new_size == 0)
189 {
190 fprintf (stderr, "[MPFR] tests_reallocate(): "
191 "attempt to reallocate 0x%" MPFR_INTMAX_FSPEC "X to 0 bytes\n",
192 (mpfr_uintmax_t) (uintptr_t) ptr);
193 fflush (NULL);
194 abort ();
195 }
196
197 hp = tests_memory_find (ptr);
198 if (hp == NULL)
199 {
200 fprintf (stderr, "[MPFR] tests_reallocate(): "
201 "attempt to reallocate bad pointer 0x%" MPFR_INTMAX_FSPEC "X\n",
202 (mpfr_uintmax_t) (uintptr_t) ptr);
203 fflush (NULL);
204 abort ();
205 }
206 h = *hp;
207
208 if (h->size != old_size)
209 {
210 /* Note: we should use the standard %zu to print sizes, but
211 this is not supported by old C implementations. */
212 fprintf (stderr, "[MPFR] tests_reallocate(): "
213 "bad old size %" MPFR_INTMAX_FSPEC
214 "u, should be %" MPFR_INTMAX_FSPEC "u\n",
215 (mpfr_uintmax_t) old_size, (mpfr_uintmax_t) h->size);
216 fflush (NULL);
217 abort ();
218 }
219
220 tests_total_size -= old_size;
221 tests_addsize (new_size);
222
223 h->size = new_size;
224 h->ptr = mpfr_default_reallocate (ptr, old_size, new_size);
225
226 MPFR_UNLOCK_WRITE(mpfr_lock_memory);
227
228 return h->ptr;
229 }
230
231 static struct header **
tests_free_find(void * ptr)232 tests_free_find (void *ptr)
233 {
234 struct header **hp = tests_memory_find (ptr);
235 if (hp == NULL)
236 {
237 fprintf (stderr, "[MPFR] tests_free(): "
238 "attempt to free bad pointer 0x%" MPFR_INTMAX_FSPEC "X\n",
239 (mpfr_uintmax_t) (uintptr_t) ptr);
240 fflush (NULL);
241 abort ();
242 }
243 return hp;
244 }
245
246 static void
tests_free_nosize(void * ptr)247 tests_free_nosize (void *ptr)
248 {
249 struct header **hp = tests_free_find (ptr);
250 struct header *h = *hp;
251
252 *hp = h->next; /* unlink */
253
254 mpfr_default_free (ptr, h->size);
255 mpfr_default_free (h, sizeof (*h));
256 }
257
258 void
tests_free(void * ptr,size_t size)259 tests_free (void *ptr, size_t size)
260 {
261 struct header **hp;
262 struct header *h;
263
264 MPFR_LOCK_WRITE(mpfr_lock_memory);
265
266 hp = tests_free_find (ptr);
267 h = *hp;
268
269 if (h->size != size)
270 {
271 /* Note: we should use the standard %zu to print sizes, but
272 this is not supported by old C implementations. */
273 fprintf (stderr, "[MPFR] tests_free(): bad size %"
274 MPFR_INTMAX_FSPEC "u, should be %" MPFR_INTMAX_FSPEC "u\n",
275 (mpfr_uintmax_t) size, (mpfr_uintmax_t) h->size);
276 fflush (NULL);
277 abort ();
278 }
279
280 tests_total_size -= size;
281 tests_free_nosize (ptr);
282
283 MPFR_UNLOCK_WRITE(mpfr_lock_memory);
284 }
285
286 void
tests_memory_start(void)287 tests_memory_start (void)
288 {
289 char *p;
290
291 tests_memory_list = NULL;
292 mp_set_memory_functions (tests_allocate, tests_reallocate, tests_free);
293
294 p = getenv ("MPFR_TESTS_MEMORY_LIMIT");
295 if (p != NULL)
296 {
297 tests_memory_limit = strtoul (p, NULL, 0);
298 if (tests_memory_limit == 0)
299 tests_memory_limit = (size_t) -1; /* no memory limit */
300 }
301 }
302
303 void
tests_memory_end(void)304 tests_memory_end (void)
305 {
306 if (tests_memory_list != NULL)
307 {
308 struct header *h;
309 unsigned count;
310
311 fprintf (stderr, "[MPFR] tests_memory_end(): not all memory freed\n");
312
313 count = 0;
314 for (h = tests_memory_list; h != NULL; h = h->next)
315 count++;
316
317 fprintf (stderr, "[MPFR] %u blocks remaining\n", count);
318 fflush (NULL);
319 abort ();
320 }
321 }
322