Patchwork [RFC,4/4] malloc_info: add tst-malloc_info-vs-mallinfo

login
register
mail settings
Submitter Ken Milmore
Date April 30, 2018, 9 p.m.
Message ID <5a6f9025-a596-326d-ba50-cdda1256b446@gmail.com>
Download mbox | patch
Permalink /patch/27066/
State New
Headers show

Comments

Ken Milmore - April 30, 2018, 9 p.m.
---
 malloc/Makefile                      |   2 +
 malloc/tst-malloc_info-vs-mallinfo.c | 258 +++++++++++++++++++++++++++++++++++
 2 files changed, 260 insertions(+)
 create mode 100644 malloc/tst-malloc_info-vs-mallinfo.c

Patch

diff --git a/malloc/Makefile b/malloc/Makefile
index 7d54bad866..abcb7f847d 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -36,6 +36,7 @@  tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-alloc_buffer \
 	 tst-malloc-tcache-leak \
 	 tst-malloc_info \
+	 tst-malloc_info-vs-mallinfo \
 	 tst-malloc-too-large \
 	 tst-malloc-stats-cancellation \
 
@@ -251,3 +252,4 @@  $(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
 
 $(objpfx)tst-malloc-tcache-leak: $(shared-thread-library)
 $(objpfx)tst-malloc_info: $(shared-thread-library)
+$(objpfx)tst-malloc_info-vs-mallinfo: $(shared-thread-library)
diff --git a/malloc/tst-malloc_info-vs-mallinfo.c b/malloc/tst-malloc_info-vs-mallinfo.c
new file mode 100644
index 0000000000..9215cf2a97
--- /dev/null
+++ b/malloc/tst-malloc_info-vs-mallinfo.c
@@ -0,0 +1,258 @@ 
+/* Test for consistency of malloc_info with mallinfo.
+   Copyright (C) 2017-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This test checks for agreement between the amounts of free and
+   allocated space reported by the malloc_info and mallinfo functions.
+   It has been adapted from tst-malloc_info.c. */
+
+#include <array_length.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/support.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+
+/* This barrier is used to have the main thread wait until the helper
+   threads have performed their allocations.  */
+static pthread_barrier_t barrier;
+
+enum
+  {
+    /* Number of threads performing allocations.  */
+    thread_count  = 4,
+
+    /* Amount of small block memory allocation per thread.
+       We keep this modest so there can be no possiblity of the mallinfo
+       totals wrapping around. */
+    per_thread_allocations = 16 * 1024 * 1024,
+
+    /* Size of the largest small block to allocate.
+       We will allocate a range of sizes up to this maximum. */
+    max_alloc_size = 512,
+
+    /* Size of large block to allocate.
+       We allocate only one of these per thread. It should be large enough
+       to ensure use of mmap. */
+    large_block_size = 1024 * 1024,
+  };
+
+static void *
+allocation_thread_function (void *closure)
+{
+  struct list
+  {
+    struct list *next;
+  };
+
+  struct list *head = NULL;
+  size_t allocated = 0;
+  unsigned int seed = 1;
+
+  xpthread_barrier_wait (&barrier);
+  /* Before allocation */
+  xpthread_barrier_wait (&barrier);
+
+  /* Allocate a single large block and then a range of small block sizes: */
+  void *large_block = xmalloc (large_block_size);
+
+  while (allocated < per_thread_allocations)
+    {
+      size_t size = rand_r (&seed) % max_alloc_size;
+      struct list *new_head = xmalloc (size);
+      allocated += size;
+      new_head->next = head;
+      head = new_head;
+    }
+
+  xpthread_barrier_wait (&barrier);
+  /* After allocation */
+  xpthread_barrier_wait (&barrier);
+
+  /* Force some memory fragmentation by freeing every other block. */
+  struct list *pos = head;
+  while (pos != NULL && pos->next != NULL)
+    {
+      struct list *next_pos = pos->next->next;
+      free (pos->next);
+      pos->next = next_pos;
+      pos = next_pos;
+    }
+
+  xpthread_barrier_wait (&barrier);
+  /* After partial deallocation */
+  xpthread_barrier_wait (&barrier);
+
+  while (head != NULL)
+    {
+      struct list *next_head = head->next;
+      free (head);
+      head = next_head;
+    }
+
+  free (large_block);
+
+  return NULL;
+}
+
+static void
+check_malloc_info (const char *context)
+{
+  printf ("\ninfo: %s:\n", context);
+
+  FILE *f = tmpfile ();
+  if (f == NULL)
+    FAIL_EXIT1 ("tmpfile failed: %m");
+
+  /* Perform a dummy seek, to allocate the stream buffer. */
+  if(fseek(f, 0L, SEEK_SET) != 0)
+    FAIL_EXIT1 ("fseek failed: %m");
+
+  /* We call mallinfo twice, i.e. before and after malloc_info, so we can
+     check that nothing has changed in the meantime which could lead to
+     false comparisons. */
+  struct mallinfo mib4 = mallinfo ();
+  malloc_info (0, f);
+  struct mallinfo mi = mallinfo ();
+
+  TEST_VERIFY_EXIT (memcmp (&mi, &mib4, sizeof (mi)) == 0);
+
+  if (fseek (f, 0L, SEEK_SET) != 0)
+    FAIL_EXIT1 ("fseek failed: %m");
+
+  size_t total_fast_count = 0;
+  size_t total_fast_size = 0;
+  size_t total_rest_count = 0;
+  size_t total_rest_size = 0;
+  size_t total_top_count = 0;
+  size_t total_top_size = 0;
+  size_t total_mmap_count = 0;
+  size_t total_mmap_size = 0;
+  size_t system_current_size = 0;
+  size_t heap0_top_size = 0;
+  int heap_nr = -1;
+
+  for (;;)
+    {
+      char tag[100];
+      if (fscanf (f, " <%99[^>]> ", tag) != 1)
+        FAIL_EXIT1 ("fscanf failed (or EOF)");
+
+      if (test_verbose > 0)
+        printf ("<%s>\n", tag);
+
+      if (strcmp (tag, "/malloc") == 0)
+        {
+          TEST_VERIFY_EXIT (heap_nr == -1);
+          break;
+        }
+      else if (strcmp (tag, "/heap") == 0)
+        {
+          TEST_VERIFY_EXIT (heap_nr >= 0);
+          heap_nr = -1;
+        }
+      else if (heap_nr == 0)
+        {
+          sscanf (tag, "total type=\"top\" count=\"1\" size=\"%zu\"",
+                  &heap0_top_size);
+        }
+      else if (heap_nr == -1)
+        {
+          sscanf (tag, "heap nr=\"%d\"", &heap_nr);
+          sscanf (tag, "total type=\"fast\" count=\"%zu\" size=\"%zu\"",
+                  &total_fast_count, &total_fast_size);
+          sscanf (tag, "total type=\"rest\" count=\"%zu\" size=\"%zu\"",
+                  &total_rest_count, &total_rest_size);
+          sscanf (tag, "total type=\"top\" count=\"%zu\" size=\"%zu\"",
+                  &total_top_count, &total_top_size);
+          sscanf (tag, "total type=\"mmap\" count=\"%zu\" size=\"%zu\"",
+                  &total_mmap_count, &total_mmap_size);
+          sscanf (tag, "system type=\"current\" size=\"%zu\"",
+                  &system_current_size);
+        }
+    }
+
+  fclose (f);
+
+  size_t total_nonfast_count = total_rest_count + total_top_count;
+  size_t total_free_size = total_fast_size + total_rest_size + total_top_size;
+  size_t total_alloc_size = system_current_size - total_free_size;
+
+  printf ("arena=%d/%zu\n", mi.arena, system_current_size);
+  TEST_COMPARE (mi.arena, system_current_size);
+
+  printf ("ordblks=%d/%zu\n", mi.ordblks, total_nonfast_count);
+  TEST_COMPARE (mi.ordblks, total_nonfast_count);
+
+  printf ("smblks=%d/%zu\n", mi.smblks, total_fast_count);
+  TEST_COMPARE (mi.smblks, total_fast_count);
+
+  printf ("hblks=%d/%zu\n", mi.hblks, total_mmap_count);
+  TEST_COMPARE (mi.hblks, total_mmap_count);
+
+  printf ("hblkhd=%d/%zu\n", mi.hblkhd, total_mmap_size);
+  TEST_COMPARE (mi.hblkhd, total_mmap_size);
+
+  printf ("fsmblks=%d/%zu\n", mi.fsmblks, total_fast_size);
+  TEST_COMPARE (mi.fsmblks, total_fast_size);
+
+  printf ("uordblks=%d/%zu\n", mi.uordblks, total_alloc_size);
+  TEST_COMPARE (mi.uordblks, total_alloc_size);
+
+  printf ("fordblks=%d/%zu\n", mi.fordblks, total_free_size);
+  TEST_COMPARE (mi.fordblks, total_free_size);
+
+  printf ("keepcost=%d/%zu\n", mi.keepcost, heap0_top_size);
+  TEST_COMPARE (mi.keepcost, heap0_top_size);
+}
+
+static int
+do_test (void)
+{
+  xpthread_barrier_init (&barrier, NULL, thread_count + 1);
+
+  pthread_t threads[thread_count];
+  for (size_t i = 0; i < array_length (threads); ++i)
+    threads[i] = xpthread_create (NULL, allocation_thread_function, NULL);
+
+  xpthread_barrier_wait (&barrier);
+  check_malloc_info ("Before allocation");
+  xpthread_barrier_wait (&barrier);
+
+  xpthread_barrier_wait (&barrier);
+  check_malloc_info ("After allocation");
+  xpthread_barrier_wait (&barrier);
+
+  xpthread_barrier_wait (&barrier);
+  check_malloc_info ("After partial deallocation");
+  xpthread_barrier_wait (&barrier);
+
+  for (size_t i = 0; i < array_length (threads); ++i)
+    xpthread_join (threads[i]);
+
+  check_malloc_info ("After full deallocation");
+
+  malloc_trim (0);
+
+  check_malloc_info ("After malloc_trim");
+
+  return 0;
+}
+
+#include <support/test-driver.c>