xref: /netbsd-src/external/lgpl3/mpfr/dist/tests/memory.c (revision ba125506a622fe649968631a56eba5d42ff57863)
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