Credit Ulrich Drepper for libpthread in contrib.texi
[glibc.git] / malloc / tst-mallocstate.c
blob5cb39c0f3d5b13ae9cef62cd26010b60e4e18c65
1 /* Emulate Emacs heap dumping to test malloc_set_state.
2 Copyright (C) 2001-2017 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
20 #include <errno.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <libc-symbols.h>
25 #include <shlib-compat.h>
27 #include "malloc.h"
29 /* Make the compatibility symbols availabile to this test case. */
30 void *malloc_get_state (void);
31 compat_symbol_reference (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
32 int malloc_set_state (void *);
33 compat_symbol_reference (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
35 static int do_test (void);
36 #define TEST_FUNCTION do_test ()
37 #include "../test-skeleton.c"
39 /* Maximum object size in the fake heap. */
40 enum { max_size = 64 };
42 /* Allocation actions. These are randomized actions executed on the
43 dumped heap (see allocation_tasks below). They are interspersed
44 with operations on the new heap (see heap_activity). */
45 enum allocation_action
47 action_free, /* Dumped and freed. */
48 action_realloc, /* Dumped and realloc'ed. */
49 action_realloc_same, /* Dumped and realloc'ed, same size. */
50 action_realloc_smaller, /* Dumped and realloc'ed, shrinked. */
51 action_count
54 /* Dumped heap. Initialize it, so that the object is placed into the
55 .data section, for increased realism. The size is an upper bound;
56 we use about half of the space. */
57 static size_t dumped_heap[action_count * max_size * max_size
58 / sizeof (size_t)] = {1};
60 /* Next free space in the dumped heap. Also top of the heap at the
61 end of the initialization procedure. */
62 static size_t *next_heap_chunk;
64 /* Copied from malloc.c and hooks.c. The version is deliberately
65 lower than the final version of malloc_set_state. */
66 #define NBINS 128
67 #define MALLOC_STATE_MAGIC 0x444c4541l
68 #define MALLOC_STATE_VERSION (0 * 0x100l + 4l)
69 static struct
71 long magic;
72 long version;
73 void *av[NBINS * 2 + 2];
74 char *sbrk_base;
75 int sbrked_mem_bytes;
76 unsigned long trim_threshold;
77 unsigned long top_pad;
78 unsigned int n_mmaps_max;
79 unsigned long mmap_threshold;
80 int check_action;
81 unsigned long max_sbrked_mem;
82 unsigned long max_total_mem;
83 unsigned int n_mmaps;
84 unsigned int max_n_mmaps;
85 unsigned long mmapped_mem;
86 unsigned long max_mmapped_mem;
87 int using_malloc_checking;
88 unsigned long max_fast;
89 unsigned long arena_test;
90 unsigned long arena_max;
91 unsigned long narenas;
92 } save_state =
94 .magic = MALLOC_STATE_MAGIC,
95 .version = MALLOC_STATE_VERSION,
98 /* Allocate a blob in the fake heap. */
99 static void *
100 dumped_heap_alloc (size_t length)
102 /* malloc needs three state bits in the size field, so the minimum
103 alignment is 8 even on 32-bit architectures. malloc_set_state
104 should be compatible with such heaps even if it currently
105 provides more alignment to applications. */
106 enum
108 heap_alignment = 8,
109 heap_alignment_mask = heap_alignment - 1
111 _Static_assert (sizeof (size_t) <= heap_alignment,
112 "size_t compatible with heap alignment");
114 /* Need at least this many bytes for metadata and application
115 data. */
116 size_t chunk_size = sizeof (size_t) + length;
117 /* Round up the allocation size to the heap alignment. */
118 chunk_size += heap_alignment_mask;
119 chunk_size &= ~heap_alignment_mask;
120 if ((chunk_size & 3) != 0)
122 /* The lower three bits in the chunk size have to be 0. */
123 write_message ("error: dumped_heap_alloc computed invalid chunk size\n");
124 _exit (1);
126 if (next_heap_chunk == NULL)
127 /* Initialize the top of the heap. Add one word of zero padding,
128 to match existing practice. */
130 dumped_heap[0] = 0;
131 next_heap_chunk = dumped_heap + 1;
133 else
134 /* The previous chunk is allocated. */
135 chunk_size |= 1;
136 *next_heap_chunk = chunk_size;
138 /* User data starts after the chunk header. */
139 void *result = next_heap_chunk + 1;
140 next_heap_chunk += chunk_size / sizeof (size_t);
142 /* Mark the previous chunk as used. */
143 *next_heap_chunk = 1;
144 return result;
147 /* Global seed variable for the random number generator. */
148 static unsigned long long global_seed;
150 /* Simple random number generator. The numbers are in the range from
151 0 to UINT_MAX (inclusive). */
152 static unsigned int
153 rand_next (unsigned long long *seed)
155 /* Linear congruential generated as used for MMIX. */
156 *seed = *seed * 6364136223846793005ULL + 1442695040888963407ULL;
157 return *seed >> 32;
160 /* Fill LENGTH bytes at BUFFER with random contents, as determined by
161 SEED. */
162 static void
163 randomize_buffer (unsigned char *buffer, size_t length,
164 unsigned long long seed)
166 for (size_t i = 0; i < length; ++i)
167 buffer[i] = rand_next (&seed);
170 /* Dumps the buffer to standard output, in hexadecimal. */
171 static void
172 dump_hex (unsigned char *buffer, size_t length)
174 for (int i = 0; i < length; ++i)
175 printf (" %02X", buffer[i]);
178 /* Set to true if an error is encountered. */
179 static bool errors = false;
181 /* Keep track of object allocations. */
182 struct allocation
184 unsigned char *data;
185 unsigned int size;
186 unsigned int seed;
189 /* Check that the allocation task allocation has the expected
190 contents. */
191 static void
192 check_allocation (const struct allocation *alloc, int index)
194 size_t size = alloc->size;
195 if (alloc->data == NULL)
197 printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n",
198 size, index, alloc->seed);
199 errors = true;
200 return;
203 unsigned char expected[4096];
204 if (size > sizeof (expected))
206 printf ("error: invalid allocation size %zu at %d, seed %u\n",
207 size, index, alloc->seed);
208 errors = true;
209 return;
211 randomize_buffer (expected, size, alloc->seed);
212 if (memcmp (alloc->data, expected, size) != 0)
214 printf ("error: allocation %d data mismatch, size %zu, seed %u\n",
215 index, size, alloc->seed);
216 printf (" expected:");
217 dump_hex (expected, size);
218 putc ('\n', stdout);
219 printf (" actual:");
220 dump_hex (alloc->data, size);
221 putc ('\n', stdout);
222 errors = true;
226 /* A heap allocation combined with pending actions on it. */
227 struct allocation_task
229 struct allocation allocation;
230 enum allocation_action action;
233 /* Allocation tasks. Initialized by init_allocation_tasks and used by
234 perform_allocations. */
235 enum { allocation_task_count = action_count * max_size };
236 static struct allocation_task allocation_tasks[allocation_task_count];
238 /* Fisher-Yates shuffle of allocation_tasks. */
239 static void
240 shuffle_allocation_tasks (void)
242 for (int i = 0; i < allocation_task_count - 1; ++i)
244 /* Pick pair in the tail of the array. */
245 int j = i + (rand_next (&global_seed)
246 % ((unsigned) (allocation_task_count - i)));
247 if (j < 0 || j >= allocation_task_count)
249 write_message ("error: test bug in shuffle\n");
250 _exit (1);
252 /* Exchange. */
253 struct allocation_task tmp = allocation_tasks[i];
254 allocation_tasks[i] = allocation_tasks[j];
255 allocation_tasks[j] = tmp;
259 /* Set up the allocation tasks and the dumped heap. */
260 static void
261 initial_allocations (void)
263 /* Initialize in a position-dependent way. */
264 for (int i = 0; i < allocation_task_count; ++i)
265 allocation_tasks[i] = (struct allocation_task)
267 .allocation =
269 .size = 1 + (i / action_count),
270 .seed = i,
272 .action = i % action_count
275 /* Execute the tasks in a random order. */
276 shuffle_allocation_tasks ();
278 /* Initialize the contents of the dumped heap. */
279 for (int i = 0; i < allocation_task_count; ++i)
281 struct allocation_task *task = allocation_tasks + i;
282 task->allocation.data = dumped_heap_alloc (task->allocation.size);
283 randomize_buffer (task->allocation.data, task->allocation.size,
284 task->allocation.seed);
287 for (int i = 0; i < allocation_task_count; ++i)
288 check_allocation (&allocation_tasks[i].allocation, i);
291 /* Indicates whether init_heap has run. This variable needs to be
292 volatile because malloc is declared __THROW, which implies it is a
293 leaf function, but we expect it to run our hooks. */
294 static volatile bool heap_initialized;
296 /* Executed by glibc malloc, through __malloc_initialize_hook
297 below. */
298 static void
299 init_heap (void)
301 write_message ("info: performing heap initialization\n");
302 heap_initialized = true;
304 /* Populate the dumped heap. */
305 initial_allocations ();
307 /* Complete initialization of the saved heap data structure. */
308 save_state.sbrk_base = (void *) dumped_heap;
309 save_state.sbrked_mem_bytes = sizeof (dumped_heap);
310 /* Top pointer. Adjust so that it points to the start of struct
311 malloc_chunk. */
312 save_state.av[2] = (void *) (next_heap_chunk - 1);
314 /* Integrate the dumped heap into the process heap. */
315 if (malloc_set_state (&save_state) != 0)
317 write_message ("error: malloc_set_state failed\n");
318 _exit (1);
322 /* Interpose the initialization callback. */
323 void (*volatile __malloc_initialize_hook) (void) = init_heap;
325 /* Simulate occasional unrelated heap activity in the non-dumped
326 heap. */
327 enum { heap_activity_allocations_count = 32 };
328 static struct allocation heap_activity_allocations
329 [heap_activity_allocations_count] = {};
330 static int heap_activity_seed_counter = 1000 * 1000;
332 static void
333 heap_activity (void)
335 /* Only do this from time to time. */
336 if ((rand_next (&global_seed) % 4) == 0)
338 int slot = rand_next (&global_seed) % heap_activity_allocations_count;
339 struct allocation *alloc = heap_activity_allocations + slot;
340 if (alloc->data == NULL)
342 alloc->size = rand_next (&global_seed) % (4096U + 1);
343 alloc->data = xmalloc (alloc->size);
344 alloc->seed = heap_activity_seed_counter++;
345 randomize_buffer (alloc->data, alloc->size, alloc->seed);
346 check_allocation (alloc, 1000 + slot);
348 else
350 check_allocation (alloc, 1000 + slot);
351 free (alloc->data);
352 alloc->data = NULL;
357 static void
358 heap_activity_deallocate (void)
360 for (int i = 0; i < heap_activity_allocations_count; ++i)
361 free (heap_activity_allocations[i].data);
364 /* Perform a full heap check across the dumped heap allocation tasks,
365 and the simulated heap activity directly above. */
366 static void
367 full_heap_check (void)
369 /* Dumped heap. */
370 for (int i = 0; i < allocation_task_count; ++i)
371 if (allocation_tasks[i].allocation.data != NULL)
372 check_allocation (&allocation_tasks[i].allocation, i);
374 /* Heap activity allocations. */
375 for (int i = 0; i < heap_activity_allocations_count; ++i)
376 if (heap_activity_allocations[i].data != NULL)
377 check_allocation (heap_activity_allocations + i, i);
380 /* Used as an optimization barrier to force a heap allocation. */
381 __attribute__ ((noinline, noclone))
382 static void
383 my_free (void *ptr)
385 free (ptr);
388 static int
389 do_test (void)
391 my_free (malloc (1));
392 if (!heap_initialized)
394 printf ("error: heap was not initialized by malloc\n");
395 return 1;
398 /* The first pass performs the randomly generated allocation
399 tasks. */
400 write_message ("info: first pass through allocation tasks\n");
401 full_heap_check ();
403 /* Execute the post-undump tasks in a random order. */
404 shuffle_allocation_tasks ();
406 for (int i = 0; i < allocation_task_count; ++i)
408 heap_activity ();
409 struct allocation_task *task = allocation_tasks + i;
410 switch (task->action)
412 case action_free:
413 check_allocation (&task->allocation, i);
414 free (task->allocation.data);
415 task->allocation.data = NULL;
416 break;
418 case action_realloc:
419 check_allocation (&task->allocation, i);
420 task->allocation.data = xrealloc
421 (task->allocation.data, task->allocation.size + max_size);
422 check_allocation (&task->allocation, i);
423 break;
425 case action_realloc_same:
426 check_allocation (&task->allocation, i);
427 task->allocation.data = xrealloc
428 (task->allocation.data, task->allocation.size);
429 check_allocation (&task->allocation, i);
430 break;
432 case action_realloc_smaller:
433 check_allocation (&task->allocation, i);
434 size_t new_size = task->allocation.size - 1;
435 task->allocation.data = xrealloc (task->allocation.data, new_size);
436 if (new_size == 0)
438 if (task->allocation.data != NULL)
440 printf ("error: realloc with size zero did not deallocate\n");
441 errors = true;
443 /* No further action on this task. */
444 task->action = action_free;
446 else
448 task->allocation.size = new_size;
449 check_allocation (&task->allocation, i);
451 break;
453 case action_count:
454 abort ();
456 full_heap_check ();
459 /* The second pass frees the objects which were allocated during the
460 first pass. */
461 write_message ("info: second pass through allocation tasks\n");
463 shuffle_allocation_tasks ();
464 for (int i = 0; i < allocation_task_count; ++i)
466 heap_activity ();
467 struct allocation_task *task = allocation_tasks + i;
468 switch (task->action)
470 case action_free:
471 /* Already freed, nothing to do. */
472 break;
474 case action_realloc:
475 case action_realloc_same:
476 case action_realloc_smaller:
477 check_allocation (&task->allocation, i);
478 free (task->allocation.data);
479 task->allocation.data = NULL;
480 break;
482 case action_count:
483 abort ();
485 full_heap_check ();
488 heap_activity_deallocate ();
490 /* Check that the malloc_get_state stub behaves in the intended
491 way. */
492 errno = 0;
493 if (malloc_get_state () != NULL)
495 printf ("error: malloc_get_state succeeded\n");
496 errors = true;
498 if (errno != ENOSYS)
500 printf ("error: malloc_get_state: %m\n");
501 errors = true;
504 return errors;