(root)/
glibc-2.38/
malloc/
tst-malloc-tcache-leak.c
       1  /* Bug 22111: Test that threads do not leak their per thread cache.
       2     Copyright (C) 2015-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* The point of this test is to start and exit a large number of
      20     threads, while at the same time looking to see if the used
      21     memory grows with each round of threads run.  If the memory
      22     grows above some linear bound we declare the test failed and
      23     that the malloc implementation is leaking memory with each
      24     thread.  This is a good indicator that the thread local cache
      25     is leaking chunks.  */
      26  
      27  #include <stdio.h>
      28  #include <stdlib.h>
      29  #include <malloc.h>
      30  #include <pthread.h>
      31  #include <assert.h>
      32  #include <libc-diag.h>
      33  
      34  #include <support/check.h>
      35  #include <support/support.h>
      36  #include <support/xthread.h>
      37  
      38  void *
      39  worker (void *data)
      40  {
      41    void *ret;
      42    /* Allocate an arbitrary amount of memory that is known to fit into
      43       the thread local cache (tcache).  If we have at least 64 bins
      44       (default e.g. TCACHE_MAX_BINS) we should be able to allocate 32
      45       bytes and force malloc to fill the tcache.  We are assuming tcache
      46       init happens at the first small alloc, but it might in the future
      47       be deferred to some other point.  Therefore to future proof this
      48       test we include a full alloc/free/alloc cycle for the thread.  We
      49       need a compiler barrier to avoid the removal of the useless
      50       alloc/free.  We send some memory back to main to have the memory
      51       freed after the thread dies, as just another check that the chunks
      52       that were previously in the tcache are still OK to free after
      53       thread death.  */
      54    ret = xmalloc (32);
      55    __asm__ volatile ("" ::: "memory");
      56    free (ret);
      57    return (void *) xmalloc (32);
      58  }
      59  
      60  static int
      61  do_test (void)
      62  {
      63    pthread_t *thread;
      64    struct mallinfo info_before, info_after;
      65    void *retval;
      66  
      67    /* This is an arbitrary choice. We choose a total of THREADS
      68       threads created and joined.  This gives us enough iterations to
      69       show a leak.  */
      70    int threads = 100000;
      71  
      72    /* Avoid there being 0 malloc'd data at this point by allocating the
      73       pthread_t required to run the test.  */
      74    thread = (pthread_t *) xcalloc (1, sizeof (pthread_t));
      75  
      76    /* The test below covers the deprecated mallinfo function.  */
      77    DIAG_PUSH_NEEDS_COMMENT;
      78    DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
      79  
      80    info_before = mallinfo ();
      81  
      82    assert (info_before.uordblks != 0);
      83  
      84    printf ("INFO: %d (bytes) are in use before starting threads.\n",
      85            info_before.uordblks);
      86  
      87    for (int loop = 0; loop < threads; loop++)
      88      {
      89        *thread = xpthread_create (NULL, worker, NULL);
      90        retval = xpthread_join (*thread);
      91        free (retval);
      92      }
      93  
      94    info_after = mallinfo ();
      95    printf ("INFO: %d (bytes) are in use after all threads joined.\n",
      96            info_after.uordblks);
      97  
      98    /* We need to compare the memory in use before and the memory in use
      99       after starting and joining THREADS threads.  We almost always grow
     100       memory slightly, but not much. Consider that if even 1-byte leaked
     101       per thread we'd have THREADS bytes of additional memory, and in
     102       general the in-use at the start of main is quite low.  We will
     103       always leak a full malloc chunk, and never just 1-byte, therefore
     104       anything above "+ threads" from the start (constant offset) is a
     105       leak.  Obviously this assumes no thread-related malloc'd internal
     106       libc data structures persist beyond the thread death, and any that
     107       did would limit the number of times you could call pthread_create,
     108       which is a QoI we'd want to detect and fix.  */
     109    if (info_after.uordblks > (info_before.uordblks + threads))
     110      FAIL_EXIT1 ("Memory usage after threads is too high.\n");
     111  
     112    DIAG_POP_NEEDS_COMMENT;
     113  
     114    /* Did not detect excessive memory usage.  */
     115    free (thread);
     116    exit (0);
     117  }
     118  
     119  #define TIMEOUT 50
     120  #include <support/test-driver.c>