tst-malloc-tcache-leak.c 4.2 KB

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