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; 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 * 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 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 ** 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 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 * 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 * 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 ** 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 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 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 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 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