Blame malloc/tst-malloc-tcache-leak.c

Packit 6c4009
/* Bug 22111: Test that threads do not leak their per thread cache.
Packit 6c4009
   Copyright (C) 2015-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* The point of this test is to start and exit a large number of
Packit 6c4009
   threads, while at the same time looking to see if the used
Packit 6c4009
   memory grows with each round of threads run.  If the memory
Packit 6c4009
   grows above some linear bound we declare the test failed and
Packit 6c4009
   that the malloc implementation is leaking memory with each
Packit 6c4009
   thread.  This is a good indicator that the thread local cache
Packit 6c4009
   is leaking chunks.  */
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <malloc.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/support.h>
Packit 6c4009
#include <support/xthread.h>
Packit 6c4009
Packit 6c4009
void *
Packit 6c4009
worker (void *data)
Packit 6c4009
{
Packit 6c4009
  void *ret;
Packit 6c4009
  /* Allocate an arbitrary amount of memory that is known to fit into
Packit 6c4009
     the thread local cache (tcache).  If we have at least 64 bins
Packit 6c4009
     (default e.g. TCACHE_MAX_BINS) we should be able to allocate 32
Packit 6c4009
     bytes and force malloc to fill the tcache.  We are assuming tcahce
Packit 6c4009
     init happens at the first small alloc, but it might in the future
Packit 6c4009
     be deferred to some other point.  Therefore to future proof this
Packit 6c4009
     test we include a full alloc/free/alloc cycle for the thread.  We
Packit 6c4009
     need a compiler barrier to avoid the removal of the useless
Packit 6c4009
     alloc/free.  We send some memory back to main to have the memory
Packit 6c4009
     freed after the thread dies, as just another check that the chunks
Packit 6c4009
     that were previously in the tcache are still OK to free after
Packit 6c4009
     thread death.  */
Packit 6c4009
  ret = xmalloc (32);
Packit 6c4009
  __asm__ volatile ("" ::: "memory");
Packit 6c4009
  free (ret);
Packit 6c4009
  return (void *) xmalloc (32);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  pthread_t *thread;
Packit 6c4009
  struct mallinfo info_before, info_after;
Packit 6c4009
  void *retval;
Packit 6c4009
Packit 6c4009
  /* This is an arbitrary choice. We choose a total of THREADS
Packit 6c4009
     threads created and joined.  This gives us enough iterations to
Packit 6c4009
     show a leak.  */
Packit 6c4009
  int threads = 100000;
Packit 6c4009
Packit 6c4009
  /* Avoid there being 0 malloc'd data at this point by allocating the
Packit 6c4009
     pthread_t required to run the test.  */
Packit 6c4009
  thread = (pthread_t *) xcalloc (1, sizeof (pthread_t));
Packit 6c4009
Packit 6c4009
  info_before = mallinfo ();
Packit 6c4009
Packit 6c4009
  assert (info_before.uordblks != 0);
Packit 6c4009
Packit 6c4009
  printf ("INFO: %d (bytes) are in use before starting threads.\n",
Packit 6c4009
          info_before.uordblks);
Packit 6c4009
Packit 6c4009
  for (int loop = 0; loop < threads; loop++)
Packit 6c4009
    {
Packit 6c4009
      *thread = xpthread_create (NULL, worker, NULL);
Packit 6c4009
      retval = xpthread_join (*thread);
Packit 6c4009
      free (retval);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  info_after = mallinfo ();
Packit 6c4009
  printf ("INFO: %d (bytes) are in use after all threads joined.\n",
Packit 6c4009
          info_after.uordblks);
Packit 6c4009
Packit 6c4009
  /* We need to compare the memory in use before and the memory in use
Packit 6c4009
     after starting and joining THREADS threads.  We almost always grow
Packit 6c4009
     memory slightly, but not much. Consider that if even 1-byte leaked
Packit 6c4009
     per thread we'd have THREADS bytes of additional memory, and in
Packit 6c4009
     general the in-use at the start of main is quite low.  We will
Packit 6c4009
     always leak a full malloc chunk, and never just 1-byte, therefore
Packit 6c4009
     anything above "+ threads" from the start (constant offset) is a
Packit 6c4009
     leak.  Obviously this assumes no thread-related malloc'd internal
Packit 6c4009
     libc data structures persist beyond the thread death, and any that
Packit 6c4009
     did would limit the number of times you could call pthread_create,
Packit 6c4009
     which is a QoI we'd want to detect and fix.  */
Packit 6c4009
  if (info_after.uordblks > (info_before.uordblks + threads))
Packit 6c4009
    FAIL_EXIT1 ("Memory usage after threads is too high.\n");
Packit 6c4009
Packit 6c4009
  /* Did not detect excessive memory usage.  */
Packit 6c4009
  free (thread);
Packit 6c4009
  exit (0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define TIMEOUT 50
Packit 6c4009
#include <support/test-driver.c>