Home | History | Annotate | Line # | Download | only in tests
      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