diff options
Diffstat (limited to 'REORG.TODO/malloc')
73 files changed, 17651 insertions, 0 deletions
diff --git a/REORG.TODO/malloc/Depend b/REORG.TODO/malloc/Depend new file mode 100644 index 0000000000..910c6d9152 --- /dev/null +++ b/REORG.TODO/malloc/Depend @@ -0,0 +1 @@ +dlfcn diff --git a/REORG.TODO/malloc/Makefile b/REORG.TODO/malloc/Makefile new file mode 100644 index 0000000000..af025cbb24 --- /dev/null +++ b/REORG.TODO/malloc/Makefile @@ -0,0 +1,233 @@ +# Copyright (C) 1991-2017 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/>. + +# +# Makefile for malloc routines +# +subdir := malloc + +include ../Makeconfig + +dist-headers := malloc.h +headers := $(dist-headers) obstack.h mcheck.h +tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ + tst-mcheck tst-mallocfork tst-trim1 \ + tst-malloc-usable tst-realloc tst-reallocarray tst-posix_memalign \ + tst-pvalloc tst-memalign tst-mallopt \ + tst-malloc-backtrace tst-malloc-thread-exit \ + tst-malloc-thread-fail tst-malloc-fork-deadlock \ + tst-mallocfork2 \ + tst-interpose-nothread \ + tst-interpose-thread \ + +tests-static := \ + tst-interpose-static-nothread \ + tst-interpose-static-thread \ + tst-malloc-usable-static \ + +tests-internal := tst-mallocstate tst-scratch_buffer + +# The dynarray framework is only available inside glibc. +tests-internal += \ + tst-dynarray \ + tst-dynarray-fail \ + tst-dynarray-at-fail \ + +ifneq (no,$(have-tunables)) +tests += tst-malloc-usable-tunables +tests-static += tst-malloc-usable-static-tunables +endif + +tests += $(tests-static) +test-srcs = tst-mtrace tst-dynarray tst-dynarray-fail + +routines = malloc morecore mcheck mtrace obstack reallocarray \ + scratch_buffer_grow scratch_buffer_grow_preserve \ + scratch_buffer_set_array_size \ + dynarray_at_failure \ + dynarray_emplace_enlarge \ + dynarray_finalize \ + dynarray_resize \ + dynarray_resize_clear \ + +install-lib := libmcheck.a +non-lib.a := libmcheck.a + +# Additional library. +extra-libs = libmemusage +extra-libs-others = $(extra-libs) + +# Helper objects for some tests. +extra-tests-objs += \ + tst-interpose-aux-nothread.o \ + tst-interpose-aux-thread.o \ + +test-extras = \ + tst-interpose-aux-nothread \ + tst-interpose-aux-thread \ + +libmemusage-routines = memusage +libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes)) + +$(objpfx)tst-malloc-backtrace: $(shared-thread-library) +$(objpfx)tst-malloc-thread-exit: $(shared-thread-library) +$(objpfx)tst-malloc-thread-fail: $(shared-thread-library) +$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library) + +# Export the __malloc_initialize_hook variable to libc.so. +LDFLAGS-tst-mallocstate = -rdynamic + +# These should be removed by `make clean'. +extra-objs = mcheck-init.o libmcheck.a + +# Include the cleanup handler. +aux := set-freeres thread-freeres + +# The Perl script to analyze the output of the mtrace functions. +ifneq ($(PERL),no) +install-bin-script = mtrace +generated += mtrace + +# The Perl script will print addresses and to do this nicely we must know +# whether we are on a 32 or 64 bit machine. +ifneq ($(findstring wordsize-32,$(config-sysdirs)),) +address-width=10 +else +address-width=18 +endif +endif + +# Unless we get a test for the availability of libgd which also works +# for cross-compiling we disable the memusagestat generation in this +# situation. +ifneq ($(cross-compiling),yes) +# If the gd library is available we build the `memusagestat' program. +ifneq ($(LIBGD),no) +others: $(objpfx)memusage +install-bin = memusagestat +install-bin-script += memusage +generated += memusagestat memusage +extra-objs += memusagestat.o + +# The configure.ac check for libgd and its headers did not use $SYSINCLUDES. +# The directory specified by --with-headers usually contains only the basic +# kernel interface headers, not something like libgd. So the simplest thing +# is to presume that the standard system headers will be ok for this file. +$(objpfx)memusagestat.o: sysincludes = # nothing +endif +endif + +# Another goal which can be used to override the configure decision. +.PHONY: do-memusagestat +do-memusagestat: $(objpfx)memusagestat + +memusagestat-modules = memusagestat + +cpp-srcs-left := $(memusagestat-modules) +lib := memusagestat +include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left)) + +$(objpfx)memusagestat: $(memusagestat-modules:%=$(objpfx)%.o) + $(LINK.o) -o $@ $^ $(libgd-LDFLAGS) -lgd -lpng -lz -lm + +ifeq ($(run-built-tests),yes) +ifeq (yes,$(build-shared)) +ifneq ($(PERL),no) +tests-special += $(objpfx)tst-mtrace.out +tests-special += $(objpfx)tst-dynarray-mem.out +tests-special += $(objpfx)tst-dynarray-fail-mem.out +endif +endif +endif + +include ../Rules + +CFLAGS-mcheck-init.c = $(PIC-ccflag) +CFLAGS-obstack.c = $(uses-callbacks) + +$(objpfx)libmcheck.a: $(objpfx)mcheck-init.o + -rm -f $@ + $(patsubst %/,cd % &&,$(objpfx)) \ + $(LN_S) $(<F) $(@F) + +lib: $(objpfx)libmcheck.a + +ifeq ($(run-built-tests),yes) +ifeq (yes,$(build-shared)) +ifneq ($(PERL),no) +$(objpfx)tst-mtrace.out: tst-mtrace.sh $(objpfx)tst-mtrace + $(SHELL) $< $(common-objpfx) '$(test-program-prefix-before-env)' \ + '$(run-program-env)' '$(test-program-prefix-after-env)' ; \ + $(evaluate-test) +endif +endif +endif + +tst-mcheck-ENV = MALLOC_CHECK_=3 +tst-malloc-usable-ENV = MALLOC_CHECK_=3 +tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV) +tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 +tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV) + +# Uncomment this for test releases. For public releases it is too expensive. +#CPPFLAGS-malloc.o += -DMALLOC_DEBUG=1 + +sLIBdir := $(shell echo $(slibdir) | sed 's,lib\(\|64\)$$,\\\\$$LIB,') + +$(objpfx)mtrace: mtrace.pl + rm -f $@.new + sed -e 's|@PERL@|$(PERL)|' -e 's|@XXX@|$(address-width)|' \ + -e 's|@VERSION@|$(version)|' \ + -e 's|@PKGVERSION@|$(PKGVERSION)|' \ + -e 's|@REPORT_BUGS_TO@|$(REPORT_BUGS_TO)|' $^ > $@.new \ + && rm -f $@ && mv $@.new $@ && chmod +x $@ + +$(objpfx)memusage: memusage.sh + rm -f $@.new + sed -e 's|@BASH@|$(BASH)|' -e 's|@VERSION@|$(version)|' \ + -e 's|@SLIBDIR@|$(sLIBdir)|' -e 's|@BINDIR@|$(bindir)|' \ + -e 's|@PKGVERSION@|$(PKGVERSION)|' \ + -e 's|@REPORT_BUGS_TO@|$(REPORT_BUGS_TO)|' $^ > $@.new \ + && rm -f $@ && mv $@.new $@ && chmod +x $@ + + +# The implementation uses `dlsym' +$(objpfx)libmemusage.so: $(libdl) + +# Extra dependencies +$(foreach o,$(all-object-suffixes),$(objpfx)malloc$(o)): arena.c hooks.c + +# Compile the tests with a flag which suppresses the mallopt call in +# the test skeleton. +$(tests:%=$(objpfx)%.o): CPPFLAGS += -DTEST_NO_MALLOPT + +$(objpfx)tst-interpose-nothread: $(objpfx)tst-interpose-aux-nothread.o +$(objpfx)tst-interpose-thread: \ + $(objpfx)tst-interpose-aux-thread.o $(shared-thread-library) +$(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o +$(objpfx)tst-interpose-static-thread: \ + $(objpfx)tst-interpose-aux-thread.o $(static-thread-library) + +tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace +$(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \ + $(evaluate-test) + +tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace +$(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \ + $(evaluate-test) diff --git a/REORG.TODO/malloc/Versions b/REORG.TODO/malloc/Versions new file mode 100644 index 0000000000..5b543069b3 --- /dev/null +++ b/REORG.TODO/malloc/Versions @@ -0,0 +1,90 @@ +libc { + GLIBC_2.0 { + # global variables + _obstack; + + # interface of malloc functions + __libc_calloc; __libc_free; __libc_mallinfo; __libc_malloc; + __libc_mallopt; __libc_memalign; __libc_pvalloc; __libc_realloc; + __libc_valloc; + __malloc_initialize_hook; __free_hook; __malloc_hook; __realloc_hook; + __memalign_hook; __after_morecore_hook; + __malloc_initialized; __default_morecore; __morecore; + + # functions used in inline functions or macros + _obstack_allocated_p; _obstack_begin; _obstack_begin_1; + _obstack_free; _obstack_memory_used; _obstack_newchunk; + + # variables in normal name space + mallwatch; obstack_alloc_failed_handler; obstack_exit_failure; + + # c* + calloc; cfree; + + # f* + free; + + # m* + mallinfo; malloc; malloc_get_state; malloc_set_state; malloc_stats; + malloc_trim; malloc_usable_size; mallopt; memalign; mprobe; mtrace; + muntrace; + + # o* + obstack_free; + + # p* + pvalloc; + + # r* + realloc; + + # t* + tr_break; + + # v* + valloc; + } + GLIBC_2.1 { + # Special functions. + __libc_freeres; + } + GLIBC_2.2 { + # m* + mcheck_check_all; mcheck_pedantic; + + # p* + posix_memalign; + } + GLIBC_2.10 { + malloc_info; + } + GLIBC_2.16 { + aligned_alloc; + } + GLIBC_2.26 { + reallocarray; + } + GLIBC_PRIVATE { + # Internal startup hook for libpthread. + __libc_malloc_pthread_startup; + + # Internal destructor hook for libpthread. + __libc_thread_freeres; + + # struct scratch_buffer support + __libc_scratch_buffer_grow; + __libc_scratch_buffer_grow_preserve; + __libc_scratch_buffer_set_array_size; + + + # Internal name for reallocarray + __libc_reallocarray; + + # dynarray support + __libc_dynarray_at_failure; + __libc_dynarray_emplace_enlarge; + __libc_dynarray_finalize; + __libc_dynarray_resize; + __libc_dynarray_resize_clear; + } +} diff --git a/REORG.TODO/malloc/arena.c b/REORG.TODO/malloc/arena.c new file mode 100644 index 0000000000..660d638c93 --- /dev/null +++ b/REORG.TODO/malloc/arena.c @@ -0,0 +1,992 @@ +/* Malloc implementation for multiple threads without lock contention. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Wolfram Gloger <wg@malloc.de>, 2001. + + 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <stdbool.h> + +#if HAVE_TUNABLES +# define TUNABLE_NAMESPACE malloc +#endif +#include <elf/dl-tunables.h> + +/* Compile-time constants. */ + +#define HEAP_MIN_SIZE (32 * 1024) +#ifndef HEAP_MAX_SIZE +# ifdef DEFAULT_MMAP_THRESHOLD_MAX +# define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX) +# else +# define HEAP_MAX_SIZE (1024 * 1024) /* must be a power of two */ +# endif +#endif + +/* HEAP_MIN_SIZE and HEAP_MAX_SIZE limit the size of mmap()ed heaps + that are dynamically created for multi-threaded programs. The + maximum size must be a power of two, for fast determination of + which heap belongs to a chunk. It should be much larger than the + mmap threshold, so that requests with a size just below that + threshold can be fulfilled without creating too many heaps. */ + +/***************************************************************************/ + +#define top(ar_ptr) ((ar_ptr)->top) + +/* A heap is a single contiguous memory region holding (coalesceable) + malloc_chunks. It is allocated with mmap() and always starts at an + address aligned to HEAP_MAX_SIZE. */ + +typedef struct _heap_info +{ + mstate ar_ptr; /* Arena for this heap. */ + struct _heap_info *prev; /* Previous heap. */ + size_t size; /* Current size in bytes. */ + size_t mprotect_size; /* Size in bytes that has been mprotected + PROT_READ|PROT_WRITE. */ + /* Make sure the following data is properly aligned, particularly + that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of + MALLOC_ALIGNMENT. */ + char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; +} heap_info; + +/* Get a compile-time error if the heap_info padding is not correct + to make alignment work as expected in sYSMALLOc. */ +extern int sanity_check_heap_info_alignment[(sizeof (heap_info) + + 2 * SIZE_SZ) % MALLOC_ALIGNMENT + ? -1 : 1]; + +/* Thread specific data. */ + +static __thread mstate thread_arena attribute_tls_model_ie; + +/* Arena free list. free_list_lock synchronizes access to the + free_list variable below, and the next_free and attached_threads + members of struct malloc_state objects. No other locks must be + acquired after free_list_lock has been acquired. */ + +__libc_lock_define_initialized (static, free_list_lock); +static size_t narenas = 1; +static mstate free_list; + +/* list_lock prevents concurrent writes to the next member of struct + malloc_state objects. + + Read access to the next member is supposed to synchronize with the + atomic_write_barrier and the write to the next member in + _int_new_arena. This suffers from data races; see the FIXME + comments in _int_new_arena and reused_arena. + + list_lock also prevents concurrent forks. At the time list_lock is + acquired, no arena lock must have been acquired, but it is + permitted to acquire arena locks subsequently, while list_lock is + acquired. */ +__libc_lock_define_initialized (static, list_lock); + +/* Already initialized? */ +int __malloc_initialized = -1; + +/**************************************************************************/ + + +/* arena_get() acquires an arena and locks the corresponding mutex. + First, try the one last locked successfully by this thread. (This + is the common case and handled with a macro for speed.) Then, loop + once over the circularly linked list of arenas. If no arena is + readily available, create a new one. In this latter case, `size' + is just a hint as to how much memory will be required immediately + in the new arena. */ + +#define arena_get(ptr, size) do { \ + ptr = thread_arena; \ + arena_lock (ptr, size); \ + } while (0) + +#define arena_lock(ptr, size) do { \ + if (ptr && !arena_is_corrupt (ptr)) \ + __libc_lock_lock (ptr->mutex); \ + else \ + ptr = arena_get2 ((size), NULL); \ + } while (0) + +/* find the heap and corresponding arena for a given ptr */ + +#define heap_for_ptr(ptr) \ + ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) +#define arena_for_chunk(ptr) \ + (chunk_main_arena (ptr) ? &main_arena : heap_for_ptr (ptr)->ar_ptr) + + +/**************************************************************************/ + +/* atfork support. */ + +/* The following three functions are called around fork from a + multi-threaded process. We do not use the general fork handler + mechanism to make sure that our handlers are the last ones being + called, so that other fork handlers can use the malloc + subsystem. */ + +void +internal_function +__malloc_fork_lock_parent (void) +{ + if (__malloc_initialized < 1) + return; + + /* We do not acquire free_list_lock here because we completely + reconstruct free_list in __malloc_fork_unlock_child. */ + + __libc_lock_lock (list_lock); + + for (mstate ar_ptr = &main_arena;; ) + { + __libc_lock_lock (ar_ptr->mutex); + ar_ptr = ar_ptr->next; + if (ar_ptr == &main_arena) + break; + } +} + +void +internal_function +__malloc_fork_unlock_parent (void) +{ + if (__malloc_initialized < 1) + return; + + for (mstate ar_ptr = &main_arena;; ) + { + __libc_lock_unlock (ar_ptr->mutex); + ar_ptr = ar_ptr->next; + if (ar_ptr == &main_arena) + break; + } + __libc_lock_unlock (list_lock); +} + +void +internal_function +__malloc_fork_unlock_child (void) +{ + if (__malloc_initialized < 1) + return; + + /* Push all arenas to the free list, except thread_arena, which is + attached to the current thread. */ + __libc_lock_init (free_list_lock); + if (thread_arena != NULL) + thread_arena->attached_threads = 1; + free_list = NULL; + for (mstate ar_ptr = &main_arena;; ) + { + __libc_lock_init (ar_ptr->mutex); + if (ar_ptr != thread_arena) + { + /* This arena is no longer attached to any thread. */ + ar_ptr->attached_threads = 0; + ar_ptr->next_free = free_list; + free_list = ar_ptr; + } + ar_ptr = ar_ptr->next; + if (ar_ptr == &main_arena) + break; + } + + __libc_lock_init (list_lock); +} + +#if HAVE_TUNABLES +static inline int do_set_mallopt_check (int32_t value); +void +TUNABLE_CALLBACK (set_mallopt_check) (tunable_val_t *valp) +{ + int32_t value = (int32_t) valp->numval; + do_set_mallopt_check (value); + if (check_action != 0) + __malloc_check_init (); +} + +# define TUNABLE_CALLBACK_FNDECL(__name, __type) \ +static inline int do_ ## __name (__type value); \ +void \ +TUNABLE_CALLBACK (__name) (tunable_val_t *valp) \ +{ \ + __type value = (__type) (valp)->numval; \ + do_ ## __name (value); \ +} + +TUNABLE_CALLBACK_FNDECL (set_mmap_threshold, size_t) +TUNABLE_CALLBACK_FNDECL (set_mmaps_max, int32_t) +TUNABLE_CALLBACK_FNDECL (set_top_pad, size_t) +TUNABLE_CALLBACK_FNDECL (set_perturb_byte, int32_t) +TUNABLE_CALLBACK_FNDECL (set_trim_threshold, size_t) +TUNABLE_CALLBACK_FNDECL (set_arena_max, size_t) +TUNABLE_CALLBACK_FNDECL (set_arena_test, size_t) +#else +/* Initialization routine. */ +#include <string.h> +extern char **_environ; + +static char * +internal_function +next_env_entry (char ***position) +{ + char **current = *position; + char *result = NULL; + + while (*current != NULL) + { + if (__builtin_expect ((*current)[0] == 'M', 0) + && (*current)[1] == 'A' + && (*current)[2] == 'L' + && (*current)[3] == 'L' + && (*current)[4] == 'O' + && (*current)[5] == 'C' + && (*current)[6] == '_') + { + result = &(*current)[7]; + + /* Save current position for next visit. */ + *position = ++current; + + break; + } + + ++current; + } + + return result; +} +#endif + + +#ifdef SHARED +static void * +__failing_morecore (ptrdiff_t d) +{ + return (void *) MORECORE_FAILURE; +} + +extern struct dl_open_hook *_dl_open_hook; +libc_hidden_proto (_dl_open_hook); +#endif + +static void +ptmalloc_init (void) +{ + if (__malloc_initialized >= 0) + return; + + __malloc_initialized = 0; + +#ifdef SHARED + /* In case this libc copy is in a non-default namespace, never use brk. + Likewise if dlopened from statically linked program. */ + Dl_info di; + struct link_map *l; + + if (_dl_open_hook != NULL + || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0 + && l->l_ns != LM_ID_BASE)) + __morecore = __failing_morecore; +#endif + + thread_arena = &main_arena; + +#if HAVE_TUNABLES + /* Ensure initialization/consolidation and do it under a lock so that a + thread attempting to use the arena in parallel waits on us till we + finish. */ + __libc_lock_lock (main_arena.mutex); + malloc_consolidate (&main_arena); + + TUNABLE_GET (check, int32_t, TUNABLE_CALLBACK (set_mallopt_check)); + TUNABLE_GET (top_pad, size_t, TUNABLE_CALLBACK (set_top_pad)); + TUNABLE_GET (perturb, int32_t, TUNABLE_CALLBACK (set_perturb_byte)); + TUNABLE_GET (mmap_threshold, size_t, TUNABLE_CALLBACK (set_mmap_threshold)); + TUNABLE_GET (trim_threshold, size_t, TUNABLE_CALLBACK (set_trim_threshold)); + TUNABLE_GET (mmap_max, int32_t, TUNABLE_CALLBACK (set_mmaps_max)); + TUNABLE_GET (arena_max, size_t, TUNABLE_CALLBACK (set_arena_max)); + TUNABLE_GET (arena_test, size_t, TUNABLE_CALLBACK (set_arena_test)); + __libc_lock_unlock (main_arena.mutex); +#else + const char *s = NULL; + if (__glibc_likely (_environ != NULL)) + { + char **runp = _environ; + char *envline; + + while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL, + 0)) + { + size_t len = strcspn (envline, "="); + + if (envline[len] != '=') + /* This is a "MALLOC_" variable at the end of the string + without a '=' character. Ignore it since otherwise we + will access invalid memory below. */ + continue; + + switch (len) + { + case 6: + if (memcmp (envline, "CHECK_", 6) == 0) + s = &envline[7]; + break; + case 8: + if (!__builtin_expect (__libc_enable_secure, 0)) + { + if (memcmp (envline, "TOP_PAD_", 8) == 0) + __libc_mallopt (M_TOP_PAD, atoi (&envline[9])); + else if (memcmp (envline, "PERTURB_", 8) == 0) + __libc_mallopt (M_PERTURB, atoi (&envline[9])); + } + break; + case 9: + if (!__builtin_expect (__libc_enable_secure, 0)) + { + if (memcmp (envline, "MMAP_MAX_", 9) == 0) + __libc_mallopt (M_MMAP_MAX, atoi (&envline[10])); + else if (memcmp (envline, "ARENA_MAX", 9) == 0) + __libc_mallopt (M_ARENA_MAX, atoi (&envline[10])); + } + break; + case 10: + if (!__builtin_expect (__libc_enable_secure, 0)) + { + if (memcmp (envline, "ARENA_TEST", 10) == 0) + __libc_mallopt (M_ARENA_TEST, atoi (&envline[11])); + } + break; + case 15: + if (!__builtin_expect (__libc_enable_secure, 0)) + { + if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0) + __libc_mallopt (M_TRIM_THRESHOLD, atoi (&envline[16])); + else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0) + __libc_mallopt (M_MMAP_THRESHOLD, atoi (&envline[16])); + } + break; + default: + break; + } + } + } + if (s && s[0]) + { + __libc_mallopt (M_CHECK_ACTION, (int) (s[0] - '0')); + if (check_action != 0) + __malloc_check_init (); + } +#endif + +#if HAVE_MALLOC_INIT_HOOK + void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook); + if (hook != NULL) + (*hook)(); +#endif + __malloc_initialized = 1; +} + +/* Managing heaps and arenas (for concurrent threads) */ + +#if MALLOC_DEBUG > 1 + +/* Print the complete contents of a single heap to stderr. */ + +static void +dump_heap (heap_info *heap) +{ + char *ptr; + mchunkptr p; + + fprintf (stderr, "Heap %p, size %10lx:\n", heap, (long) heap->size); + ptr = (heap->ar_ptr != (mstate) (heap + 1)) ? + (char *) (heap + 1) : (char *) (heap + 1) + sizeof (struct malloc_state); + p = (mchunkptr) (((unsigned long) ptr + MALLOC_ALIGN_MASK) & + ~MALLOC_ALIGN_MASK); + for (;; ) + { + fprintf (stderr, "chunk %p size %10lx", p, (long) p->size); + if (p == top (heap->ar_ptr)) + { + fprintf (stderr, " (top)\n"); + break; + } + else if (p->size == (0 | PREV_INUSE)) + { + fprintf (stderr, " (fence)\n"); + break; + } + fprintf (stderr, "\n"); + p = next_chunk (p); + } +} +#endif /* MALLOC_DEBUG > 1 */ + +/* If consecutive mmap (0, HEAP_MAX_SIZE << 1, ...) calls return decreasing + addresses as opposed to increasing, new_heap would badly fragment the + address space. In that case remember the second HEAP_MAX_SIZE part + aligned to HEAP_MAX_SIZE from last mmap (0, HEAP_MAX_SIZE << 1, ...) + call (if it is already aligned) and try to reuse it next time. We need + no locking for it, as kernel ensures the atomicity for us - worst case + we'll call mmap (addr, HEAP_MAX_SIZE, ...) for some value of addr in + multiple threads, but only one will succeed. */ +static char *aligned_heap_area; + +/* Create a new heap. size is automatically rounded up to a multiple + of the page size. */ + +static heap_info * +internal_function +new_heap (size_t size, size_t top_pad) +{ + size_t pagesize = GLRO (dl_pagesize); + char *p1, *p2; + unsigned long ul; + heap_info *h; + + if (size + top_pad < HEAP_MIN_SIZE) + size = HEAP_MIN_SIZE; + else if (size + top_pad <= HEAP_MAX_SIZE) + size += top_pad; + else if (size > HEAP_MAX_SIZE) + return 0; + else + size = HEAP_MAX_SIZE; + size = ALIGN_UP (size, pagesize); + + /* A memory region aligned to a multiple of HEAP_MAX_SIZE is needed. + No swap space needs to be reserved for the following large + mapping (on Linux, this is the case for all non-writable mappings + anyway). */ + p2 = MAP_FAILED; + if (aligned_heap_area) + { + p2 = (char *) MMAP (aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE, + MAP_NORESERVE); + aligned_heap_area = NULL; + if (p2 != MAP_FAILED && ((unsigned long) p2 & (HEAP_MAX_SIZE - 1))) + { + __munmap (p2, HEAP_MAX_SIZE); + p2 = MAP_FAILED; + } + } + if (p2 == MAP_FAILED) + { + p1 = (char *) MMAP (0, HEAP_MAX_SIZE << 1, PROT_NONE, MAP_NORESERVE); + if (p1 != MAP_FAILED) + { + p2 = (char *) (((unsigned long) p1 + (HEAP_MAX_SIZE - 1)) + & ~(HEAP_MAX_SIZE - 1)); + ul = p2 - p1; + if (ul) + __munmap (p1, ul); + else + aligned_heap_area = p2 + HEAP_MAX_SIZE; + __munmap (p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul); + } + else + { + /* Try to take the chance that an allocation of only HEAP_MAX_SIZE + is already aligned. */ + p2 = (char *) MMAP (0, HEAP_MAX_SIZE, PROT_NONE, MAP_NORESERVE); + if (p2 == MAP_FAILED) + return 0; + + if ((unsigned long) p2 & (HEAP_MAX_SIZE - 1)) + { + __munmap (p2, HEAP_MAX_SIZE); + return 0; + } + } + } + if (__mprotect (p2, size, PROT_READ | PROT_WRITE) != 0) + { + __munmap (p2, HEAP_MAX_SIZE); + return 0; + } + h = (heap_info *) p2; + h->size = size; + h->mprotect_size = size; + LIBC_PROBE (memory_heap_new, 2, h, h->size); + return h; +} + +/* Grow a heap. size is automatically rounded up to a + multiple of the page size. */ + +static int +grow_heap (heap_info *h, long diff) +{ + size_t pagesize = GLRO (dl_pagesize); + long new_size; + + diff = ALIGN_UP (diff, pagesize); + new_size = (long) h->size + diff; + if ((unsigned long) new_size > (unsigned long) HEAP_MAX_SIZE) + return -1; + + if ((unsigned long) new_size > h->mprotect_size) + { + if (__mprotect ((char *) h + h->mprotect_size, + (unsigned long) new_size - h->mprotect_size, + PROT_READ | PROT_WRITE) != 0) + return -2; + + h->mprotect_size = new_size; + } + + h->size = new_size; + LIBC_PROBE (memory_heap_more, 2, h, h->size); + return 0; +} + +/* Shrink a heap. */ + +static int +shrink_heap (heap_info *h, long diff) +{ + long new_size; + + new_size = (long) h->size - diff; + if (new_size < (long) sizeof (*h)) + return -1; + + /* Try to re-map the extra heap space freshly to save memory, and make it + inaccessible. See malloc-sysdep.h to know when this is true. */ + if (__glibc_unlikely (check_may_shrink_heap ())) + { + if ((char *) MMAP ((char *) h + new_size, diff, PROT_NONE, + MAP_FIXED) == (char *) MAP_FAILED) + return -2; + + h->mprotect_size = new_size; + } + else + __madvise ((char *) h + new_size, diff, MADV_DONTNEED); + /*fprintf(stderr, "shrink %p %08lx\n", h, new_size);*/ + + h->size = new_size; + LIBC_PROBE (memory_heap_less, 2, h, h->size); + return 0; +} + +/* Delete a heap. */ + +#define delete_heap(heap) \ + do { \ + if ((char *) (heap) + HEAP_MAX_SIZE == aligned_heap_area) \ + aligned_heap_area = NULL; \ + __munmap ((char *) (heap), HEAP_MAX_SIZE); \ + } while (0) + +static int +internal_function +heap_trim (heap_info *heap, size_t pad) +{ + mstate ar_ptr = heap->ar_ptr; + unsigned long pagesz = GLRO (dl_pagesize); + mchunkptr top_chunk = top (ar_ptr), p, bck, fwd; + heap_info *prev_heap; + long new_size, top_size, top_area, extra, prev_size, misalign; + + /* Can this heap go away completely? */ + while (top_chunk == chunk_at_offset (heap, sizeof (*heap))) + { + prev_heap = heap->prev; + prev_size = prev_heap->size - (MINSIZE - 2 * SIZE_SZ); + p = chunk_at_offset (prev_heap, prev_size); + /* fencepost must be properly aligned. */ + misalign = ((long) p) & MALLOC_ALIGN_MASK; + p = chunk_at_offset (prev_heap, prev_size - misalign); + assert (chunksize_nomask (p) == (0 | PREV_INUSE)); /* must be fencepost */ + p = prev_chunk (p); + new_size = chunksize (p) + (MINSIZE - 2 * SIZE_SZ) + misalign; + assert (new_size > 0 && new_size < (long) (2 * MINSIZE)); + if (!prev_inuse (p)) + new_size += prev_size (p); + assert (new_size > 0 && new_size < HEAP_MAX_SIZE); + if (new_size + (HEAP_MAX_SIZE - prev_heap->size) < pad + MINSIZE + pagesz) + break; + ar_ptr->system_mem -= heap->size; + LIBC_PROBE (memory_heap_free, 2, heap, heap->size); + delete_heap (heap); + heap = prev_heap; + if (!prev_inuse (p)) /* consolidate backward */ + { + p = prev_chunk (p); + unlink (ar_ptr, p, bck, fwd); + } + assert (((unsigned long) ((char *) p + new_size) & (pagesz - 1)) == 0); + assert (((char *) p + new_size) == ((char *) heap + heap->size)); + top (ar_ptr) = top_chunk = p; + set_head (top_chunk, new_size | PREV_INUSE); + /*check_chunk(ar_ptr, top_chunk);*/ + } + + /* Uses similar logic for per-thread arenas as the main arena with systrim + and _int_free by preserving the top pad and rounding down to the nearest + page. */ + top_size = chunksize (top_chunk); + if ((unsigned long)(top_size) < + (unsigned long)(mp_.trim_threshold)) + return 0; + + top_area = top_size - MINSIZE - 1; + if (top_area < 0 || (size_t) top_area <= pad) + return 0; + + /* Release in pagesize units and round down to the nearest page. */ + extra = ALIGN_DOWN(top_area - pad, pagesz); + if (extra == 0) + return 0; + + /* Try to shrink. */ + if (shrink_heap (heap, extra) != 0) + return 0; + + ar_ptr->system_mem -= extra; + + /* Success. Adjust top accordingly. */ + set_head (top_chunk, (top_size - extra) | PREV_INUSE); + /*check_chunk(ar_ptr, top_chunk);*/ + return 1; +} + +/* Create a new arena with initial size "size". */ + +/* If REPLACED_ARENA is not NULL, detach it from this thread. Must be + called while free_list_lock is held. */ +static void +detach_arena (mstate replaced_arena) +{ + if (replaced_arena != NULL) + { + assert (replaced_arena->attached_threads > 0); + /* The current implementation only detaches from main_arena in + case of allocation failure. This means that it is likely not + beneficial to put the arena on free_list even if the + reference count reaches zero. */ + --replaced_arena->attached_threads; + } +} + +static mstate +_int_new_arena (size_t size) +{ + mstate a; + heap_info *h; + char *ptr; + unsigned long misalign; + + h = new_heap (size + (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT), + mp_.top_pad); + if (!h) + { + /* Maybe size is too large to fit in a single heap. So, just try + to create a minimally-sized arena and let _int_malloc() attempt + to deal with the large request via mmap_chunk(). */ + h = new_heap (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT, mp_.top_pad); + if (!h) + return 0; + } + a = h->ar_ptr = (mstate) (h + 1); + malloc_init_state (a); + a->attached_threads = 1; + /*a->next = NULL;*/ + a->system_mem = a->max_system_mem = h->size; + + /* Set up the top chunk, with proper alignment. */ + ptr = (char *) (a + 1); + misalign = (unsigned long) chunk2mem (ptr) & MALLOC_ALIGN_MASK; + if (misalign > 0) + ptr += MALLOC_ALIGNMENT - misalign; + top (a) = (mchunkptr) ptr; + set_head (top (a), (((char *) h + h->size) - ptr) | PREV_INUSE); + + LIBC_PROBE (memory_arena_new, 2, a, size); + mstate replaced_arena = thread_arena; + thread_arena = a; + __libc_lock_init (a->mutex); + + __libc_lock_lock (list_lock); + + /* Add the new arena to the global list. */ + a->next = main_arena.next; + /* FIXME: The barrier is an attempt to synchronize with read access + in reused_arena, which does not acquire list_lock while + traversing the list. */ + atomic_write_barrier (); + main_arena.next = a; + + __libc_lock_unlock (list_lock); + + __libc_lock_lock (free_list_lock); + detach_arena (replaced_arena); + __libc_lock_unlock (free_list_lock); + + /* Lock this arena. NB: Another thread may have been attached to + this arena because the arena is now accessible from the + main_arena.next list and could have been picked by reused_arena. + This can only happen for the last arena created (before the arena + limit is reached). At this point, some arena has to be attached + to two threads. We could acquire the arena lock before list_lock + to make it less likely that reused_arena picks this new arena, + but this could result in a deadlock with + __malloc_fork_lock_parent. */ + + __libc_lock_lock (a->mutex); + + return a; +} + + +/* Remove an arena from free_list. */ +static mstate +get_free_list (void) +{ + mstate replaced_arena = thread_arena; + mstate result = free_list; + if (result != NULL) + { + __libc_lock_lock (free_list_lock); + result = free_list; + if (result != NULL) + { + free_list = result->next_free; + + /* The arena will be attached to this thread. */ + assert (result->attached_threads == 0); + result->attached_threads = 1; + + detach_arena (replaced_arena); + } + __libc_lock_unlock (free_list_lock); + + if (result != NULL) + { + LIBC_PROBE (memory_arena_reuse_free_list, 1, result); + __libc_lock_lock (result->mutex); + thread_arena = result; + } + } + + return result; +} + +/* Remove the arena from the free list (if it is present). + free_list_lock must have been acquired by the caller. */ +static void +remove_from_free_list (mstate arena) +{ + mstate *previous = &free_list; + for (mstate p = free_list; p != NULL; p = p->next_free) + { + assert (p->attached_threads == 0); + if (p == arena) + { + /* Remove the requested arena from the list. */ + *previous = p->next_free; + break; + } + else + previous = &p->next_free; + } +} + +/* Lock and return an arena that can be reused for memory allocation. + Avoid AVOID_ARENA as we have already failed to allocate memory in + it and it is currently locked. */ +static mstate +reused_arena (mstate avoid_arena) +{ + mstate result; + /* FIXME: Access to next_to_use suffers from data races. */ + static mstate next_to_use; + if (next_to_use == NULL) + next_to_use = &main_arena; + + /* Iterate over all arenas (including those linked from + free_list). */ + result = next_to_use; + do + { + if (!arena_is_corrupt (result) && !__libc_lock_trylock (result->mutex)) + goto out; + + /* FIXME: This is a data race, see _int_new_arena. */ + result = result->next; + } + while (result != next_to_use); + + /* Avoid AVOID_ARENA as we have already failed to allocate memory + in that arena and it is currently locked. */ + if (result == avoid_arena) + result = result->next; + + /* Make sure that the arena we get is not corrupted. */ + mstate begin = result; + while (arena_is_corrupt (result) || result == avoid_arena) + { + result = result->next; + if (result == begin) + /* We looped around the arena list. We could not find any + arena that was either not corrupted or not the one we + wanted to avoid. */ + return NULL; + } + + /* No arena available without contention. Wait for the next in line. */ + LIBC_PROBE (memory_arena_reuse_wait, 3, &result->mutex, result, avoid_arena); + __libc_lock_lock (result->mutex); + +out: + /* Attach the arena to the current thread. */ + { + /* Update the arena thread attachment counters. */ + mstate replaced_arena = thread_arena; + __libc_lock_lock (free_list_lock); + detach_arena (replaced_arena); + + /* We may have picked up an arena on the free list. We need to + preserve the invariant that no arena on the free list has a + positive attached_threads counter (otherwise, + arena_thread_freeres cannot use the counter to determine if the + arena needs to be put on the free list). We unconditionally + remove the selected arena from the free list. The caller of + reused_arena checked the free list and observed it to be empty, + so the list is very short. */ + remove_from_free_list (result); + + ++result->attached_threads; + + __libc_lock_unlock (free_list_lock); + } + + LIBC_PROBE (memory_arena_reuse, 2, result, avoid_arena); + thread_arena = result; + next_to_use = result->next; + + return result; +} + +static mstate +internal_function +arena_get2 (size_t size, mstate avoid_arena) +{ + mstate a; + + static size_t narenas_limit; + + a = get_free_list (); + if (a == NULL) + { + /* Nothing immediately available, so generate a new arena. */ + if (narenas_limit == 0) + { + if (mp_.arena_max != 0) + narenas_limit = mp_.arena_max; + else if (narenas > mp_.arena_test) + { + int n = __get_nprocs (); + + if (n >= 1) + narenas_limit = NARENAS_FROM_NCORES (n); + else + /* We have no information about the system. Assume two + cores. */ + narenas_limit = NARENAS_FROM_NCORES (2); + } + } + repeat:; + size_t n = narenas; + /* NB: the following depends on the fact that (size_t)0 - 1 is a + very large number and that the underflow is OK. If arena_max + is set the value of arena_test is irrelevant. If arena_test + is set but narenas is not yet larger or equal to arena_test + narenas_limit is 0. There is no possibility for narenas to + be too big for the test to always fail since there is not + enough address space to create that many arenas. */ + if (__glibc_unlikely (n <= narenas_limit - 1)) + { + if (catomic_compare_and_exchange_bool_acq (&narenas, n + 1, n)) + goto repeat; + a = _int_new_arena (size); + if (__glibc_unlikely (a == NULL)) + catomic_decrement (&narenas); + } + else + a = reused_arena (avoid_arena); + } + return a; +} + +/* If we don't have the main arena, then maybe the failure is due to running + out of mmapped areas, so we can try allocating on the main arena. + Otherwise, it is likely that sbrk() has failed and there is still a chance + to mmap(), so try one of the other arenas. */ +static mstate +arena_get_retry (mstate ar_ptr, size_t bytes) +{ + LIBC_PROBE (memory_arena_retry, 2, bytes, ar_ptr); + if (ar_ptr != &main_arena) + { + __libc_lock_unlock (ar_ptr->mutex); + /* Don't touch the main arena if it is corrupt. */ + if (arena_is_corrupt (&main_arena)) + return NULL; + + ar_ptr = &main_arena; + __libc_lock_lock (ar_ptr->mutex); + } + else + { + __libc_lock_unlock (ar_ptr->mutex); + ar_ptr = arena_get2 (bytes, ar_ptr); + } + + return ar_ptr; +} + +static void __attribute__ ((section ("__libc_thread_freeres_fn"))) +arena_thread_freeres (void) +{ + mstate a = thread_arena; + thread_arena = NULL; + + if (a != NULL) + { + __libc_lock_lock (free_list_lock); + /* If this was the last attached thread for this arena, put the + arena on the free list. */ + assert (a->attached_threads > 0); + if (--a->attached_threads == 0) + { + a->next_free = free_list; + free_list = a; + } + __libc_lock_unlock (free_list_lock); + } +} +text_set_element (__libc_thread_subfreeres, arena_thread_freeres); + +/* + * Local variables: + * c-basic-offset: 2 + * End: + */ diff --git a/REORG.TODO/malloc/dynarray-skeleton.c b/REORG.TODO/malloc/dynarray-skeleton.c new file mode 100644 index 0000000000..7a10e083f4 --- /dev/null +++ b/REORG.TODO/malloc/dynarray-skeleton.c @@ -0,0 +1,499 @@ +/* Type-safe arrays which grow dynamically. + Copyright (C) 2017 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/>. */ + +/* Pre-processor macros which act as parameters: + + DYNARRAY_STRUCT + The struct tag of dynamic array to be defined. + DYNARRAY_ELEMENT + The type name of the element type. Elements are copied + as if by memcpy, and can change address as the dynamic + array grows. + DYNARRAY_PREFIX + The prefix of the functions which are defined. + + The following parameters are optional: + + DYNARRAY_ELEMENT_FREE + DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the + contents of elements. E is of type DYNARRAY_ELEMENT *. + DYNARRAY_ELEMENT_INIT + DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new + element. E is of type DYNARRAY_ELEMENT *. + If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is + defined, new elements are automatically zero-initialized. + Otherwise, new elements have undefined contents. + DYNARRAY_INITIAL_SIZE + The size of the statically allocated array (default: + at least 2, more elements if they fit into 128 bytes). + Must be a preprocessor constant. If DYNARRAY_INITIAL_SIZE is 0, + there is no statically allocated array at, and all non-empty + arrays are heap-allocated. + DYNARRAY_FINAL_TYPE + The name of the type which holds the final array. If not + defined, is PREFIX##finalize not provided. DYNARRAY_FINAL_TYPE + must be a struct type, with members of type DYNARRAY_ELEMENT and + size_t at the start (in this order). + + These macros are undefined after this header file has been + included. + + The following types are provided (their members are private to the + dynarray implementation): + + struct DYNARRAY_STRUCT + + The following functions are provided: + + void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *); + void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *); + bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *); + void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *); + size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *); + DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t); + void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT); + DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *); + bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t); + void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *); + void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *); + + The following functions are provided are provided if the + prerequisites are met: + + bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *, + DYNARRAY_FINAL_TYPE *); + (if DYNARRAY_FINAL_TYPE is defined) + DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *, + size_t *); + (if DYNARRAY_FINAL_TYPE is not defined) +*/ + +#include <malloc/dynarray.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DYNARRAY_STRUCT +# error "DYNARRAY_STRUCT must be defined" +#endif + +#ifndef DYNARRAY_ELEMENT +# error "DYNARRAY_ELEMENT must be defined" +#endif + +#ifndef DYNARRAY_PREFIX +# error "DYNARRAY_PREFIX must be defined" +#endif + +#ifdef DYNARRAY_INITIAL_SIZE +# if DYNARRAY_INITIAL_SIZE < 0 +# error "DYNARRAY_INITIAL_SIZE must be non-negative" +# endif +# if DYNARRAY_INITIAL_SIZE > 0 +# define DYNARRAY_HAVE_SCRATCH 1 +# else +# define DYNARRAY_HAVE_SCRATCH 0 +# endif +#else +/* Provide a reasonable default which limits the size of + DYNARRAY_STRUCT. */ +# define DYNARRAY_INITIAL_SIZE \ + (sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT)) +# define DYNARRAY_HAVE_SCRATCH 1 +#endif + +/* Public type definitions. */ + +/* All fields of this struct are private to the implementation. */ +struct DYNARRAY_STRUCT +{ + union + { + struct dynarray_header dynarray_abstract; + struct + { + /* These fields must match struct dynarray_header. */ + size_t used; + size_t allocated; + DYNARRAY_ELEMENT *array; + } dynarray_header; + }; + +#if DYNARRAY_HAVE_SCRATCH + /* Initial inline allocation. */ + DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE]; +#endif +}; + +/* Internal use only: Helper macros. */ + +/* Ensure macro-expansion of DYNARRAY_PREFIX. */ +#define DYNARRAY_CONCAT0(prefix, name) prefix##name +#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name) +#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name) + +/* Address of the scratch buffer if any. */ +#if DYNARRAY_HAVE_SCRATCH +# define DYNARRAY_SCRATCH(list) (list)->scratch +#else +# define DYNARRAY_SCRATCH(list) NULL +#endif + +/* Internal use only: Helper functions. */ + +/* Internal function. Call DYNARRAY_ELEMENT_FREE with the array + elements. Name mangling needed due to the DYNARRAY_ELEMENT_FREE + macro expansion. */ +static inline void +DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array, + size_t __dynarray_used) +{ +#ifdef DYNARRAY_ELEMENT_FREE + for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i) + DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]); +#endif /* DYNARRAY_ELEMENT_FREE */ +} + +/* Internal function. Free the non-scratch array allocation. */ +static inline void +DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list) +{ +#if DYNARRAY_HAVE_SCRATCH + if (list->dynarray_header.array != list->scratch) + free (list->dynarray_header.array); +#else + free (list->dynarray_header.array); +#endif +} + +/* Public functions. */ + +/* Initialize a dynamic array object. This must be called before any + use of the object. */ +__attribute__ ((nonnull (1))) +static void +DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list) +{ + list->dynarray_header.used = 0; + list->dynarray_header.allocated = DYNARRAY_INITIAL_SIZE; + list->dynarray_header.array = DYNARRAY_SCRATCH (list); +} + +/* Deallocate the dynamic array and its elements. */ +__attribute__ ((unused, nonnull (1))) +static void +DYNARRAY_NAME (free) (struct DYNARRAY_STRUCT *list) +{ + DYNARRAY_NAME (free__elements__) + (list->dynarray_header.array, list->dynarray_header.used); + DYNARRAY_NAME (free__array__) (list); + DYNARRAY_NAME (init) (list); +} + +/* Return true if the dynamic array is in an error state. */ +__attribute__ ((nonnull (1))) +static inline bool +DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list) +{ + return list->dynarray_header.allocated == __dynarray_error_marker (); +} + +/* Mark the dynamic array as failed. All elements are deallocated as + a side effect. */ +__attribute__ ((nonnull (1))) +static void +DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list) +{ + DYNARRAY_NAME (free__elements__) + (list->dynarray_header.array, list->dynarray_header.used); + DYNARRAY_NAME (free__array__) (list); + list->dynarray_header.array = DYNARRAY_SCRATCH (list); + list->dynarray_header.used = 0; + list->dynarray_header.allocated = __dynarray_error_marker (); +} + +/* Return the number of elements which have been added to the dynamic + array. */ +__attribute__ ((nonnull (1))) +static inline size_t +DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list) +{ + return list->dynarray_header.used; +} + +/* Return a pointer to the array element at INDEX. Terminate the + process if INDEX is out of bounds. */ +__attribute__ ((nonnull (1))) +static inline DYNARRAY_ELEMENT * +DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index) +{ + if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list))) + __libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index); + return list->dynarray_header.array + index; +} + +/* Internal function. Slow path for the add function below. */ +static void +DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item) +{ + if (__glibc_unlikely + (!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract, + DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT)))) + { + DYNARRAY_NAME (mark_failed) (list); + return; + } + + /* Copy the new element and increase the array length. */ + list->dynarray_header.array[list->dynarray_header.used++] = item; +} + +/* Add ITEM at the end of the array, enlarging it by one element. + Mark *LIST as failed if the dynamic array allocation size cannot be + increased. */ +__attribute__ ((unused, nonnull (1))) +static inline void +DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item) +{ + /* Do nothing in case of previous error. */ + if (DYNARRAY_NAME (has_failed) (list)) + return; + + /* Enlarge the array if necessary. */ + if (__glibc_unlikely (list->dynarray_header.used + == list->dynarray_header.allocated)) + { + DYNARRAY_NAME (add__) (list, item); + return; + } + + /* Copy the new element and increase the array length. */ + list->dynarray_header.array[list->dynarray_header.used++] = item; +} + +/* Internal function. Building block for the emplace functions below. + Assumes space for one more element in *LIST. */ +static inline DYNARRAY_ELEMENT * +DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list) +{ + DYNARRAY_ELEMENT *result + = &list->dynarray_header.array[list->dynarray_header.used]; + ++list->dynarray_header.used; +#if defined (DYNARRAY_ELEMENT_INIT) + DYNARRAY_ELEMENT_INIT (result); +#elif defined (DYNARRAY_ELEMENT_FREE) + memset (result, 0, sizeof (*result)); +#endif + return result; +} + +/* Internal function. Slow path for the emplace function below. */ +static DYNARRAY_ELEMENT * +DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list) +{ + if (__glibc_unlikely + (!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract, + DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT)))) + { + DYNARRAY_NAME (mark_failed) (list); + return NULL; + } + return DYNARRAY_NAME (emplace__tail__) (list); +} + +/* Allocate a place for a new element in *LIST and return a pointer to + it. The pointer can be NULL if the dynamic array cannot be + enlarged due to a memory allocation failure. */ +__attribute__ ((unused, warn_unused_result, nonnull (1))) +static +/* Avoid inlining with the larger initialization code. */ +#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE)) +inline +#endif +DYNARRAY_ELEMENT * +DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list) +{ + /* Do nothing in case of previous error. */ + if (DYNARRAY_NAME (has_failed) (list)) + return NULL; + + /* Enlarge the array if necessary. */ + if (__glibc_unlikely (list->dynarray_header.used + == list->dynarray_header.allocated)) + return (DYNARRAY_NAME (emplace__) (list)); + return DYNARRAY_NAME (emplace__tail__) (list); +} + +/* Change the size of *LIST to SIZE. If SIZE is larger than the + existing size, new elements are added (which can be initialized). + Otherwise, the list is truncated, and elements are freed. Return + false on memory allocation failure (and mark *LIST as failed). */ +__attribute__ ((unused, nonnull (1))) +static bool +DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size) +{ + if (size > list->dynarray_header.used) + { + bool ok; +#if defined (DYNARRAY_ELEMENT_INIT) + /* The new elements have to be initialized. */ + size_t old_size = list->dynarray_header.used; + ok = __libc_dynarray_resize (&list->dynarray_abstract, + size, DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT)); + if (ok) + for (size_t i = old_size; i < size; ++i) + { + DYNARRAY_ELEMENT_INIT (&list->dynarray_header.array[i]); + } +#elif defined (DYNARRAY_ELEMENT_FREE) + /* Zero initialization is needed so that the elements can be + safely freed. */ + ok = __libc_dynarray_resize_clear + (&list->dynarray_abstract, size, + DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT)); +#else + ok = __libc_dynarray_resize (&list->dynarray_abstract, + size, DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT)); +#endif + if (__glibc_unlikely (!ok)) + DYNARRAY_NAME (mark_failed) (list); + return ok; + } + else + { + /* The list has shrunk in size. Free the removed elements. */ + DYNARRAY_NAME (free__elements__) + (list->dynarray_header.array + size, + list->dynarray_header.used - size); + list->dynarray_header.used = size; + return true; + } +} + +/* Remove the last element of LIST if it is present. */ +__attribute__ ((unused, nonnull (1))) +static void +DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list) +{ + /* used > 0 implies that the array is the non-failed state. */ + if (list->dynarray_header.used > 0) + { + size_t new_length = list->dynarray_header.used - 1; +#ifdef DYNARRAY_ELEMENT_FREE + DYNARRAY_ELEMENT_FREE (&list->dynarray_header.array[new_length]); +#endif + list->dynarray_header.used = new_length; + } +} + +/* Remove all elements from the list. The elements are freed, but the + list itself is not. */ +__attribute__ ((unused, nonnull (1))) +static void +DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list) +{ + /* free__elements__ does nothing if the list is in the failed + state. */ + DYNARRAY_NAME (free__elements__) + (list->dynarray_header.array, list->dynarray_header.used); + list->dynarray_header.used = 0; +} + +#ifdef DYNARRAY_FINAL_TYPE +/* Transfer the dynamic array to a permanent location at *RESULT. + Returns true on success on false on allocation failure. In either + case, *LIST is re-initialized and can be reused. A NULL pointer is + stored in *RESULT if LIST refers to an empty list. On success, the + pointer in *RESULT is heap-allocated and must be deallocated using + free. */ +__attribute__ ((unused, warn_unused_result, nonnull (1, 2))) +static bool +DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, + DYNARRAY_FINAL_TYPE *result) +{ + struct dynarray_finalize_result res; + if (__libc_dynarray_finalize (&list->dynarray_abstract, + DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT), &res)) + { + /* On success, the result owns all the data. */ + DYNARRAY_NAME (init) (list); + *result = (DYNARRAY_FINAL_TYPE) { res.array, res.length }; + return true; + } + else + { + /* On error, we need to free all data. */ + DYNARRAY_NAME (free) (list); + errno = ENOMEM; + return false; + } +} +#else /* !DYNARRAY_FINAL_TYPE */ +/* Transfer the dynamic array to a heap-allocated array and return a + pointer to it. The pointer is NULL if memory allocation fails, or + if the array is empty, so this function should be used only for + arrays which are known not be empty (usually because they always + have a sentinel at the end). If LENGTHP is not NULL, the array + length is written to *LENGTHP. *LIST is re-initialized and can be + reused. */ +__attribute__ ((unused, warn_unused_result, nonnull (1))) +static DYNARRAY_ELEMENT * +DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp) +{ + struct dynarray_finalize_result res; + if (__libc_dynarray_finalize (&list->dynarray_abstract, + DYNARRAY_SCRATCH (list), + sizeof (DYNARRAY_ELEMENT), &res)) + { + /* On success, the result owns all the data. */ + DYNARRAY_NAME (init) (list); + if (lengthp != NULL) + *lengthp = res.length; + return res.array; + } + else + { + /* On error, we need to free all data. */ + DYNARRAY_NAME (free) (list); + errno = ENOMEM; + return NULL; + } +} +#endif /* !DYNARRAY_FINAL_TYPE */ + +/* Undo macro definitions. */ + +#undef DYNARRAY_CONCAT0 +#undef DYNARRAY_CONCAT1 +#undef DYNARRAY_NAME +#undef DYNARRAY_SCRATCH +#undef DYNARRAY_HAVE_SCRATCH + +#undef DYNARRAY_STRUCT +#undef DYNARRAY_ELEMENT +#undef DYNARRAY_PREFIX +#undef DYNARRAY_ELEMENT_FREE +#undef DYNARRAY_ELEMENT_INIT +#undef DYNARRAY_INITIAL_SIZE +#undef DYNARRAY_FINAL_TYPE diff --git a/REORG.TODO/malloc/dynarray.h b/REORG.TODO/malloc/dynarray.h new file mode 100644 index 0000000000..c73e08b6cf --- /dev/null +++ b/REORG.TODO/malloc/dynarray.h @@ -0,0 +1,176 @@ +/* Type-safe arrays which grow dynamically. Shared definitions. + Copyright (C) 2017 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/>. */ + +/* To use the dynarray facility, you need to include + <malloc/dynarray-skeleton.c> and define the parameter macros + documented in that file. + + A minimal example which provides a growing list of integers can be + defined like this: + + struct int_array + { + // Pointer to result array followed by its length, + // as required by DYNARRAY_FINAL_TYPE. + int *array; + size_t length; + }; + + #define DYNARRAY_STRUCT dynarray_int + #define DYNARRAY_ELEMENT int + #define DYNARRAY_PREFIX dynarray_int_ + #define DYNARRAY_FINAL_TYPE struct int_array + #include <malloc/dynarray-skeleton.c> + + To create a three-element array with elements 1, 2, 3, use this + code: + + struct dynarray_int dyn; + dynarray_int_init (&dyn); + for (int i = 1; i <= 3; ++i) + { + int *place = dynarray_int_emplace (&dyn); + assert (place != NULL); + *place = i; + } + struct int_array result; + bool ok = dynarray_int_finalize (&dyn, &result); + assert (ok); + assert (result.length == 3); + assert (result.array[0] == 1); + assert (result.array[1] == 2); + assert (result.array[2] == 3); + free (result.array); + + If the elements contain resources which must be freed, define + DYNARRAY_ELEMENT_FREE appropriately, like this: + + struct str_array + { + char **array; + size_t length; + }; + + #define DYNARRAY_STRUCT dynarray_str + #define DYNARRAY_ELEMENT char * + #define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr) + #define DYNARRAY_PREFIX dynarray_str_ + #define DYNARRAY_FINAL_TYPE struct str_array + #include <malloc/dynarray-skeleton.c> + + Compared to scratch buffers, dynamic arrays have the following + features: + + - They have an element type, and are not just an untyped buffer of + bytes. + + - When growing, previously stored elements are preserved. (It is + expected that scratch_buffer_grow_preserve and + scratch_buffer_set_array_size eventually go away because all + current users are moved to dynamic arrays.) + + - Scratch buffers have a more aggressive growth policy because + growing them typically means a retry of an operation (across an + NSS service module boundary), which is expensive. + + - For the same reason, scratch buffers have a much larger initial + stack allocation. */ + +#ifndef _DYNARRAY_H +#define _DYNARRAY_H + +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + +struct dynarray_header +{ + size_t used; + size_t allocated; + void *array; +}; + +/* Marker used in the allocated member to indicate that an error was + encountered. */ +static inline size_t +__dynarray_error_marker (void) +{ + return -1; +} + +/* Internal function. See the has_failed function in + dynarray-skeleton.c. */ +static inline bool +__dynarray_error (struct dynarray_header *list) +{ + return list->allocated == __dynarray_error_marker (); +} + +/* Internal function. Enlarge the dynamically allocated area of the + array to make room for one more element. SCRATCH is a pointer to + the scratch area (which is not heap-allocated and must not be + freed). ELEMENT_SIZE is the size, in bytes, of one element. + Return false on failure, true on success. */ +bool __libc_dynarray_emplace_enlarge (struct dynarray_header *, + void *scratch, size_t element_size); +libc_hidden_proto (__libc_dynarray_emplace_enlarge) + +/* Internal function. Enlarge the dynamically allocated area of the + array to make room for at least SIZE elements (which must be larger + than the existing used part of the dynamic array). SCRATCH is a + pointer to the scratch area (which is not heap-allocated and must + not be freed). ELEMENT_SIZE is the size, in bytes, of one element. + Return false on failure, true on success. */ +bool __libc_dynarray_resize (struct dynarray_header *, size_t size, + void *scratch, size_t element_size); +libc_hidden_proto (__libc_dynarray_resize) + +/* Internal function. Like __libc_dynarray_resize, but clear the new + part of the dynamic array. */ +bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size, + void *scratch, size_t element_size); +libc_hidden_proto (__libc_dynarray_resize_clear) + +/* Internal type. */ +struct dynarray_finalize_result +{ + void *array; + size_t length; +}; + +/* Internal function. Copy the dynamically-allocated area to an + explicitly-sized heap allocation. SCRATCH is a pointer to the + embedded scratch space. ELEMENT_SIZE is the size, in bytes, of the + element type. On success, true is returned, and pointer and length + are written to *RESULT. On failure, false is returned. The caller + has to take care of some of the memory management; this function is + expected to be called from dynarray-skeleton.c. */ +bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch, + size_t element_size, + struct dynarray_finalize_result *result); +libc_hidden_proto (__libc_dynarray_finalize) + + +/* Internal function. Terminate the process after an index error. + SIZE is the number of elements of the dynamic array. INDEX is the + lookup index which triggered the failure. */ +void __libc_dynarray_at_failure (size_t size, size_t index) + __attribute__ ((noreturn)); +libc_hidden_proto (__libc_dynarray_at_failure) + +#endif /* _DYNARRAY_H */ diff --git a/REORG.TODO/malloc/dynarray_at_failure.c b/REORG.TODO/malloc/dynarray_at_failure.c new file mode 100644 index 0000000000..fcc06f030b --- /dev/null +++ b/REORG.TODO/malloc/dynarray_at_failure.c @@ -0,0 +1,31 @@ +/* Report an dynamic array index out of bounds condition. + Copyright (C) 2017 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/>. */ + +#include <dynarray.h> +#include <stdio.h> + +void +__libc_dynarray_at_failure (size_t size, size_t index) +{ + char buf[200]; + __snprintf (buf, sizeof (buf), "Fatal glibc error: " + "array index %zu not less than array length %zu\n", + index, size); + __libc_fatal (buf); +} +libc_hidden_def (__libc_dynarray_at_failure) diff --git a/REORG.TODO/malloc/dynarray_emplace_enlarge.c b/REORG.TODO/malloc/dynarray_emplace_enlarge.c new file mode 100644 index 0000000000..dfc70017ce --- /dev/null +++ b/REORG.TODO/malloc/dynarray_emplace_enlarge.c @@ -0,0 +1,69 @@ +/* Increase the size of a dynamic array in preparation of an emplace operation. + Copyright (C) 2017 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/>. */ + +#include <dynarray.h> +#include <malloc-internal.h> +#include <stdlib.h> +#include <string.h> + +bool +__libc_dynarray_emplace_enlarge (struct dynarray_header *list, + void *scratch, size_t element_size) +{ + size_t new_allocated; + if (list->allocated == 0) + { + /* No scratch buffer provided. Choose a reasonable default + size. */ + if (element_size < 4) + new_allocated = 16; + if (element_size < 8) + new_allocated = 8; + else + new_allocated = 4; + } + else + /* Increase the allocated size, using an exponential growth + policy. */ + { + new_allocated = list->allocated + list->allocated / 2 + 1; + if (new_allocated <= list->allocated) + /* Overflow. */ + return false; + } + + size_t new_size; + if (check_mul_overflow_size_t (new_allocated, element_size, &new_size)) + return false; + void *new_array; + if (list->array == scratch) + { + /* The previous array was not heap-allocated. */ + new_array = malloc (new_size); + if (new_array != NULL && list->array != NULL) + memcpy (new_array, list->array, list->used * element_size); + } + else + new_array = realloc (list->array, new_size); + if (new_array == NULL) + return false; + list->array = new_array; + list->allocated = new_allocated; + return true; +} +libc_hidden_def (__libc_dynarray_emplace_enlarge) diff --git a/REORG.TODO/malloc/dynarray_finalize.c b/REORG.TODO/malloc/dynarray_finalize.c new file mode 100644 index 0000000000..6dd8705382 --- /dev/null +++ b/REORG.TODO/malloc/dynarray_finalize.c @@ -0,0 +1,62 @@ +/* Copy the dynamically-allocated area to an explicitly-sized heap allocation. + Copyright (C) 2017 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/>. */ + +#include <dynarray.h> +#include <stdlib.h> +#include <string.h> + +bool +__libc_dynarray_finalize (struct dynarray_header *list, + void *scratch, size_t element_size, + struct dynarray_finalize_result *result) +{ + if (__dynarray_error (list)) + /* The caller will reported the deferred error. */ + return false; + + size_t used = list->used; + + /* Empty list. */ + if (used == 0) + { + /* An empty list could still be backed by a heap-allocated + array. Free it if necessary. */ + if (list->array != scratch) + free (list->array); + *result = (struct dynarray_finalize_result) { NULL, 0 }; + return true; + } + + size_t allocation_size = used * element_size; + void *heap_array = malloc (allocation_size); + if (heap_array != NULL) + { + /* The new array takes ownership of the strings. */ + if (list->array != NULL) + memcpy (heap_array, list->array, allocation_size); + if (list->array != scratch) + free (list->array); + *result = (struct dynarray_finalize_result) + { .array = heap_array, .length = used }; + return true; + } + else + /* The caller will perform the freeing operation. */ + return false; +} +libc_hidden_def (__libc_dynarray_finalize) diff --git a/REORG.TODO/malloc/dynarray_resize.c b/REORG.TODO/malloc/dynarray_resize.c new file mode 100644 index 0000000000..e6dc9fbc68 --- /dev/null +++ b/REORG.TODO/malloc/dynarray_resize.c @@ -0,0 +1,59 @@ +/* Increase the size of a dynamic array. + Copyright (C) 2017 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/>. */ + +#include <dynarray.h> +#include <malloc-internal.h> +#include <stdlib.h> +#include <string.h> + +bool +__libc_dynarray_resize (struct dynarray_header *list, size_t size, + void *scratch, size_t element_size) +{ + /* The existing allocation provides sufficient room. */ + if (size <= list->allocated) + { + list->used = size; + return true; + } + + /* Otherwise, use size as the new allocation size. The caller is + expected to provide the final size of the array, so there is no + over-allocation here. */ + + size_t new_size_bytes; + if (check_mul_overflow_size_t (size, element_size, &new_size_bytes)) + return false; + void *new_array; + if (list->array == scratch) + { + /* The previous array was not heap-allocated. */ + new_array = malloc (new_size_bytes); + if (new_array != NULL && list->array != NULL) + memcpy (new_array, list->array, list->used * element_size); + } + else + new_array = realloc (list->array, new_size_bytes); + if (new_array == NULL) + return false; + list->array = new_array; + list->allocated = size; + list->used = size; + return true; +} +libc_hidden_def (__libc_dynarray_resize) diff --git a/REORG.TODO/malloc/dynarray_resize_clear.c b/REORG.TODO/malloc/dynarray_resize_clear.c new file mode 100644 index 0000000000..0c4ced1d38 --- /dev/null +++ b/REORG.TODO/malloc/dynarray_resize_clear.c @@ -0,0 +1,35 @@ +/* Increase the size of a dynamic array and clear the new part. + Copyright (C) 2017 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/>. */ + +#include <dynarray.h> +#include <stdlib.h> +#include <string.h> + +bool +__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size, + void *scratch, size_t element_size) +{ + size_t old_size = list->used; + if (!__libc_dynarray_resize (list, size, scratch, element_size)) + return false; + /* __libc_dynarray_resize already checked for overflow. */ + memset (list->array + (old_size * element_size), 0, + (size - old_size) * element_size); + return true; +} +libc_hidden_def (__libc_dynarray_resize_clear) diff --git a/REORG.TODO/malloc/hooks.c b/REORG.TODO/malloc/hooks.c new file mode 100644 index 0000000000..1d80be20d2 --- /dev/null +++ b/REORG.TODO/malloc/hooks.c @@ -0,0 +1,585 @@ +/* Malloc implementation for multiple threads without lock contention. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Wolfram Gloger <wg@malloc.de>, 2001. + + 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +/* What to do if the standard debugging hooks are in place and a + corrupt pointer is detected: do nothing (0), print an error message + (1), or call abort() (2). */ + +/* Hooks for debugging versions. The initial hooks just call the + initialization routine, then do the normal work. */ + +static void * +malloc_hook_ini (size_t sz, const void *caller) +{ + __malloc_hook = NULL; + ptmalloc_init (); + return __libc_malloc (sz); +} + +static void * +realloc_hook_ini (void *ptr, size_t sz, const void *caller) +{ + __malloc_hook = NULL; + __realloc_hook = NULL; + ptmalloc_init (); + return __libc_realloc (ptr, sz); +} + +static void * +memalign_hook_ini (size_t alignment, size_t sz, const void *caller) +{ + __memalign_hook = NULL; + ptmalloc_init (); + return __libc_memalign (alignment, sz); +} + +/* Whether we are using malloc checking. */ +static int using_malloc_checking; + +/* A flag that is set by malloc_set_state, to signal that malloc checking + must not be enabled on the request from the user (via the MALLOC_CHECK_ + environment variable). It is reset by __malloc_check_init to tell + malloc_set_state that the user has requested malloc checking. + + The purpose of this flag is to make sure that malloc checking is not + enabled when the heap to be restored was constructed without malloc + checking, and thus does not contain the required magic bytes. + Otherwise the heap would be corrupted by calls to free and realloc. If + it turns out that the heap was created with malloc checking and the + user has requested it malloc_set_state just calls __malloc_check_init + again to enable it. On the other hand, reusing such a heap without + further malloc checking is safe. */ +static int disallow_malloc_check; + +/* Activate a standard set of debugging hooks. */ +void +__malloc_check_init (void) +{ + if (disallow_malloc_check) + { + disallow_malloc_check = 0; + return; + } + using_malloc_checking = 1; + __malloc_hook = malloc_check; + __free_hook = free_check; + __realloc_hook = realloc_check; + __memalign_hook = memalign_check; +} + +/* A simple, standard set of debugging hooks. Overhead is `only' one + byte per chunk; still this will catch most cases of double frees or + overruns. The goal here is to avoid obscure crashes due to invalid + usage, unlike in the MALLOC_DEBUG code. */ + +static unsigned char +magicbyte (const void *p) +{ + unsigned char magic; + + magic = (((uintptr_t) p >> 3) ^ ((uintptr_t) p >> 11)) & 0xFF; + /* Do not return 1. See the comment in mem2mem_check(). */ + if (magic == 1) + ++magic; + return magic; +} + + +/* Visualize the chunk as being partitioned into blocks of 255 bytes from the + highest address of the chunk, downwards. The end of each block tells + us the size of that block, up to the actual size of the requested + memory. Our magic byte is right at the end of the requested size, so we + must reach it with this iteration, otherwise we have witnessed a memory + corruption. */ +static size_t +malloc_check_get_size (mchunkptr p) +{ + size_t size; + unsigned char c; + unsigned char magic = magicbyte (p); + + assert (using_malloc_checking == 1); + + for (size = chunksize (p) - 1 + (chunk_is_mmapped (p) ? 0 : SIZE_SZ); + (c = ((unsigned char *) p)[size]) != magic; + size -= c) + { + if (c <= 0 || size < (c + 2 * SIZE_SZ)) + { + malloc_printerr (check_action, "malloc_check_get_size: memory corruption", + chunk2mem (p), + chunk_is_mmapped (p) ? NULL : arena_for_chunk (p)); + return 0; + } + } + + /* chunk2mem size. */ + return size - 2 * SIZE_SZ; +} + +/* Instrument a chunk with overrun detector byte(s) and convert it + into a user pointer with requested size req_sz. */ + +static void * +internal_function +mem2mem_check (void *ptr, size_t req_sz) +{ + mchunkptr p; + unsigned char *m_ptr = ptr; + size_t max_sz, block_sz, i; + unsigned char magic; + + if (!ptr) + return ptr; + + p = mem2chunk (ptr); + magic = magicbyte (p); + max_sz = chunksize (p) - 2 * SIZE_SZ; + if (!chunk_is_mmapped (p)) + max_sz += SIZE_SZ; + for (i = max_sz - 1; i > req_sz; i -= block_sz) + { + block_sz = MIN (i - req_sz, 0xff); + /* Don't allow the magic byte to appear in the chain of length bytes. + For the following to work, magicbyte cannot return 0x01. */ + if (block_sz == magic) + --block_sz; + + m_ptr[i] = block_sz; + } + m_ptr[req_sz] = magic; + return (void *) m_ptr; +} + +/* Convert a pointer to be free()d or realloc()ed to a valid chunk + pointer. If the provided pointer is not valid, return NULL. */ + +static mchunkptr +internal_function +mem2chunk_check (void *mem, unsigned char **magic_p) +{ + mchunkptr p; + INTERNAL_SIZE_T sz, c; + unsigned char magic; + + if (!aligned_OK (mem)) + return NULL; + + p = mem2chunk (mem); + sz = chunksize (p); + magic = magicbyte (p); + if (!chunk_is_mmapped (p)) + { + /* Must be a chunk in conventional heap memory. */ + int contig = contiguous (&main_arena); + if ((contig && + ((char *) p < mp_.sbrk_base || + ((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) || + sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) || + (!prev_inuse (p) && ((prev_size (p) & MALLOC_ALIGN_MASK) != 0 || + (contig && (char *) prev_chunk (p) < mp_.sbrk_base) || + next_chunk (prev_chunk (p)) != p))) + return NULL; + + for (sz += SIZE_SZ - 1; (c = ((unsigned char *) p)[sz]) != magic; sz -= c) + { + if (c == 0 || sz < (c + 2 * SIZE_SZ)) + return NULL; + } + } + else + { + unsigned long offset, page_mask = GLRO (dl_pagesize) - 1; + + /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two + alignment relative to the beginning of a page. Check this + first. */ + offset = (unsigned long) mem & page_mask; + if ((offset != MALLOC_ALIGNMENT && offset != 0 && offset != 0x10 && + offset != 0x20 && offset != 0x40 && offset != 0x80 && offset != 0x100 && + offset != 0x200 && offset != 0x400 && offset != 0x800 && offset != 0x1000 && + offset < 0x2000) || + !chunk_is_mmapped (p) || prev_inuse (p) || + ((((unsigned long) p - prev_size (p)) & page_mask) != 0) || + ((prev_size (p) + sz) & page_mask) != 0) + return NULL; + + for (sz -= 1; (c = ((unsigned char *) p)[sz]) != magic; sz -= c) + { + if (c == 0 || sz < (c + 2 * SIZE_SZ)) + return NULL; + } + } + ((unsigned char *) p)[sz] ^= 0xFF; + if (magic_p) + *magic_p = (unsigned char *) p + sz; + return p; +} + +/* Check for corruption of the top chunk, and try to recover if + necessary. */ + +static int +internal_function +top_check (void) +{ + mchunkptr t = top (&main_arena); + char *brk, *new_brk; + INTERNAL_SIZE_T front_misalign, sbrk_size; + unsigned long pagesz = GLRO (dl_pagesize); + + if (t == initial_top (&main_arena) || + (!chunk_is_mmapped (t) && + chunksize (t) >= MINSIZE && + prev_inuse (t) && + (!contiguous (&main_arena) || + (char *) t + chunksize (t) == mp_.sbrk_base + main_arena.system_mem))) + return 0; + + malloc_printerr (check_action, "malloc: top chunk is corrupt", t, + &main_arena); + + /* Try to set up a new top chunk. */ + brk = MORECORE (0); + front_misalign = (unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + front_misalign = MALLOC_ALIGNMENT - front_misalign; + sbrk_size = front_misalign + mp_.top_pad + MINSIZE; + sbrk_size += pagesz - ((unsigned long) (brk + sbrk_size) & (pagesz - 1)); + new_brk = (char *) (MORECORE (sbrk_size)); + if (new_brk == (char *) (MORECORE_FAILURE)) + { + __set_errno (ENOMEM); + return -1; + } + /* Call the `morecore' hook if necessary. */ + void (*hook) (void) = atomic_forced_read (__after_morecore_hook); + if (hook) + (*hook)(); + main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size; + + top (&main_arena) = (mchunkptr) (brk + front_misalign); + set_head (top (&main_arena), (sbrk_size - front_misalign) | PREV_INUSE); + + return 0; +} + +static void * +malloc_check (size_t sz, const void *caller) +{ + void *victim; + + if (sz + 1 == 0) + { + __set_errno (ENOMEM); + return NULL; + } + + __libc_lock_lock (main_arena.mutex); + victim = (top_check () >= 0) ? _int_malloc (&main_arena, sz + 1) : NULL; + __libc_lock_unlock (main_arena.mutex); + return mem2mem_check (victim, sz); +} + +static void +free_check (void *mem, const void *caller) +{ + mchunkptr p; + + if (!mem) + return; + + __libc_lock_lock (main_arena.mutex); + p = mem2chunk_check (mem, NULL); + if (!p) + { + __libc_lock_unlock (main_arena.mutex); + + malloc_printerr (check_action, "free(): invalid pointer", mem, + &main_arena); + return; + } + if (chunk_is_mmapped (p)) + { + __libc_lock_unlock (main_arena.mutex); + munmap_chunk (p); + return; + } + _int_free (&main_arena, p, 1); + __libc_lock_unlock (main_arena.mutex); +} + +static void * +realloc_check (void *oldmem, size_t bytes, const void *caller) +{ + INTERNAL_SIZE_T nb; + void *newmem = 0; + unsigned char *magic_p; + + if (bytes + 1 == 0) + { + __set_errno (ENOMEM); + return NULL; + } + if (oldmem == 0) + return malloc_check (bytes, NULL); + + if (bytes == 0) + { + free_check (oldmem, NULL); + return NULL; + } + __libc_lock_lock (main_arena.mutex); + const mchunkptr oldp = mem2chunk_check (oldmem, &magic_p); + __libc_lock_unlock (main_arena.mutex); + if (!oldp) + { + malloc_printerr (check_action, "realloc(): invalid pointer", oldmem, + &main_arena); + return malloc_check (bytes, NULL); + } + const INTERNAL_SIZE_T oldsize = chunksize (oldp); + + checked_request2size (bytes + 1, nb); + __libc_lock_lock (main_arena.mutex); + + if (chunk_is_mmapped (oldp)) + { +#if HAVE_MREMAP + mchunkptr newp = mremap_chunk (oldp, nb); + if (newp) + newmem = chunk2mem (newp); + else +#endif + { + /* Note the extra SIZE_SZ overhead. */ + if (oldsize - SIZE_SZ >= nb) + newmem = oldmem; /* do nothing */ + else + { + /* Must alloc, copy, free. */ + if (top_check () >= 0) + newmem = _int_malloc (&main_arena, bytes + 1); + if (newmem) + { + memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ); + munmap_chunk (oldp); + } + } + } + } + else + { + if (top_check () >= 0) + { + INTERNAL_SIZE_T nb; + checked_request2size (bytes + 1, nb); + newmem = _int_realloc (&main_arena, oldp, oldsize, nb); + } + } + + /* mem2chunk_check changed the magic byte in the old chunk. + If newmem is NULL, then the old chunk will still be used though, + so we need to invert that change here. */ + if (newmem == NULL) + *magic_p ^= 0xFF; + + __libc_lock_unlock (main_arena.mutex); + + return mem2mem_check (newmem, bytes); +} + +static void * +memalign_check (size_t alignment, size_t bytes, const void *caller) +{ + void *mem; + + if (alignment <= MALLOC_ALIGNMENT) + return malloc_check (bytes, NULL); + + if (alignment < MINSIZE) + alignment = MINSIZE; + + /* If the alignment is greater than SIZE_MAX / 2 + 1 it cannot be a + power of 2 and will cause overflow in the check below. */ + if (alignment > SIZE_MAX / 2 + 1) + { + __set_errno (EINVAL); + return 0; + } + + /* Check for overflow. */ + if (bytes > SIZE_MAX - alignment - MINSIZE) + { + __set_errno (ENOMEM); + return 0; + } + + /* Make sure alignment is power of 2. */ + if (!powerof2 (alignment)) + { + size_t a = MALLOC_ALIGNMENT * 2; + while (a < alignment) + a <<= 1; + alignment = a; + } + + __libc_lock_lock (main_arena.mutex); + mem = (top_check () >= 0) ? _int_memalign (&main_arena, alignment, bytes + 1) : + NULL; + __libc_lock_unlock (main_arena.mutex); + return mem2mem_check (mem, bytes); +} + +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25) + +/* Get/set state: malloc_get_state() records the current state of all + malloc variables (_except_ for the actual heap contents and `hook' + function pointers) in a system dependent, opaque data structure. + This data structure is dynamically allocated and can be free()d + after use. malloc_set_state() restores the state of all malloc + variables to the previously obtained state. This is especially + useful when using this malloc as part of a shared library, and when + the heap contents are saved/restored via some other method. The + primary example for this is GNU Emacs with its `dumping' procedure. + `Hook' function pointers are never saved or restored by these + functions, with two exceptions: If malloc checking was in use when + malloc_get_state() was called, then malloc_set_state() calls + __malloc_check_init() if possible; if malloc checking was not in + use in the recorded state but the user requested malloc checking, + then the hooks are reset to 0. */ + +#define MALLOC_STATE_MAGIC 0x444c4541l +#define MALLOC_STATE_VERSION (0 * 0x100l + 5l) /* major*0x100 + minor */ + +struct malloc_save_state +{ + long magic; + long version; + mbinptr av[NBINS * 2 + 2]; + char *sbrk_base; + int sbrked_mem_bytes; + unsigned long trim_threshold; + unsigned long top_pad; + unsigned int n_mmaps_max; + unsigned long mmap_threshold; + int check_action; + unsigned long max_sbrked_mem; + unsigned long max_total_mem; /* Always 0, for backwards compatibility. */ + unsigned int n_mmaps; + unsigned int max_n_mmaps; + unsigned long mmapped_mem; + unsigned long max_mmapped_mem; + int using_malloc_checking; + unsigned long max_fast; + unsigned long arena_test; + unsigned long arena_max; + unsigned long narenas; +}; + +/* Dummy implementation which always fails. We need to provide this + symbol so that existing Emacs binaries continue to work with + BIND_NOW. */ +void * +attribute_compat_text_section +malloc_get_state (void) +{ + __set_errno (ENOSYS); + return NULL; +} +compat_symbol (libc, malloc_get_state, malloc_get_state, GLIBC_2_0); + +int +attribute_compat_text_section +malloc_set_state (void *msptr) +{ + struct malloc_save_state *ms = (struct malloc_save_state *) msptr; + + if (ms->magic != MALLOC_STATE_MAGIC) + return -1; + + /* Must fail if the major version is too high. */ + if ((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) + return -2; + + /* We do not need to perform locking here because __malloc_set_state + must be called before the first call into the malloc subsytem + (usually via __malloc_initialize_hook). pthread_create always + calls calloc and thus must be called only afterwards, so there + cannot be more than one thread when we reach this point. */ + + /* Disable the malloc hooks (and malloc checking). */ + __malloc_hook = NULL; + __realloc_hook = NULL; + __free_hook = NULL; + __memalign_hook = NULL; + using_malloc_checking = 0; + + /* Patch the dumped heap. We no longer try to integrate into the + existing heap. Instead, we mark the existing chunks as mmapped. + Together with the update to dumped_main_arena_start and + dumped_main_arena_end, realloc and free will recognize these + chunks as dumped fake mmapped chunks and never free them. */ + + /* Find the chunk with the lowest address with the heap. */ + mchunkptr chunk = NULL; + { + size_t *candidate = (size_t *) ms->sbrk_base; + size_t *end = (size_t *) (ms->sbrk_base + ms->sbrked_mem_bytes); + while (candidate < end) + if (*candidate != 0) + { + chunk = mem2chunk ((void *) (candidate + 1)); + break; + } + else + ++candidate; + } + if (chunk == NULL) + return 0; + + /* Iterate over the dumped heap and patch the chunks so that they + are treated as fake mmapped chunks. */ + mchunkptr top = ms->av[2]; + while (chunk < top) + { + if (inuse (chunk)) + { + /* Mark chunk as mmapped, to trigger the fallback path. */ + size_t size = chunksize (chunk); + set_head (chunk, size | IS_MMAPPED); + } + chunk = next_chunk (chunk); + } + + /* The dumped fake mmapped chunks all lie in this address range. */ + dumped_main_arena_start = (mchunkptr) ms->sbrk_base; + dumped_main_arena_end = top; + + return 0; +} +compat_symbol (libc, malloc_set_state, malloc_set_state, GLIBC_2_0); + +#endif /* SHLIB_COMPAT */ + +/* + * Local variables: + * c-basic-offset: 2 + * End: + */ diff --git a/REORG.TODO/malloc/malloc-hooks.h b/REORG.TODO/malloc/malloc-hooks.h new file mode 100644 index 0000000000..6b1d192bcd --- /dev/null +++ b/REORG.TODO/malloc/malloc-hooks.h @@ -0,0 +1,24 @@ +/* Internal declarations of malloc hooks no longer in the public API. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _MALLOC_HOOKS_H +#define _MALLOC_HOOKS_H + +void (*__malloc_initialize_hook) (void); + +#endif /* _MALLOC_HOOKS_H */ diff --git a/REORG.TODO/malloc/malloc-internal.h b/REORG.TODO/malloc/malloc-internal.h new file mode 100644 index 0000000000..dbd801a58e --- /dev/null +++ b/REORG.TODO/malloc/malloc-internal.h @@ -0,0 +1,104 @@ +/* Internal declarations for malloc, for use within libc. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _MALLOC_INTERNAL_H +#define _MALLOC_INTERNAL_H + +#include <malloc-machine.h> +#include <malloc-sysdep.h> + +/* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of + chunk sizes. + + The default version is the same as size_t. + + While not strictly necessary, it is best to define this as an + unsigned type, even if size_t is a signed type. This may avoid some + artificial size limitations on some systems. + + On a 64-bit machine, you may be able to reduce malloc overhead by + defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the + expense of not being able to handle more than 2^32 of malloced + space. If this limitation is acceptable, you are encouraged to set + this unless you are on a platform requiring 16byte alignments. In + this case the alignment requirements turn out to negate any + potential advantages of decreasing size_t word size. + + Implementors: Beware of the possible combinations of: + - INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits, + and might be the same width as int or as long + - size_t might have different width and signedness as INTERNAL_SIZE_T + - int and long might be 32 or 64 bits, and might be the same width + + To deal with this, most comparisons and difference computations + among INTERNAL_SIZE_Ts should cast them to unsigned long, being + aware of the fact that casting an unsigned int to a wider long does + not sign-extend. (This also makes checking for negative numbers + awkward.) Some of these casts result in harmless compiler warnings + on some systems. */ +#ifndef INTERNAL_SIZE_T +# define INTERNAL_SIZE_T size_t +#endif + +/* The corresponding word size. */ +#define SIZE_SZ (sizeof (INTERNAL_SIZE_T)) + +/* MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks. It + must be a power of two at least 2 * SIZE_SZ, even on machines for + which smaller alignments would suffice. It may be defined as larger + than this though. Note however that code and data structures are + optimized for the case of 8-byte alignment. */ +#ifndef MALLOC_ALIGNMENT +# define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \ + ? __alignof__ (long double) : 2 * SIZE_SZ) +#endif + +/* The corresponding bit mask value. */ +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) + + +/* Called in the parent process before a fork. */ +void __malloc_fork_lock_parent (void) internal_function attribute_hidden; + +/* Called in the parent process after a fork. */ +void __malloc_fork_unlock_parent (void) internal_function attribute_hidden; + +/* Called in the child process after a fork. */ +void __malloc_fork_unlock_child (void) internal_function attribute_hidden; + +/* Set *RESULT to LEFT * RIGHT. Return true if the multiplication + overflowed. */ +static inline bool +check_mul_overflow_size_t (size_t left, size_t right, size_t *result) +{ +#if __GNUC__ >= 5 + return __builtin_mul_overflow (left, right, result); +#else + /* size_t is unsigned so the behavior on overflow is defined. */ + *result = left * right; + size_t half_size_t = ((size_t) 1) << (8 * sizeof (size_t) / 2); + if (__glibc_unlikely ((left | right) >= half_size_t)) + { + if (__glibc_unlikely (right != 0 && *result / right != left)) + return true; + } + return false; +#endif +} + +#endif /* _MALLOC_INTERNAL_H */ diff --git a/REORG.TODO/malloc/malloc.c b/REORG.TODO/malloc/malloc.c new file mode 100644 index 0000000000..aa45626093 --- /dev/null +++ b/REORG.TODO/malloc/malloc.c @@ -0,0 +1,5321 @@ +/* Malloc implementation for multiple threads without lock contention. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Wolfram Gloger <wg@malloc.de> + and Doug Lea <dl@cs.oswego.edu>, 2001. + + 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +/* + This is a version (aka ptmalloc2) of malloc/free/realloc written by + Doug Lea and adapted to multiple threads/arenas by Wolfram Gloger. + + There have been substantial changes made after the integration into + glibc in all parts of the code. Do not look for much commonality + with the ptmalloc2 version. + +* Version ptmalloc2-20011215 + based on: + VERSION 2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + +* Quickstart + + In order to compile this implementation, a Makefile is provided with + the ptmalloc2 distribution, which has pre-defined targets for some + popular systems (e.g. "make posix" for Posix threads). All that is + typically required with regard to compiler flags is the selection of + the thread package via defining one out of USE_PTHREADS, USE_THR or + USE_SPROC. Check the thread-m.h file for what effects this has. + Many/most systems will additionally require USE_TSD_DATA_HACK to be + defined, so this is the default for "make posix". + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator for malloc-intensive programs. + + The main properties of the algorithms are: + * For large (>= 512 bytes) requests, it is a pure best-fit allocator, + with ties normally decided via FIFO (i.e. least recently used). + * For small (<= 64 bytes by default) requests, it is a caching + allocator, that maintains pools of quickly recycled chunks. + * In between, and for combinations of large and small requests, it does + the best it can trying to meet both goals at once. + * For very large requests (>= 128KB by default), it relies on system + memory mapping facilities, if supported. + + For a longer but slightly out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + + You may already by default be using a C library containing a malloc + that is based on some version of this malloc (for example in + linux). You might still want to use the one in this file in order to + customize settings or to avoid overheads associated with library + versions. + +* Contents, described in more detail in "description of public routines" below. + + Standard (ANSI/SVID/...) functions: + malloc(size_t n); + calloc(size_t n_elements, size_t element_size); + free(void* p); + realloc(void* p, size_t n); + memalign(size_t alignment, size_t n); + valloc(size_t n); + mallinfo() + mallopt(int parameter_number, int parameter_value) + + Additional functions: + independent_calloc(size_t n_elements, size_t size, void* chunks[]); + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + pvalloc(size_t n); + malloc_trim(size_t pad); + malloc_usable_size(void* p); + malloc_stats(); + +* Vital statistics: + + Supported pointer representation: 4 or 8 bytes + Supported size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + You can adjust this by defining INTERNAL_SIZE_T + + Alignment: 2 * sizeof(size_t) (default) + (i.e., 8 byte alignment with 4byte size_t). This suffices for + nearly all current machines and C compilers. However, you can + define MALLOC_ALIGNMENT to be wider than this if necessary. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden word of overhead holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field and 8 (16) bytes for + free list pointers. Thus, the minimum allocatable size is + 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is 2 * + sizeof(size_t) bytes plus the remainder from a system page (the + minimal mmap unit); typically 4096 or 8192 bytes. + + Maximum allocated size: 4-byte size_t: 2^32 minus about two pages + 8-byte size_t: 2^64 minus about two pages + + It is assumed that (possibly signed) size_t values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. The ISO C standard says that it must be + unsigned, but a few systems are known not to adhere to this. + Additionally, even when size_t is unsigned, sbrk (which is by + default used to obtain memory from system) accepts signed + arguments, and may not be able to handle size_t-wide arguments + with negative sign bit. Generally, values that would + appear as negative after accounting for overhead and alignment + are supported only via mmap(), which does not have this + limitation. + + Requests for sizes outside the allowed range will perform an optional + failure action and then return null. (Requests may also + also fail because a system is out of memory.) + + Thread-safety: thread-safe + + Compliance: I believe it is compliant with the 1997 Single Unix Specification + Also SVID/XPG, ANSI C, and probably others as well. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and Linux. + People also report using it in stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. It is not + at all modular. (Sorry!) It uses a lot of macros. To be at all + usable, this code should be compiled using an optimizing compiler + (for example gcc -O3) that can simplify expressions and control + paths. (FAQ: some macros import variables as arguments rather than + declare locals because people reported that some debuggers + otherwise get confused.) + + OPTION DEFAULT VALUE + + Compilation Environment options: + + HAVE_MREMAP 0 + + Changing default word sizes: + + INTERNAL_SIZE_T size_t + + Configuration and functionality options: + + USE_PUBLIC_MALLOC_WRAPPERS NOT defined + USE_MALLOC_LOCK NOT defined + MALLOC_DEBUG NOT defined + REALLOC_ZERO_BYTES_FREES 1 + TRIM_FASTBINS 0 + + Options for customizing MORECORE: + + MORECORE sbrk + MORECORE_FAILURE -1 + MORECORE_CONTIGUOUS 1 + MORECORE_CANNOT_TRIM NOT defined + MORECORE_CLEARS 1 + MMAP_AS_MORECORE_SIZE (1024 * 1024) + + Tuning options that are also dynamically changeable via mallopt: + + DEFAULT_MXFAST 64 (for 32bit), 128 (for 64bit) + DEFAULT_TRIM_THRESHOLD 128 * 1024 + DEFAULT_TOP_PAD 0 + DEFAULT_MMAP_THRESHOLD 128 * 1024 + DEFAULT_MMAP_MAX 65536 + + There are several other #defined constants and macros that you + probably don't want to touch unless you are extending or adapting malloc. */ + +/* + void* is the pointer type that malloc should say it returns +*/ + +#ifndef void +#define void void +#endif /*void*/ + +#include <stddef.h> /* for size_t */ +#include <stdlib.h> /* for getenv(), abort() */ +#include <unistd.h> /* for __libc_enable_secure */ + +#include <atomic.h> +#include <_itoa.h> +#include <bits/wordsize.h> +#include <sys/sysinfo.h> + +#include <ldsodefs.h> + +#include <unistd.h> +#include <stdio.h> /* needed for malloc_stats */ +#include <errno.h> + +#include <shlib-compat.h> + +/* For uintptr_t. */ +#include <stdint.h> + +/* For va_arg, va_start, va_end. */ +#include <stdarg.h> + +/* For MIN, MAX, powerof2. */ +#include <sys/param.h> + +/* For ALIGN_UP et. al. */ +#include <libc-pointer-arith.h> + +#include <malloc/malloc-internal.h> + +/* + Debugging: + + Because freed chunks may be overwritten with bookkeeping fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with -DMALLOC_DEBUG, a number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The checking + is fairly extensive, and will slow down execution + noticeably. Calling malloc_stats or mallinfo with MALLOC_DEBUG set + will attempt to check every non-mmapped allocated and free chunk in + the course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting MALLOC_DEBUG may also be helpful if you are trying to modify + this code. The assertions in the check routines spell out in more + detail the assumptions and invariants underlying the algorithms. + + Setting MALLOC_DEBUG does NOT provide an automated mechanism for + checking that all accesses to malloced memory stay within their + bounds. However, there are several add-ons and adaptations of this + or other mallocs available that do this. +*/ + +#ifndef MALLOC_DEBUG +#define MALLOC_DEBUG 0 +#endif + +#ifdef NDEBUG +# define assert(expr) ((void) 0) +#else +# define assert(expr) \ + ((expr) \ + ? ((void) 0) \ + : __malloc_assert (#expr, __FILE__, __LINE__, __func__)) + +extern const char *__progname; + +static void +__malloc_assert (const char *assertion, const char *file, unsigned int line, + const char *function) +{ + (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n", + __progname, __progname[0] ? ": " : "", + file, line, + function ? function : "", function ? ": " : "", + assertion); + fflush (stderr); + abort (); +} +#endif + + +/* + REALLOC_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + This is required by the C standard. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + +#ifndef REALLOC_ZERO_BYTES_FREES +#define REALLOC_ZERO_BYTES_FREES 1 +#endif + +/* + TRIM_FASTBINS controls whether free() of a very small chunk can + immediately lead to trimming. Setting to true (1) can reduce memory + footprint, but will almost always slow down programs that use a lot + of small chunks. + + Define this only if you are willing to give up some speed to more + aggressively reduce system-level memory footprint when releasing + memory in programs that use many small chunks. You can get + essentially the same effect by setting MXFAST to 0, but this can + lead to even greater slowdowns in programs using many small chunks. + TRIM_FASTBINS is an in-between compile-time option, that disables + only those chunks bordering topmost memory from being placed in + fastbins. +*/ + +#ifndef TRIM_FASTBINS +#define TRIM_FASTBINS 0 +#endif + + +/* Definition for getting more memory from the OS. */ +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +void * __default_morecore (ptrdiff_t); +void *(*__morecore)(ptrdiff_t) = __default_morecore; + + +#include <string.h> + +/* + MORECORE-related declarations. By default, rely on sbrk +*/ + + +/* + MORECORE is the name of the routine to call to obtain more memory + from the system. See below for general guidance on writing + alternative MORECORE functions, as well as a version for WIN32 and a + sample version for pre-OSX macos. +*/ + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +/* + MORECORE_FAILURE is the value returned upon failure of MORECORE + as well as mmap. Since it cannot be an otherwise valid memory address, + and must reflect values of standard sys calls, you probably ought not + try to redefine it. +*/ + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE (-1) +#endif + +/* + If MORECORE_CONTIGUOUS is true, take advantage of fact that + consecutive calls to MORECORE with positive arguments always return + contiguous increasing addresses. This is true of unix sbrk. Even + if not defined, when regions happen to be contiguous, malloc will + permit allocations spanning regions obtained from different + calls. But defining this when applicable enables some stronger + consistency checks and space efficiencies. +*/ + +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif + +/* + Define MORECORE_CANNOT_TRIM if your version of MORECORE + cannot release space back to the system when given negative + arguments. This is generally necessary only if you are using + a hand-crafted MORECORE function that cannot handle negative arguments. +*/ + +/* #define MORECORE_CANNOT_TRIM */ + +/* MORECORE_CLEARS (default 1) + The degree to which the routine mapped to MORECORE zeroes out + memory: never (0), only for newly allocated space (1) or always + (2). The distinction between (1) and (2) is necessary because on + some systems, if the application first decrements and then + increments the break value, the contents of the reallocated space + are unspecified. + */ + +#ifndef MORECORE_CLEARS +# define MORECORE_CLEARS 1 +#endif + + +/* + MMAP_AS_MORECORE_SIZE is the minimum mmap size argument to use if + sbrk fails, and mmap is used as a backup. The value must be a + multiple of page size. This backup strategy generally applies only + when systems have "holes" in address space, so sbrk cannot perform + contiguous expansion, but there is still space available on system. + On systems for which this is known to be useful (i.e. most linux + kernels), this occurs only when programs allocate huge amounts of + memory. Between this, and the fact that mmap regions tend to be + limited, the size should be large, to avoid too many mmap calls and + thus avoid running out of kernel resources. */ + +#ifndef MMAP_AS_MORECORE_SIZE +#define MMAP_AS_MORECORE_SIZE (1024 * 1024) +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. +*/ + +#ifndef HAVE_MREMAP +#define HAVE_MREMAP 0 +#endif + +/* We may need to support __malloc_initialize_hook for backwards + compatibility. */ + +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_24) +# define HAVE_MALLOC_INIT_HOOK 1 +#else +# define HAVE_MALLOC_INIT_HOOK 0 +#endif + + +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any SVID/XPG compliant system that has + a /usr/include/malloc.h defining struct mallinfo. (If you'd like to + install such a thing yourself, cut out the preliminary declarations + as described above and below and save them in a malloc.h file. But + there's no compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields that are not even meaningful in this version of + malloc. These fields are are instead filled by mallinfo() with + other numbers that might be of interest. +*/ + + +/* ---------- description of public routines ------------ */ + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. Additionally, on failure, errno is + set to ENOMEM on ANSI C systems. + + If n is zero, malloc returns a minumum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 24 or 32 bytes on 64bit + systems.) On most systems, size_t is an unsigned type, so calls + with negative arguments are interpreted as requests for huge amounts + of space, which will often fail. The maximum supported value of n + differs across systems, but is in all cases less than the maximum + representable value of a size_t. +*/ +void* __libc_malloc(size_t); +libc_hidden_proto (__libc_malloc) + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. It can have arbitrary (i.e., bad!) + effects if p has already been freed. + + Unless disabled (using mallopt), freeing very large spaces will + when possible, automatically trigger operations that give + back unused memory to the system, thus reducing program footprint. +*/ +void __libc_free(void*); +libc_hidden_proto (__libc_free) + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +void* __libc_calloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p when possible, otherwise it employs the + equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. Unless the #define + REALLOC_ZERO_BYTES_FREES is set, realloc with a size argument of + zero (re)allocates a minimum-sized chunk. + + Large chunks that were internally obtained via mmap will always be + grown using malloc-copy-free sequences unless the system supports + MREMAP (currently only linux). + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ +void* __libc_realloc(void*, size_t); +libc_hidden_proto (__libc_realloc) + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +void* __libc_memalign(size_t, size_t); +libc_hidden_proto (__libc_memalign) + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +void* __libc_valloc(size_t); + + + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. Only one of these (M_MXFAST) is used + in this malloc. The others (M_NLBLKS, M_GRAIN, M_KEEP) don't apply, + so setting them has no effect. But this malloc also supports four + other options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_MXFAST 1 64 0-80 (0 disables fastbins) + M_TRIM_THRESHOLD -1 128*1024 any (-1U disables trimming) + M_TOP_PAD -2 0 any + M_MMAP_THRESHOLD -3 128*1024 any (or 0 if no MMAP support) + M_MMAP_MAX -4 65536 any (0 disables use of mmap) +*/ +int __libc_mallopt(int, int); +libc_hidden_proto (__libc_mallopt) + + +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: the number of fastbin blocks (i.e., small chunks that + have been freed but not use resused or consolidated) + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: always 0 + fsmblks: total bytes held in fastbin blocks + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ +struct mallinfo __libc_mallinfo(void); + + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +void* __libc_pvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + On systems that do not support "negative sbrks", it will always + return 0. +*/ +int __malloc_trim(size_t); + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); + +*/ +size_t __malloc_usable_size(void*); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + +*/ +void __malloc_stats(void); + +/* + malloc_get_state(void); + + Returns the state of all malloc variables in an opaque data + structure. +*/ +void* __malloc_get_state(void); + +/* + malloc_set_state(void* state); + + Restore the state of all malloc variables from data obtained with + malloc_get_state(). +*/ +int __malloc_set_state(void*); + +/* + posix_memalign(void **memptr, size_t alignment, size_t size); + + POSIX wrapper like memalign(), checking for validity of size. +*/ +int __posix_memalign(void **, size_t, size_t); + +/* mallopt tuning options */ + +/* + M_MXFAST is the maximum request size used for "fastbins", special bins + that hold returned chunks without consolidating their spaces. This + enables future requests for chunks of the same size to be handled + very quickly, but can increase fragmentation, and thus increase the + overall memory footprint of a program. + + This malloc manages fastbins very conservatively yet still + efficiently, so fragmentation is rarely a problem for values less + than or equal to the default. The maximum supported value of MXFAST + is 80. You wouldn't want it any higher than this anyway. Fastbins + are designed especially for use with many small structs, objects or + strings -- the default handles structs/objects/arrays with sizes up + to 8 4byte fields, or small strings representing words, tokens, + etc. Using fastbins for larger objects normally worsens + fragmentation without improving speed. + + M_MXFAST is set in REQUEST size units. It is internally used in + chunksize units, which adds padding and alignment. You can reduce + M_MXFAST to 0 to disable all use of fastbins. This causes the malloc + algorithm to be a closer approximation of fifo-best-fit in all cases, + not just for larger requests, but will generally cause it to be + slower. +*/ + + +/* M_MXFAST is a standard SVID/XPG tuning option, usually listed in malloc.h */ +#ifndef M_MXFAST +#define M_MXFAST 1 +#endif + +#ifndef DEFAULT_MXFAST +#define DEFAULT_MXFAST (64 * SIZE_SZ / 4) +#endif + + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing this much memory. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The trim value It must be greater than page size to have any useful + effect. To disable trimming completely, you can set to + (unsigned long)(-1) + + Trim settings interact with fastbin (MXFAST) settings: Unless + TRIM_FASTBINS is defined, automatic trimming never takes place upon + freeing a chunk with size less than or equal to MXFAST. Trimming is + instead delayed until subsequent freeing of larger chunks. However, + you can still force an attempted trim by calling malloc_trim. + + Also, trimming is not generally possible in cases where + the main arena is obtained via mmap. + + Note that the trick some people use of mallocing a huge space and + then freeing it at program startup, in an attempt to reserve system + memory, doesn't have the intended effect under automatic trimming, + since that memory will immediately be returned to the system. +*/ + +#define M_TRIM_THRESHOLD -1 + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. +*/ + +#define M_TOP_PAD -2 + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + MMAP_THRESHOLD_MAX and _MIN are the bounds on the dynamically + adjusted MMAP_THRESHOLD. +*/ + +#ifndef DEFAULT_MMAP_THRESHOLD_MIN +#define DEFAULT_MMAP_THRESHOLD_MIN (128 * 1024) +#endif + +#ifndef DEFAULT_MMAP_THRESHOLD_MAX + /* For 32-bit platforms we cannot increase the maximum mmap + threshold much because it is also the minimum value for the + maximum heap size and its alignment. Going above 512k (i.e., 1M + for new heaps) wastes too much address space. */ +# if __WORDSIZE == 32 +# define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024) +# else +# define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long)) +# endif +#endif + +/* + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefits that: + + 1. Mmapped space can ALWAYS be individually released back + to the system, which helps keep the system level memory + demands of a long-lived program low. + 2. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + means that even trimming via malloc_trim would not release them. + 3. On some systems with "holes" in address spaces, mmap can obtain + memory that sbrk cannot. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + The advantages of mmap nearly always outweigh disadvantages for + "large" chunks, but the value of "large" varies across systems. The + default is an empirically derived value that works well in most + systems. + + + Update in 2006: + The above was written in 2001. Since then the world has changed a lot. + Memory got bigger. Applications got bigger. The virtual address space + layout in 32 bit linux changed. + + In the new situation, brk() and mmap space is shared and there are no + artificial limits on brk size imposed by the kernel. What is more, + applications have started using transient allocations larger than the + 128Kb as was imagined in 2001. + + The price for mmap is also high now; each time glibc mmaps from the + kernel, the kernel is forced to zero out the memory it gives to the + application. Zeroing memory is expensive and eats a lot of cache and + memory bandwidth. This has nothing to do with the efficiency of the + virtual memory system, by doing mmap the kernel just has no choice but + to zero. + + In 2001, the kernel had a maximum size for brk() which was about 800 + megabytes on 32 bit x86, at that point brk() would hit the first + mmaped shared libaries and couldn't expand anymore. With current 2.6 + kernels, the VA space layout is different and brk() and mmap + both can span the entire heap at will. + + Rather than using a static threshold for the brk/mmap tradeoff, + we are now using a simple dynamic one. The goal is still to avoid + fragmentation. The old goals we kept are + 1) try to get the long lived large allocations to use mmap() + 2) really large allocations should always use mmap() + and we're adding now: + 3) transient allocations should use brk() to avoid forcing the kernel + having to zero memory over and over again + + The implementation works with a sliding threshold, which is by default + limited to go between 128Kb and 32Mb (64Mb for 64 bitmachines) and starts + out at 128Kb as per the 2001 default. + + This allows us to satisfy requirement 1) under the assumption that long + lived allocations are made early in the process' lifespan, before it has + started doing dynamic allocations of the same size (which will + increase the threshold). + + The upperbound on the threshold satisfies requirement 2) + + The threshold goes up in value when the application frees memory that was + allocated with the mmap allocator. The idea is that once the application + starts freeing memory of a certain size, it's highly probable that this is + a size the application uses for transient allocations. This estimator + is there to satisfy the new third requirement. + +*/ + +#define M_MMAP_THRESHOLD -3 + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD DEFAULT_MMAP_THRESHOLD_MIN +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because + some systems have a limited number of internal tables for + use by mmap, and using more than a few of them may degrade + performance. + + The default is set to a value that serves only as a safeguard. + Setting to 0 disables use of mmap for servicing large requests. +*/ + +#define M_MMAP_MAX -4 + +#ifndef DEFAULT_MMAP_MAX +#define DEFAULT_MMAP_MAX (65536) +#endif + +#include <malloc.h> + +#ifndef RETURN_ADDRESS +#define RETURN_ADDRESS(X_) (NULL) +#endif + +/* On some platforms we can compile internal, not exported functions better. + Let the environment provide a macro and define it to be empty if it + is not available. */ +#ifndef internal_function +# define internal_function +#endif + +/* Forward declarations. */ +struct malloc_chunk; +typedef struct malloc_chunk* mchunkptr; + +/* Internal routines. */ + +static void* _int_malloc(mstate, size_t); +static void _int_free(mstate, mchunkptr, int); +static void* _int_realloc(mstate, mchunkptr, INTERNAL_SIZE_T, + INTERNAL_SIZE_T); +static void* _int_memalign(mstate, size_t, size_t); +static void* _mid_memalign(size_t, size_t, void *); + +static void malloc_printerr(int action, const char *str, void *ptr, mstate av); + +static void* internal_function mem2mem_check(void *p, size_t sz); +static int internal_function top_check(void); +static void internal_function munmap_chunk(mchunkptr p); +#if HAVE_MREMAP +static mchunkptr internal_function mremap_chunk(mchunkptr p, size_t new_size); +#endif + +static void* malloc_check(size_t sz, const void *caller); +static void free_check(void* mem, const void *caller); +static void* realloc_check(void* oldmem, size_t bytes, + const void *caller); +static void* memalign_check(size_t alignment, size_t bytes, + const void *caller); + +/* ------------------ MMAP support ------------------ */ + + +#include <fcntl.h> +#include <sys/mman.h> + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +#endif + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +#define MMAP(addr, size, prot, flags) \ + __mmap((addr), (size), (prot), (flags)|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0) + + +/* + ----------------------- Chunk representations ----------------------- +*/ + + +/* + This struct declaration is misleading (but accurate and necessary). + It declares a "view" into memory allowing access to necessary + fields at known offsets from a given base. See explanation below. +*/ + +struct malloc_chunk { + + INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */ + + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; + + /* Only used for large blocks: pointer to next larger size. */ + struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ + struct malloc_chunk* bk_nextsize; +}; + + +/* + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if unallocated (P clear) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |A|M|P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_size() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | (size of chunk, but used for application data) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of next chunk, in bytes |A|0|1| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if unallocated (P clear) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |A|0|P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of next chunk, in bytes |A|0|0| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory. If + prev_inuse is set for any given chunk, then you CANNOT determine + the size of the previous chunk, and might even get a memory + addressing fault when trying to do so. + + The A (NON_MAIN_ARENA) bit is cleared for chunks on the initial, + main arena, described by the main_arena variable. When additional + threads are spawned, each thread receives its own arena (up to a + configurable limit, after which arenas are reused for multiple + threads), and the chunks in these arenas have the A bit set. To + find the arena for a chunk on such a non-main arena, heap_for_ptr + performs a bit mask operation and indirection through the ar_ptr + member of the per-heap header heap_info (see arena.c). + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The three exceptions to all this are: + + 1. The special chunk `top' doesn't bother using the + trailing size field since there is no next contiguous chunk + that would have to index off it. After initialization, `top' + is forced to always exist. If it would become less than + MINSIZE bytes long, it is replenished. + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit M (IS_MMAPPED) set in their size fields. Because they are + allocated one-by-one, each must contain its own trailing size + field. If the M bit is set, the other bits are ignored + (because mmapped chunks are neither in an arena, nor adjacent + to a freed chunk). The M bit is also used for chunks which + originally came from a dumped heap via malloc_set_state in + hooks.c. + + 3. Chunks in fastbins are treated as allocated chunks from the + point of view of the chunk allocator. They are consolidated + with their neighbors only in bulk, in malloc_consolidate. +*/ + +/* + ---------- Size and alignment checks and conversions ---------- +*/ + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* The smallest possible chunk */ +#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize)) + +/* The smallest size we can malloc is an aligned minimal chunk */ + +#define MINSIZE \ + (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0) + +#define misaligned_chunk(p) \ + ((uintptr_t)(MALLOC_ALIGNMENT == 2 * SIZE_SZ ? (p) : chunk2mem (p)) \ + & MALLOC_ALIGN_MASK) + + +/* + Check if a request is so large that it would wrap around zero when + padded and aligned. To simplify some other code, the bound is made + low enough so that adding MINSIZE will also not wrap around zero. + */ + +#define REQUEST_OUT_OF_RANGE(req) \ + ((unsigned long) (req) >= \ + (unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE)) + +/* pad request bytes into a usable size -- internal version */ + +#define request2size(req) \ + (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \ + MINSIZE : \ + ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) + +/* Same, except also perform argument check */ + +#define checked_request2size(req, sz) \ + if (REQUEST_OUT_OF_RANGE (req)) { \ + __set_errno (ENOMEM); \ + return 0; \ + } \ + (sz) = request2size (req); + +/* + --------------- Physical chunk operations --------------- + */ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ +#define PREV_INUSE 0x1 + +/* extract inuse bit of previous chunk */ +#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE) + + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ +#define IS_MMAPPED 0x2 + +/* check for mmap()'ed chunk */ +#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED) + + +/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained + from a non-main arena. This is only set immediately before handing + the chunk to the user, if necessary. */ +#define NON_MAIN_ARENA 0x4 + +/* Check for chunk from main arena. */ +#define chunk_main_arena(p) (((p)->mchunk_size & NON_MAIN_ARENA) == 0) + +/* Mark a chunk as not being on the main arena. */ +#define set_non_main_arena(p) ((p)->mchunk_size |= NON_MAIN_ARENA) + + +/* + Bits to mask off when extracting size + + Note: IS_MMAPPED is intentionally not masked off from size field in + macros for which mmapped chunks should never be seen. This should + cause helpful core dumps to occur if it is tried by accident by + people extending or adapting this malloc. + */ +#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) + +/* Get size, ignoring use bits */ +#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS)) + +/* Like chunksize, but do not mask SIZE_BITS. */ +#define chunksize_nomask(p) ((p)->mchunk_size) + +/* Ptr to next physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p))) + +/* Size of the chunk below P. Only valid if prev_inuse (P). */ +#define prev_size(p) ((p)->mchunk_prev_size) + +/* Set the size of the chunk below P. Only valid if prev_inuse (P). */ +#define set_prev_size(p, sz) ((p)->mchunk_prev_size = (sz)) + +/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */ +#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p))) + +/* Treat space at ptr + offset as a chunk */ +#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s))) + +/* extract p's inuse bit */ +#define inuse(p) \ + ((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE) + +/* set/clear chunk as being inuse without otherwise disturbing */ +#define set_inuse(p) \ + ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE + +#define clear_inuse(p) \ + ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE) + + +/* check/set/clear inuse bits in known places */ +#define inuse_bit_at_offset(p, s) \ + (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s) \ + (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s) \ + (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE)) + + +/* Set size at head, without disturbing its use bit */ +#define set_head_size(p, s) ((p)->mchunk_size = (((p)->mchunk_size & SIZE_BITS) | (s))) + +/* Set size/use field */ +#define set_head(p, s) ((p)->mchunk_size = (s)) + +/* Set size at footer (only when chunk is not in use) */ +#define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->mchunk_prev_size = (s)) + + +#pragma GCC poison mchunk_size +#pragma GCC poison mchunk_prev_size + +/* + -------------------- Internal data structures -------------------- + + All internal state is held in an instance of malloc_state defined + below. There are no other static variables, except in two optional + cases: + * If USE_MALLOC_LOCK is defined, the mALLOC_MUTEx declared above. + * If mmap doesn't support MAP_ANONYMOUS, a dummy file descriptor + for mmap. + + Beware of lots of tricks that minimize the total bookkeeping space + requirements. The result is a little over 1K bytes (for 4byte + pointers and size_t.) + */ + +/* + Bins + + An array of bin headers for free chunks. Each bin is doubly + linked. The bins are approximately proportionally (log) spaced. + There are a lot of these bins (128). This may look excessive, but + works very well in practice. Most bins hold sizes that are + unusual as malloc request sizes, but are more usual for fragments + and consolidated sets of chunks, which is what these bins hold, so + they can be found quickly. All procedures maintain the invariant + that no consolidated chunk physically borders another one, so each + chunk in a list is known to be preceeded and followed by either + inuse chunks or the ends of memory. + + Chunks in bins are kept in size order, with ties going to the + approximately least recently used chunk. Ordering isn't needed + for the small bins, which all contain the same-sized chunks, but + facilitates best-fit allocation for larger chunks. These lists + are just sequential. Keeping them in order almost never requires + enough traversal to warrant using fancier ordered data + structures. + + Chunks of the same size are linked with the most + recently freed at the front, and allocations are taken from the + back. This results in LRU (FIFO) allocation order, which tends + to give each chunk an equal opportunity to be consolidated with + adjacent freed chunks, resulting in larger free chunks and less + fragmentation. + + To simplify use in double-linked lists, each bin header acts + as a malloc_chunk. This avoids special-casing for headers. + But to conserve space and improve locality, we allocate + only the fd/bk pointers of bins, and then use repositioning tricks + to treat these as the fields of a malloc_chunk*. + */ + +typedef struct malloc_chunk *mbinptr; + +/* addressing -- note that bin_at(0) does not exist */ +#define bin_at(m, i) \ + (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \ + - offsetof (struct malloc_chunk, fd)) + +/* analog of ++bin */ +#define next_bin(b) ((mbinptr) ((char *) (b) + (sizeof (mchunkptr) << 1))) + +/* Reminders about list directionality within bins */ +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* Take a chunk off a bin list */ +#define unlink(AV, P, BK, FD) { \ + if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ + malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \ + FD = P->fd; \ + BK = P->bk; \ + if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ + malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ + else { \ + FD->bk = BK; \ + BK->fd = FD; \ + if (!in_smallbin_range (chunksize_nomask (P)) \ + && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ + if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ + || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ + malloc_printerr (check_action, \ + "corrupted double-linked list (not small)", \ + P, AV); \ + if (FD->fd_nextsize == NULL) { \ + if (P->fd_nextsize == P) \ + FD->fd_nextsize = FD->bk_nextsize = FD; \ + else { \ + FD->fd_nextsize = P->fd_nextsize; \ + FD->bk_nextsize = P->bk_nextsize; \ + P->fd_nextsize->bk_nextsize = FD; \ + P->bk_nextsize->fd_nextsize = FD; \ + } \ + } else { \ + P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ + P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ + } \ + } \ + } \ +} + +/* + Indexing + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically spaced: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The bins top out around 1MB because we expect to service large + requests via mmap. + + Bin 0 does not exist. Bin 1 is the unordered list; if that would be + a valid chunk size the small bins are bumped up one. + */ + +#define NBINS 128 +#define NSMALLBINS 64 +#define SMALLBIN_WIDTH MALLOC_ALIGNMENT +#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ) +#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH) + +#define in_smallbin_range(sz) \ + ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE) + +#define smallbin_index(sz) \ + ((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\ + + SMALLBIN_CORRECTION) + +#define largebin_index_32(sz) \ + (((((unsigned long) (sz)) >> 6) <= 38) ? 56 + (((unsigned long) (sz)) >> 6) :\ + ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\ + ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\ + ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\ + ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\ + 126) + +#define largebin_index_32_big(sz) \ + (((((unsigned long) (sz)) >> 6) <= 45) ? 49 + (((unsigned long) (sz)) >> 6) :\ + ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\ + ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\ + ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\ + ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\ + 126) + +// XXX It remains to be seen whether it is good to keep the widths of +// XXX the buckets the same or whether it should be scaled by a factor +// XXX of two as well. +#define largebin_index_64(sz) \ + (((((unsigned long) (sz)) >> 6) <= 48) ? 48 + (((unsigned long) (sz)) >> 6) :\ + ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\ + ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\ + ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\ + ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\ + 126) + +#define largebin_index(sz) \ + (SIZE_SZ == 8 ? largebin_index_64 (sz) \ + : MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz) \ + : largebin_index_32 (sz)) + +#define bin_index(sz) \ + ((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz)) + + +/* + Unsorted chunks + + All remainders from chunk splits, as well as all returned chunks, + are first placed in the "unsorted" bin. They are then placed + in regular bins after malloc gives them ONE chance to be used before + binning. So, basically, the unsorted_chunks list acts as a queue, + with chunks being placed on it in free (and malloc_consolidate), + and taken off (to be either used or placed in bins) in malloc. + + The NON_MAIN_ARENA flag is never set for unsorted chunks, so it + does not have to be taken into account in size comparisons. + */ + +/* The otherwise unindexable 1-bin is used to hold unsorted chunks. */ +#define unsorted_chunks(M) (bin_at (M, 1)) + +/* + Top + + The top-most available chunk (i.e., the one bordering the end of + available memory) is treated specially. It is never included in + any bin, is used only if no other chunk is available, and is + released back to the system if it is very large (see + M_TRIM_THRESHOLD). Because top initially + points to its own bin with initial zero size, thus forcing + extension on the first malloc request, we avoid having any special + code in malloc to check whether it even exists yet. But we still + need to do so when getting memory from system, so we make + initial_top treat the bin as a legal but unusable chunk during the + interval between initialization and the first call to + sysmalloc. (This is somewhat delicate, since it relies on + the 2 preceding words to be zero during this interval as well.) + */ + +/* Conveniently, the unsorted bin can be used as dummy top on first call */ +#define initial_top(M) (unsorted_chunks (M)) + +/* + Binmap + + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binmap' is a + bitvector recording whether bins are definitely empty so they can + be skipped over during during traversals. The bits are NOT always + cleared as soon as bins are empty, but instead only + when they are noticed to be empty during traversal in malloc. + */ + +/* Conservatively use 32 bits per map word, even if on 64bit system */ +#define BINMAPSHIFT 5 +#define BITSPERMAP (1U << BINMAPSHIFT) +#define BINMAPSIZE (NBINS / BITSPERMAP) + +#define idx2block(i) ((i) >> BINMAPSHIFT) +#define idx2bit(i) ((1U << ((i) & ((1U << BINMAPSHIFT) - 1)))) + +#define mark_bin(m, i) ((m)->binmap[idx2block (i)] |= idx2bit (i)) +#define unmark_bin(m, i) ((m)->binmap[idx2block (i)] &= ~(idx2bit (i))) +#define get_binmap(m, i) ((m)->binmap[idx2block (i)] & idx2bit (i)) + +/* + Fastbins + + An array of lists holding recently freed small chunks. Fastbins + are not doubly linked. It is faster to single-link them, and + since chunks are never removed from the middles of these lists, + double linking is not necessary. Also, unlike regular bins, they + are not even processed in FIFO order (they use faster LIFO) since + ordering doesn't much matter in the transient contexts in which + fastbins are normally used. + + Chunks in fastbins keep their inuse bit set, so they cannot + be consolidated with other free chunks. malloc_consolidate + releases all chunks in fastbins and consolidates them with + other free chunks. + */ + +typedef struct malloc_chunk *mfastbinptr; +#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx]) + +/* offset 2 to use otherwise unindexable first 2 bins */ +#define fastbin_index(sz) \ + ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2) + + +/* The maximum fastbin request size we support */ +#define MAX_FAST_SIZE (80 * SIZE_SZ / 4) + +#define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1) + +/* + FASTBIN_CONSOLIDATION_THRESHOLD is the size of a chunk in free() + that triggers automatic consolidation of possibly-surrounding + fastbin chunks. This is a heuristic, so the exact value should not + matter too much. It is defined at half the default trim threshold as a + compromise heuristic to only attempt consolidation if it is likely + to lead to trimming. However, it is not dynamically tunable, since + consolidation reduces fragmentation surrounding large chunks even + if trimming is not used. + */ + +#define FASTBIN_CONSOLIDATION_THRESHOLD (65536UL) + +/* + Since the lowest 2 bits in max_fast don't matter in size comparisons, + they are used as flags. + */ + +/* + FASTCHUNKS_BIT held in max_fast indicates that there are probably + some fastbin chunks. It is set true on entering a chunk into any + fastbin, and cleared only in malloc_consolidate. + + The truth value is inverted so that have_fastchunks will be true + upon startup (since statics are zero-filled), simplifying + initialization checks. + */ + +#define FASTCHUNKS_BIT (1U) + +#define have_fastchunks(M) (((M)->flags & FASTCHUNKS_BIT) == 0) +#define clear_fastchunks(M) catomic_or (&(M)->flags, FASTCHUNKS_BIT) +#define set_fastchunks(M) catomic_and (&(M)->flags, ~FASTCHUNKS_BIT) + +/* + NONCONTIGUOUS_BIT indicates that MORECORE does not return contiguous + regions. Otherwise, contiguity is exploited in merging together, + when possible, results from consecutive MORECORE calls. + + The initial value comes from MORECORE_CONTIGUOUS, but is + changed dynamically if mmap is ever used as an sbrk substitute. + */ + +#define NONCONTIGUOUS_BIT (2U) + +#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0) +#define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0) +#define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT) +#define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT) + +/* ARENA_CORRUPTION_BIT is set if a memory corruption was detected on the + arena. Such an arena is no longer used to allocate chunks. Chunks + allocated in that arena before detecting corruption are not freed. */ + +#define ARENA_CORRUPTION_BIT (4U) + +#define arena_is_corrupt(A) (((A)->flags & ARENA_CORRUPTION_BIT)) +#define set_arena_corrupt(A) ((A)->flags |= ARENA_CORRUPTION_BIT) + +/* + Set value of max_fast. + Use impossibly small value if 0. + Precondition: there are no existing fastbin chunks. + Setting the value clears fastchunk bit but preserves noncontiguous bit. + */ + +#define set_max_fast(s) \ + global_max_fast = (((s) == 0) \ + ? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK)) +#define get_max_fast() global_max_fast + + +/* + ----------- Internal state representation and initialization ----------- + */ + +struct malloc_state +{ + /* Serialize access. */ + __libc_lock_define (, mutex); + + /* Flags (formerly in max_fast). */ + int flags; + + /* Fastbins */ + mfastbinptr fastbinsY[NFASTBINS]; + + /* Base of the topmost chunk -- not otherwise kept in a bin */ + mchunkptr top; + + /* The remainder from the most recent split of a small request */ + mchunkptr last_remainder; + + /* Normal bins packed as described above */ + mchunkptr bins[NBINS * 2 - 2]; + + /* Bitmap of bins */ + unsigned int binmap[BINMAPSIZE]; + + /* Linked list */ + struct malloc_state *next; + + /* Linked list for free arenas. Access to this field is serialized + by free_list_lock in arena.c. */ + struct malloc_state *next_free; + + /* Number of threads attached to this arena. 0 if the arena is on + the free list. Access to this field is serialized by + free_list_lock in arena.c. */ + INTERNAL_SIZE_T attached_threads; + + /* Memory allocated from the system in this arena. */ + INTERNAL_SIZE_T system_mem; + INTERNAL_SIZE_T max_system_mem; +}; + +struct malloc_par +{ + /* Tunable parameters */ + unsigned long trim_threshold; + INTERNAL_SIZE_T top_pad; + INTERNAL_SIZE_T mmap_threshold; + INTERNAL_SIZE_T arena_test; + INTERNAL_SIZE_T arena_max; + + /* Memory map support */ + int n_mmaps; + int n_mmaps_max; + int max_n_mmaps; + /* the mmap_threshold is dynamic, until the user sets + it manually, at which point we need to disable any + dynamic behavior. */ + int no_dyn_threshold; + + /* Statistics */ + INTERNAL_SIZE_T mmapped_mem; + INTERNAL_SIZE_T max_mmapped_mem; + + /* First address handed out by MORECORE/sbrk. */ + char *sbrk_base; +}; + +/* There are several instances of this struct ("arenas") in this + malloc. If you are adapting this malloc in a way that does NOT use + a static or mmapped malloc_state, you MUST explicitly zero-fill it + before using. This malloc relies on the property that malloc_state + is initialized to all zeroes (as is true of C statics). */ + +static struct malloc_state main_arena = +{ + .mutex = _LIBC_LOCK_INITIALIZER, + .next = &main_arena, + .attached_threads = 1 +}; + +/* These variables are used for undumping support. Chunked are marked + as using mmap, but we leave them alone if they fall into this + range. NB: The chunk size for these chunks only includes the + initial size field (of SIZE_SZ bytes), there is no trailing size + field (unlike with regular mmapped chunks). */ +static mchunkptr dumped_main_arena_start; /* Inclusive. */ +static mchunkptr dumped_main_arena_end; /* Exclusive. */ + +/* True if the pointer falls into the dumped arena. Use this after + chunk_is_mmapped indicates a chunk is mmapped. */ +#define DUMPED_MAIN_ARENA_CHUNK(p) \ + ((p) >= dumped_main_arena_start && (p) < dumped_main_arena_end) + +/* There is only one instance of the malloc parameters. */ + +static struct malloc_par mp_ = +{ + .top_pad = DEFAULT_TOP_PAD, + .n_mmaps_max = DEFAULT_MMAP_MAX, + .mmap_threshold = DEFAULT_MMAP_THRESHOLD, + .trim_threshold = DEFAULT_TRIM_THRESHOLD, +#define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8)) + .arena_test = NARENAS_FROM_NCORES (1) +}; + +/* Maximum size of memory handled in fastbins. */ +static INTERNAL_SIZE_T global_max_fast; + +/* + Initialize a malloc_state struct. + + This is called only from within malloc_consolidate, which needs + be called in the same contexts anyway. It is never called directly + outside of malloc_consolidate because some optimizing compilers try + to inline it at all call points, which turns out not to be an + optimization at all. (Inlining it in malloc_consolidate is fine though.) + */ + +static void +malloc_init_state (mstate av) +{ + int i; + mbinptr bin; + + /* Establish circular links for normal bins */ + for (i = 1; i < NBINS; ++i) + { + bin = bin_at (av, i); + bin->fd = bin->bk = bin; + } + +#if MORECORE_CONTIGUOUS + if (av != &main_arena) +#endif + set_noncontiguous (av); + if (av == &main_arena) + set_max_fast (DEFAULT_MXFAST); + av->flags |= FASTCHUNKS_BIT; + + av->top = initial_top (av); +} + +/* + Other internal utilities operating on mstates + */ + +static void *sysmalloc (INTERNAL_SIZE_T, mstate); +static int systrim (size_t, mstate); +static void malloc_consolidate (mstate); + + +/* -------------- Early definitions for debugging hooks ---------------- */ + +/* Define and initialize the hook variables. These weak definitions must + appear before any use of the variables in a function (arena.c uses one). */ +#ifndef weak_variable +/* In GNU libc we want the hook variables to be weak definitions to + avoid a problem with Emacs. */ +# define weak_variable weak_function +#endif + +/* Forward declarations. */ +static void *malloc_hook_ini (size_t sz, + const void *caller) __THROW; +static void *realloc_hook_ini (void *ptr, size_t sz, + const void *caller) __THROW; +static void *memalign_hook_ini (size_t alignment, size_t sz, + const void *caller) __THROW; + +#if HAVE_MALLOC_INIT_HOOK +void weak_variable (*__malloc_initialize_hook) (void) = NULL; +compat_symbol (libc, __malloc_initialize_hook, + __malloc_initialize_hook, GLIBC_2_0); +#endif + +void weak_variable (*__free_hook) (void *__ptr, + const void *) = NULL; +void *weak_variable (*__malloc_hook) + (size_t __size, const void *) = malloc_hook_ini; +void *weak_variable (*__realloc_hook) + (void *__ptr, size_t __size, const void *) + = realloc_hook_ini; +void *weak_variable (*__memalign_hook) + (size_t __alignment, size_t __size, const void *) + = memalign_hook_ini; +void weak_variable (*__after_morecore_hook) (void) = NULL; + + +/* ---------------- Error behavior ------------------------------------ */ + +#ifndef DEFAULT_CHECK_ACTION +# define DEFAULT_CHECK_ACTION 3 +#endif + +static int check_action = DEFAULT_CHECK_ACTION; + + +/* ------------------ Testing support ----------------------------------*/ + +static int perturb_byte; + +static void +alloc_perturb (char *p, size_t n) +{ + if (__glibc_unlikely (perturb_byte)) + memset (p, perturb_byte ^ 0xff, n); +} + +static void +free_perturb (char *p, size_t n) +{ + if (__glibc_unlikely (perturb_byte)) + memset (p, perturb_byte, n); +} + + + +#include <stap-probe.h> + +/* ------------------- Support for multiple arenas -------------------- */ +#include "arena.c" + +/* + Debugging support + + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) + */ + +#if !MALLOC_DEBUG + +# define check_chunk(A, P) +# define check_free_chunk(A, P) +# define check_inuse_chunk(A, P) +# define check_remalloced_chunk(A, P, N) +# define check_malloced_chunk(A, P, N) +# define check_malloc_state(A) + +#else + +# define check_chunk(A, P) do_check_chunk (A, P) +# define check_free_chunk(A, P) do_check_free_chunk (A, P) +# define check_inuse_chunk(A, P) do_check_inuse_chunk (A, P) +# define check_remalloced_chunk(A, P, N) do_check_remalloced_chunk (A, P, N) +# define check_malloced_chunk(A, P, N) do_check_malloced_chunk (A, P, N) +# define check_malloc_state(A) do_check_malloc_state (A) + +/* + Properties of all chunks + */ + +static void +do_check_chunk (mstate av, mchunkptr p) +{ + unsigned long sz = chunksize (p); + /* min and max possible addresses assuming contiguous allocation */ + char *max_address = (char *) (av->top) + chunksize (av->top); + char *min_address = max_address - av->system_mem; + + if (!chunk_is_mmapped (p)) + { + /* Has legal address ... */ + if (p != av->top) + { + if (contiguous (av)) + { + assert (((char *) p) >= min_address); + assert (((char *) p + sz) <= ((char *) (av->top))); + } + } + else + { + /* top size is always at least MINSIZE */ + assert ((unsigned long) (sz) >= MINSIZE); + /* top predecessor always marked inuse */ + assert (prev_inuse (p)); + } + } + else if (!DUMPED_MAIN_ARENA_CHUNK (p)) + { + /* address is outside main heap */ + if (contiguous (av) && av->top != initial_top (av)) + { + assert (((char *) p) < min_address || ((char *) p) >= max_address); + } + /* chunk is page-aligned */ + assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0); + /* mem is aligned */ + assert (aligned_OK (chunk2mem (p))); + } +} + +/* + Properties of free chunks + */ + +static void +do_check_free_chunk (mstate av, mchunkptr p) +{ + INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA); + mchunkptr next = chunk_at_offset (p, sz); + + do_check_chunk (av, p); + + /* Chunk must claim to be free ... */ + assert (!inuse (p)); + assert (!chunk_is_mmapped (p)); + + /* Unless a special marker, must have OK fields */ + if ((unsigned long) (sz) >= MINSIZE) + { + assert ((sz & MALLOC_ALIGN_MASK) == 0); + assert (aligned_OK (chunk2mem (p))); + /* ... matching footer field */ + assert (prev_size (p) == sz); + /* ... and is fully consolidated */ + assert (prev_inuse (p)); + assert (next == av->top || inuse (next)); + + /* ... and has minimally sane links */ + assert (p->fd->bk == p); + assert (p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + assert (sz == SIZE_SZ); +} + +/* + Properties of inuse chunks + */ + +static void +do_check_inuse_chunk (mstate av, mchunkptr p) +{ + mchunkptr next; + + do_check_chunk (av, p); + + if (chunk_is_mmapped (p)) + return; /* mmapped chunks have no next/prev */ + + /* Check whether it claims to be in use ... */ + assert (inuse (p)); + + next = next_chunk (p); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse (p)) + { + /* Note that we cannot even look at prev unless it is not inuse */ + mchunkptr prv = prev_chunk (p); + assert (next_chunk (prv) == p); + do_check_free_chunk (av, prv); + } + + if (next == av->top) + { + assert (prev_inuse (next)); + assert (chunksize (next) >= MINSIZE); + } + else if (!inuse (next)) + do_check_free_chunk (av, next); +} + +/* + Properties of chunks recycled from fastbins + */ + +static void +do_check_remalloced_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T s) +{ + INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA); + + if (!chunk_is_mmapped (p)) + { + assert (av == arena_for_chunk (p)); + if (chunk_main_arena (p)) + assert (av == &main_arena); + else + assert (av != &main_arena); + } + + do_check_inuse_chunk (av, p); + + /* Legal size ... */ + assert ((sz & MALLOC_ALIGN_MASK) == 0); + assert ((unsigned long) (sz) >= MINSIZE); + /* ... and alignment */ + assert (aligned_OK (chunk2mem (p))); + /* chunk is less than MINSIZE more than request */ + assert ((long) (sz) - (long) (s) >= 0); + assert ((long) (sz) - (long) (s + MINSIZE) < 0); +} + +/* + Properties of nonrecycled chunks at the point they are malloced + */ + +static void +do_check_malloced_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T s) +{ + /* same as recycled case ... */ + do_check_remalloced_chunk (av, p, s); + + /* + ... plus, must obey implementation invariant that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use + chunk, or the base of its memory arena. This is ensured + by making all allocations from the `lowest' part of any found + chunk. This does not necessarily hold however for chunks + recycled via fastbins. + */ + + assert (prev_inuse (p)); +} + + +/* + Properties of malloc_state. + + This may be useful for debugging malloc, as well as detecting user + programmer errors that somehow write into malloc_state. + + If you are extending or experimenting with this malloc, you can + probably figure out how to hack this routine to print out or + display chunk addresses, sizes, bins, and other instrumentation. + */ + +static void +do_check_malloc_state (mstate av) +{ + int i; + mchunkptr p; + mchunkptr q; + mbinptr b; + unsigned int idx; + INTERNAL_SIZE_T size; + unsigned long total = 0; + int max_fast_bin; + + /* internal size_t must be no wider than pointer type */ + assert (sizeof (INTERNAL_SIZE_T) <= sizeof (char *)); + + /* alignment is a power of 2 */ + assert ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT - 1)) == 0); + + /* cannot run remaining checks until fully initialized */ + if (av->top == 0 || av->top == initial_top (av)) + return; + + /* pagesize is a power of 2 */ + assert (powerof2(GLRO (dl_pagesize))); + + /* A contiguous main_arena is consistent with sbrk_base. */ + if (av == &main_arena && contiguous (av)) + assert ((char *) mp_.sbrk_base + av->system_mem == + (char *) av->top + chunksize (av->top)); + + /* properties of fastbins */ + + /* max_fast is in allowed range */ + assert ((get_max_fast () & ~1) <= request2size (MAX_FAST_SIZE)); + + max_fast_bin = fastbin_index (get_max_fast ()); + + for (i = 0; i < NFASTBINS; ++i) + { + p = fastbin (av, i); + + /* The following test can only be performed for the main arena. + While mallopt calls malloc_consolidate to get rid of all fast + bins (especially those larger than the new maximum) this does + only happen for the main arena. Trying to do this for any + other arena would mean those arenas have to be locked and + malloc_consolidate be called for them. This is excessive. And + even if this is acceptable to somebody it still cannot solve + the problem completely since if the arena is locked a + concurrent malloc call might create a new arena which then + could use the newly invalid fast bins. */ + + /* all bins past max_fast are empty */ + if (av == &main_arena && i > max_fast_bin) + assert (p == 0); + + while (p != 0) + { + /* each chunk claims to be inuse */ + do_check_inuse_chunk (av, p); + total += chunksize (p); + /* chunk belongs in this bin */ + assert (fastbin_index (chunksize (p)) == i); + p = p->fd; + } + } + + if (total != 0) + assert (have_fastchunks (av)); + else if (!have_fastchunks (av)) + assert (total == 0); + + /* check normal bins */ + for (i = 1; i < NBINS; ++i) + { + b = bin_at (av, i); + + /* binmap is accurate (except for bin 1 == unsorted_chunks) */ + if (i >= 2) + { + unsigned int binbit = get_binmap (av, i); + int empty = last (b) == b; + if (!binbit) + assert (empty); + else if (!empty) + assert (binbit); + } + + for (p = last (b); p != b; p = p->bk) + { + /* each chunk claims to be free */ + do_check_free_chunk (av, p); + size = chunksize (p); + total += size; + if (i >= 2) + { + /* chunk belongs in bin */ + idx = bin_index (size); + assert (idx == i); + /* lists are sorted */ + assert (p->bk == b || + (unsigned long) chunksize (p->bk) >= (unsigned long) chunksize (p)); + + if (!in_smallbin_range (size)) + { + if (p->fd_nextsize != NULL) + { + if (p->fd_nextsize == p) + assert (p->bk_nextsize == p); + else + { + if (p->fd_nextsize == first (b)) + assert (chunksize (p) < chunksize (p->fd_nextsize)); + else + assert (chunksize (p) > chunksize (p->fd_nextsize)); + + if (p == first (b)) + assert (chunksize (p) > chunksize (p->bk_nextsize)); + else + assert (chunksize (p) < chunksize (p->bk_nextsize)); + } + } + else + assert (p->bk_nextsize == NULL); + } + } + else if (!in_smallbin_range (size)) + assert (p->fd_nextsize == NULL && p->bk_nextsize == NULL); + /* chunk is followed by a legal chain of inuse chunks */ + for (q = next_chunk (p); + (q != av->top && inuse (q) && + (unsigned long) (chunksize (q)) >= MINSIZE); + q = next_chunk (q)) + do_check_inuse_chunk (av, q); + } + } + + /* top chunk is OK */ + check_chunk (av, av->top); +} +#endif + + +/* ----------------- Support for debugging hooks -------------------- */ +#include "hooks.c" + + +/* ----------- Routines dealing with system allocation -------------- */ + +/* + sysmalloc handles malloc cases requiring more memory from the system. + On entry, it is assumed that av->top does not have enough + space to service request for nb bytes, thus requiring that av->top + be extended or replaced. + */ + +static void * +sysmalloc (INTERNAL_SIZE_T nb, mstate av) +{ + mchunkptr old_top; /* incoming value of av->top */ + INTERNAL_SIZE_T old_size; /* its size */ + char *old_end; /* its end address */ + + long size; /* arg to first MORECORE or mmap call */ + char *brk; /* return value from MORECORE */ + + long correction; /* arg to 2nd MORECORE call */ + char *snd_brk; /* 2nd return val */ + + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of new space */ + INTERNAL_SIZE_T end_misalign; /* partial page left at end of new space */ + char *aligned_brk; /* aligned offset into brk */ + + mchunkptr p; /* the allocated/returned chunk */ + mchunkptr remainder; /* remainder from allocation */ + unsigned long remainder_size; /* its size */ + + + size_t pagesize = GLRO (dl_pagesize); + bool tried_mmap = false; + + + /* + If have mmap, and the request size meets the mmap threshold, and + the system supports mmap, and there are few enough currently + allocated mmapped regions, try to directly map this request + rather than expanding top. + */ + + if (av == NULL + || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold) + && (mp_.n_mmaps < mp_.n_mmaps_max))) + { + char *mm; /* return value from mmap call*/ + + try_mmap: + /* + Round up size to nearest page. For mmapped chunks, the overhead + is one SIZE_SZ unit larger than for normal chunks, because there + is no following chunk whose prev_size field could be used. + + See the front_misalign handling below, for glibc there is no + need for further alignments unless we have have high alignment. + */ + if (MALLOC_ALIGNMENT == 2 * SIZE_SZ) + size = ALIGN_UP (nb + SIZE_SZ, pagesize); + else + size = ALIGN_UP (nb + SIZE_SZ + MALLOC_ALIGN_MASK, pagesize); + tried_mmap = true; + + /* Don't try if size wraps around 0 */ + if ((unsigned long) (size) > (unsigned long) (nb)) + { + mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0)); + + if (mm != MAP_FAILED) + { + /* + The offset to the start of the mmapped region is stored + in the prev_size field of the chunk. This allows us to adjust + returned start address to meet alignment requirements here + and in memalign(), and still be able to compute proper + address argument for later munmap in free() and realloc(). + */ + + if (MALLOC_ALIGNMENT == 2 * SIZE_SZ) + { + /* For glibc, chunk2mem increases the address by 2*SIZE_SZ and + MALLOC_ALIGN_MASK is 2*SIZE_SZ-1. Each mmap'ed area is page + aligned and therefore definitely MALLOC_ALIGN_MASK-aligned. */ + assert (((INTERNAL_SIZE_T) chunk2mem (mm) & MALLOC_ALIGN_MASK) == 0); + front_misalign = 0; + } + else + front_misalign = (INTERNAL_SIZE_T) chunk2mem (mm) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = MALLOC_ALIGNMENT - front_misalign; + p = (mchunkptr) (mm + correction); + set_prev_size (p, correction); + set_head (p, (size - correction) | IS_MMAPPED); + } + else + { + p = (mchunkptr) mm; + set_prev_size (p, 0); + set_head (p, size | IS_MMAPPED); + } + + /* update statistics */ + + int new = atomic_exchange_and_add (&mp_.n_mmaps, 1) + 1; + atomic_max (&mp_.max_n_mmaps, new); + + unsigned long sum; + sum = atomic_exchange_and_add (&mp_.mmapped_mem, size) + size; + atomic_max (&mp_.max_mmapped_mem, sum); + + check_chunk (av, p); + + return chunk2mem (p); + } + } + } + + /* There are no usable arenas and mmap also failed. */ + if (av == NULL) + return 0; + + /* Record incoming configuration of top */ + + old_top = av->top; + old_size = chunksize (old_top); + old_end = (char *) (chunk_at_offset (old_top, old_size)); + + brk = snd_brk = (char *) (MORECORE_FAILURE); + + /* + If not the first time through, we require old_size to be + at least MINSIZE and to have prev_inuse set. + */ + + assert ((old_top == initial_top (av) && old_size == 0) || + ((unsigned long) (old_size) >= MINSIZE && + prev_inuse (old_top) && + ((unsigned long) old_end & (pagesize - 1)) == 0)); + + /* Precondition: not enough current space to satisfy nb request */ + assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE)); + + + if (av != &main_arena) + { + heap_info *old_heap, *heap; + size_t old_heap_size; + + /* First try to extend the current heap. */ + old_heap = heap_for_ptr (old_top); + old_heap_size = old_heap->size; + if ((long) (MINSIZE + nb - old_size) > 0 + && grow_heap (old_heap, MINSIZE + nb - old_size) == 0) + { + av->system_mem += old_heap->size - old_heap_size; + set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top) + | PREV_INUSE); + } + else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad))) + { + /* Use a newly allocated heap. */ + heap->ar_ptr = av; + heap->prev = old_heap; + av->system_mem += heap->size; + /* Set up the new top. */ + top (av) = chunk_at_offset (heap, sizeof (*heap)); + set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE); + + /* Setup fencepost and free the old top chunk with a multiple of + MALLOC_ALIGNMENT in size. */ + /* The fencepost takes at least MINSIZE bytes, because it might + become the top chunk again later. Note that a footer is set + up, too, although the chunk is marked in use. */ + old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK; + set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE); + if (old_size >= MINSIZE) + { + set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE); + set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ)); + set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA); + _int_free (av, old_top, 1); + } + else + { + set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE); + set_foot (old_top, (old_size + 2 * SIZE_SZ)); + } + } + else if (!tried_mmap) + /* We can at least try to use to mmap memory. */ + goto try_mmap; + } + else /* av == main_arena */ + + + { /* Request enough space for nb + pad + overhead */ + size = nb + mp_.top_pad + MINSIZE; + + /* + If contiguous, we can subtract out existing space that we hope to + combine with new space. We add it back later only if + we don't actually get contiguous space. + */ + + if (contiguous (av)) + size -= old_size; + + /* + Round to a multiple of page size. + If MORECORE is not contiguous, this ensures that we only call it + with whole-page arguments. And if MORECORE is contiguous and + this is not first time through, this preserves page-alignment of + previous calls. Otherwise, we correct to page-align below. + */ + + size = ALIGN_UP (size, pagesize); + + /* + Don't try to call MORECORE if argument is so big as to appear + negative. Note that since mmap takes size_t arg, it may succeed + below even if we cannot call MORECORE. + */ + + if (size > 0) + { + brk = (char *) (MORECORE (size)); + LIBC_PROBE (memory_sbrk_more, 2, brk, size); + } + + if (brk != (char *) (MORECORE_FAILURE)) + { + /* Call the `morecore' hook if necessary. */ + void (*hook) (void) = atomic_forced_read (__after_morecore_hook); + if (__builtin_expect (hook != NULL, 0)) + (*hook)(); + } + else + { + /* + If have mmap, try using it as a backup when MORECORE fails or + cannot be used. This is worth doing on systems that have "holes" in + address space, so sbrk cannot extend to give contiguous space, but + space is available elsewhere. Note that we ignore mmap max count + and threshold limits, since the space will not be used as a + segregated mmap region. + */ + + /* Cannot merge with old top, so add its size back in */ + if (contiguous (av)) + size = ALIGN_UP (size + old_size, pagesize); + + /* If we are relying on mmap as backup, then use larger units */ + if ((unsigned long) (size) < (unsigned long) (MMAP_AS_MORECORE_SIZE)) + size = MMAP_AS_MORECORE_SIZE; + + /* Don't try if size wraps around 0 */ + if ((unsigned long) (size) > (unsigned long) (nb)) + { + char *mbrk = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0)); + + if (mbrk != MAP_FAILED) + { + /* We do not need, and cannot use, another sbrk call to find end */ + brk = mbrk; + snd_brk = brk + size; + + /* + Record that we no longer have a contiguous sbrk region. + After the first time mmap is used as backup, we do not + ever rely on contiguous space since this could incorrectly + bridge regions. + */ + set_noncontiguous (av); + } + } + } + + if (brk != (char *) (MORECORE_FAILURE)) + { + if (mp_.sbrk_base == 0) + mp_.sbrk_base = brk; + av->system_mem += size; + + /* + If MORECORE extends previous space, we can likewise extend top size. + */ + + if (brk == old_end && snd_brk == (char *) (MORECORE_FAILURE)) + set_head (old_top, (size + old_size) | PREV_INUSE); + + else if (contiguous (av) && old_size && brk < old_end) + { + /* Oops! Someone else killed our space.. Can't touch anything. */ + malloc_printerr (3, "break adjusted to free malloc space", brk, + av); + } + + /* + Otherwise, make adjustments: + + * If the first time through or noncontiguous, we need to call sbrk + just to find out where the end of memory lies. + + * We need to ensure that all returned chunks from malloc will meet + MALLOC_ALIGNMENT + + * If there was an intervening foreign sbrk, we need to adjust sbrk + request size to account for fact that we will not be able to + combine new space with existing space in old_top. + + * Almost all systems internally allocate whole pages at a time, in + which case we might as well use the whole last page of request. + So we allocate enough more memory to hit a page boundary now, + which in turn causes future contiguous calls to page-align. + */ + + else + { + front_misalign = 0; + end_misalign = 0; + correction = 0; + aligned_brk = brk; + + /* handle contiguous cases */ + if (contiguous (av)) + { + /* Count foreign sbrk as system_mem. */ + if (old_size) + av->system_mem += brk - old_end; + + /* Guarantee alignment of first new chunk made from this space */ + + front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + /* + Skip over some bytes to arrive at an aligned position. + We don't need to specially mark these wasted front bytes. + They will never be accessed anyway because + prev_inuse of av->top (and any chunk created from its start) + is always true after initialization. + */ + + correction = MALLOC_ALIGNMENT - front_misalign; + aligned_brk += correction; + } + + /* + If this isn't adjacent to existing space, then we will not + be able to merge with old_top space, so must add to 2nd request. + */ + + correction += old_size; + + /* Extend the end address to hit a page boundary */ + end_misalign = (INTERNAL_SIZE_T) (brk + size + correction); + correction += (ALIGN_UP (end_misalign, pagesize)) - end_misalign; + + assert (correction >= 0); + snd_brk = (char *) (MORECORE (correction)); + + /* + If can't allocate correction, try to at least find out current + brk. It might be enough to proceed without failing. + + Note that if second sbrk did NOT fail, we assume that space + is contiguous with first sbrk. This is a safe assumption unless + program is multithreaded but doesn't use locks and a foreign sbrk + occurred between our first and second calls. + */ + + if (snd_brk == (char *) (MORECORE_FAILURE)) + { + correction = 0; + snd_brk = (char *) (MORECORE (0)); + } + else + { + /* Call the `morecore' hook if necessary. */ + void (*hook) (void) = atomic_forced_read (__after_morecore_hook); + if (__builtin_expect (hook != NULL, 0)) + (*hook)(); + } + } + + /* handle non-contiguous cases */ + else + { + if (MALLOC_ALIGNMENT == 2 * SIZE_SZ) + /* MORECORE/mmap must correctly align */ + assert (((unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK) == 0); + else + { + front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + /* + Skip over some bytes to arrive at an aligned position. + We don't need to specially mark these wasted front bytes. + They will never be accessed anyway because + prev_inuse of av->top (and any chunk created from its start) + is always true after initialization. + */ + + aligned_brk += MALLOC_ALIGNMENT - front_misalign; + } + } + + /* Find out current end of memory */ + if (snd_brk == (char *) (MORECORE_FAILURE)) + { + snd_brk = (char *) (MORECORE (0)); + } + } + + /* Adjust top based on results of second sbrk */ + if (snd_brk != (char *) (MORECORE_FAILURE)) + { + av->top = (mchunkptr) aligned_brk; + set_head (av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE); + av->system_mem += correction; + + /* + If not the first time through, we either have a + gap due to foreign sbrk or a non-contiguous region. Insert a + double fencepost at old_top to prevent consolidation with space + we don't own. These fenceposts are artificial chunks that are + marked as inuse and are in any case too small to use. We need + two to make sizes and alignments work out. + */ + + if (old_size != 0) + { + /* + Shrink old_top to insert fenceposts, keeping size a + multiple of MALLOC_ALIGNMENT. We know there is at least + enough space in old_top to do this. + */ + old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head (old_top, old_size | PREV_INUSE); + + /* + Note that the following assignments completely overwrite + old_top when old_size was previously MINSIZE. This is + intentional. We need the fencepost, even if old_top otherwise gets + lost. + */ + set_head (chunk_at_offset (old_top, old_size), + (2 * SIZE_SZ) | PREV_INUSE); + set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), + (2 * SIZE_SZ) | PREV_INUSE); + + /* If possible, release the rest. */ + if (old_size >= MINSIZE) + { + _int_free (av, old_top, 1); + } + } + } + } + } + } /* if (av != &main_arena) */ + + if ((unsigned long) av->system_mem > (unsigned long) (av->max_system_mem)) + av->max_system_mem = av->system_mem; + check_malloc_state (av); + + /* finally, do the allocation */ + p = av->top; + size = chunksize (p); + + /* check that one of the above allocation paths succeeded */ + if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) + { + remainder_size = size - nb; + remainder = chunk_at_offset (p, nb); + av->top = remainder; + set_head (p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE); + check_malloced_chunk (av, p, nb); + return chunk2mem (p); + } + + /* catch all failure paths */ + __set_errno (ENOMEM); + return 0; +} + + +/* + systrim is an inverse of sorts to sysmalloc. It gives memory back + to the system (via negative arguments to sbrk) if there is unused + memory at the `high' end of the malloc pool. It is called + automatically by free() when top space exceeds the trim + threshold. It is also called by the public malloc_trim routine. It + returns 1 if it actually released any memory, else 0. + */ + +static int +systrim (size_t pad, mstate av) +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + long released; /* Amount actually released */ + char *current_brk; /* address returned by pre-check sbrk call */ + char *new_brk; /* address returned by post-check sbrk call */ + size_t pagesize; + long top_area; + + pagesize = GLRO (dl_pagesize); + top_size = chunksize (av->top); + + top_area = top_size - MINSIZE - 1; + if (top_area <= pad) + return 0; + + /* Release in pagesize units and round down to the nearest page. */ + extra = ALIGN_DOWN(top_area - pad, pagesize); + + if (extra == 0) + return 0; + + /* + Only proceed if end of memory is where we last set it. + This avoids problems if there were foreign sbrk calls. + */ + current_brk = (char *) (MORECORE (0)); + if (current_brk == (char *) (av->top) + top_size) + { + /* + Attempt to release memory. We ignore MORECORE return value, + and instead call again to find out where new end of memory is. + This avoids problems if first call releases less than we asked, + of if failure somehow altered brk value. (We could still + encounter problems if it altered brk in some very bad way, + but the only thing we can do is adjust anyway, which will cause + some downstream failure.) + */ + + MORECORE (-extra); + /* Call the `morecore' hook if necessary. */ + void (*hook) (void) = atomic_forced_read (__after_morecore_hook); + if (__builtin_expect (hook != NULL, 0)) + (*hook)(); + new_brk = (char *) (MORECORE (0)); + + LIBC_PROBE (memory_sbrk_less, 2, new_brk, extra); + + if (new_brk != (char *) MORECORE_FAILURE) + { + released = (long) (current_brk - new_brk); + + if (released != 0) + { + /* Success. Adjust top. */ + av->system_mem -= released; + set_head (av->top, (top_size - released) | PREV_INUSE); + check_malloc_state (av); + return 1; + } + } + } + return 0; +} + +static void +internal_function +munmap_chunk (mchunkptr p) +{ + INTERNAL_SIZE_T size = chunksize (p); + + assert (chunk_is_mmapped (p)); + + /* Do nothing if the chunk is a faked mmapped chunk in the dumped + main arena. We never free this memory. */ + if (DUMPED_MAIN_ARENA_CHUNK (p)) + return; + + uintptr_t block = (uintptr_t) p - prev_size (p); + size_t total_size = prev_size (p) + size; + /* Unfortunately we have to do the compilers job by hand here. Normally + we would test BLOCK and TOTAL-SIZE separately for compliance with the + page size. But gcc does not recognize the optimization possibility + (in the moment at least) so we combine the two values into one before + the bit test. */ + if (__builtin_expect (((block | total_size) & (GLRO (dl_pagesize) - 1)) != 0, 0)) + { + malloc_printerr (check_action, "munmap_chunk(): invalid pointer", + chunk2mem (p), NULL); + return; + } + + atomic_decrement (&mp_.n_mmaps); + atomic_add (&mp_.mmapped_mem, -total_size); + + /* If munmap failed the process virtual memory address space is in a + bad shape. Just leave the block hanging around, the process will + terminate shortly anyway since not much can be done. */ + __munmap ((char *) block, total_size); +} + +#if HAVE_MREMAP + +static mchunkptr +internal_function +mremap_chunk (mchunkptr p, size_t new_size) +{ + size_t pagesize = GLRO (dl_pagesize); + INTERNAL_SIZE_T offset = prev_size (p); + INTERNAL_SIZE_T size = chunksize (p); + char *cp; + + assert (chunk_is_mmapped (p)); + assert (((size + offset) & (GLRO (dl_pagesize) - 1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = ALIGN_UP (new_size + offset + SIZE_SZ, pagesize); + + /* No need to remap if the number of pages does not change. */ + if (size + offset == new_size) + return p; + + cp = (char *) __mremap ((char *) p - offset, size + offset, new_size, + MREMAP_MAYMOVE); + + if (cp == MAP_FAILED) + return 0; + + p = (mchunkptr) (cp + offset); + + assert (aligned_OK (chunk2mem (p))); + + assert (prev_size (p) == offset); + set_head (p, (new_size - offset) | IS_MMAPPED); + + INTERNAL_SIZE_T new; + new = atomic_exchange_and_add (&mp_.mmapped_mem, new_size - size - offset) + + new_size - size - offset; + atomic_max (&mp_.max_mmapped_mem, new); + return p; +} +#endif /* HAVE_MREMAP */ + +/*------------------------ Public wrappers. --------------------------------*/ + +void * +__libc_malloc (size_t bytes) +{ + mstate ar_ptr; + void *victim; + + void *(*hook) (size_t, const void *) + = atomic_forced_read (__malloc_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook)(bytes, RETURN_ADDRESS (0)); + + arena_get (ar_ptr, bytes); + + victim = _int_malloc (ar_ptr, bytes); + /* Retry with another arena only if we were able to find a usable arena + before. */ + if (!victim && ar_ptr != NULL) + { + LIBC_PROBE (memory_malloc_retry, 1, bytes); + ar_ptr = arena_get_retry (ar_ptr, bytes); + victim = _int_malloc (ar_ptr, bytes); + } + + if (ar_ptr != NULL) + __libc_lock_unlock (ar_ptr->mutex); + + assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || + ar_ptr == arena_for_chunk (mem2chunk (victim))); + return victim; +} +libc_hidden_def (__libc_malloc) + +void +__libc_free (void *mem) +{ + mstate ar_ptr; + mchunkptr p; /* chunk corresponding to mem */ + + void (*hook) (void *, const void *) + = atomic_forced_read (__free_hook); + if (__builtin_expect (hook != NULL, 0)) + { + (*hook)(mem, RETURN_ADDRESS (0)); + return; + } + + if (mem == 0) /* free(0) has no effect */ + return; + + p = mem2chunk (mem); + + if (chunk_is_mmapped (p)) /* release mmapped memory. */ + { + /* See if the dynamic brk/mmap threshold needs adjusting. + Dumped fake mmapped chunks do not affect the threshold. */ + if (!mp_.no_dyn_threshold + && chunksize_nomask (p) > mp_.mmap_threshold + && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX + && !DUMPED_MAIN_ARENA_CHUNK (p)) + { + mp_.mmap_threshold = chunksize (p); + mp_.trim_threshold = 2 * mp_.mmap_threshold; + LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2, + mp_.mmap_threshold, mp_.trim_threshold); + } + munmap_chunk (p); + return; + } + + ar_ptr = arena_for_chunk (p); + _int_free (ar_ptr, p, 0); +} +libc_hidden_def (__libc_free) + +void * +__libc_realloc (void *oldmem, size_t bytes) +{ + mstate ar_ptr; + INTERNAL_SIZE_T nb; /* padded request size */ + + void *newp; /* chunk to return */ + + void *(*hook) (void *, size_t, const void *) = + atomic_forced_read (__realloc_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); + +#if REALLOC_ZERO_BYTES_FREES + if (bytes == 0 && oldmem != NULL) + { + __libc_free (oldmem); return 0; + } +#endif + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == 0) + return __libc_malloc (bytes); + + /* chunk corresponding to oldmem */ + const mchunkptr oldp = mem2chunk (oldmem); + /* its size */ + const INTERNAL_SIZE_T oldsize = chunksize (oldp); + + if (chunk_is_mmapped (oldp)) + ar_ptr = NULL; + else + ar_ptr = arena_for_chunk (oldp); + + /* Little security check which won't hurt performance: the allocator + never wrapps around at the end of the address space. Therefore + we can exclude some size values which might appear here by + accident or by "design" from some intruder. We need to bypass + this check for dumped fake mmap chunks from the old main arena + because the new malloc may provide additional alignment. */ + if ((__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0) + || __builtin_expect (misaligned_chunk (oldp), 0)) + && !DUMPED_MAIN_ARENA_CHUNK (oldp)) + { + malloc_printerr (check_action, "realloc(): invalid pointer", oldmem, + ar_ptr); + return NULL; + } + + checked_request2size (bytes, nb); + + if (chunk_is_mmapped (oldp)) + { + /* If this is a faked mmapped chunk from the dumped main arena, + always make a copy (and do not free the old chunk). */ + if (DUMPED_MAIN_ARENA_CHUNK (oldp)) + { + /* Must alloc, copy, free. */ + void *newmem = __libc_malloc (bytes); + if (newmem == 0) + return NULL; + /* Copy as many bytes as are available from the old chunk + and fit into the new size. NB: The overhead for faked + mmapped chunks is only SIZE_SZ, not 2 * SIZE_SZ as for + regular mmapped chunks. */ + if (bytes > oldsize - SIZE_SZ) + bytes = oldsize - SIZE_SZ; + memcpy (newmem, oldmem, bytes); + return newmem; + } + + void *newmem; + +#if HAVE_MREMAP + newp = mremap_chunk (oldp, nb); + if (newp) + return chunk2mem (newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if (oldsize - SIZE_SZ >= nb) + return oldmem; /* do nothing */ + + /* Must alloc, copy, free. */ + newmem = __libc_malloc (bytes); + if (newmem == 0) + return 0; /* propagate failure */ + + memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ); + munmap_chunk (oldp); + return newmem; + } + + __libc_lock_lock (ar_ptr->mutex); + + newp = _int_realloc (ar_ptr, oldp, oldsize, nb); + + __libc_lock_unlock (ar_ptr->mutex); + assert (!newp || chunk_is_mmapped (mem2chunk (newp)) || + ar_ptr == arena_for_chunk (mem2chunk (newp))); + + if (newp == NULL) + { + /* Try harder to allocate memory in other arenas. */ + LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem); + newp = __libc_malloc (bytes); + if (newp != NULL) + { + memcpy (newp, oldmem, oldsize - SIZE_SZ); + _int_free (ar_ptr, oldp, 0); + } + } + + return newp; +} +libc_hidden_def (__libc_realloc) + +void * +__libc_memalign (size_t alignment, size_t bytes) +{ + void *address = RETURN_ADDRESS (0); + return _mid_memalign (alignment, bytes, address); +} + +static void * +_mid_memalign (size_t alignment, size_t bytes, void *address) +{ + mstate ar_ptr; + void *p; + + void *(*hook) (size_t, size_t, const void *) = + atomic_forced_read (__memalign_hook); + if (__builtin_expect (hook != NULL, 0)) + return (*hook)(alignment, bytes, address); + + /* If we need less alignment than we give anyway, just relay to malloc. */ + if (alignment <= MALLOC_ALIGNMENT) + return __libc_malloc (bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + if (alignment < MINSIZE) + alignment = MINSIZE; + + /* If the alignment is greater than SIZE_MAX / 2 + 1 it cannot be a + power of 2 and will cause overflow in the check below. */ + if (alignment > SIZE_MAX / 2 + 1) + { + __set_errno (EINVAL); + return 0; + } + + /* Check for overflow. */ + if (bytes > SIZE_MAX - alignment - MINSIZE) + { + __set_errno (ENOMEM); + return 0; + } + + + /* Make sure alignment is power of 2. */ + if (!powerof2 (alignment)) + { + size_t a = MALLOC_ALIGNMENT * 2; + while (a < alignment) + a <<= 1; + alignment = a; + } + + arena_get (ar_ptr, bytes + alignment + MINSIZE); + + p = _int_memalign (ar_ptr, alignment, bytes); + if (!p && ar_ptr != NULL) + { + LIBC_PROBE (memory_memalign_retry, 2, bytes, alignment); + ar_ptr = arena_get_retry (ar_ptr, bytes); + p = _int_memalign (ar_ptr, alignment, bytes); + } + + if (ar_ptr != NULL) + __libc_lock_unlock (ar_ptr->mutex); + + assert (!p || chunk_is_mmapped (mem2chunk (p)) || + ar_ptr == arena_for_chunk (mem2chunk (p))); + return p; +} +/* For ISO C11. */ +weak_alias (__libc_memalign, aligned_alloc) +libc_hidden_def (__libc_memalign) + +void * +__libc_valloc (size_t bytes) +{ + if (__malloc_initialized < 0) + ptmalloc_init (); + + void *address = RETURN_ADDRESS (0); + size_t pagesize = GLRO (dl_pagesize); + return _mid_memalign (pagesize, bytes, address); +} + +void * +__libc_pvalloc (size_t bytes) +{ + if (__malloc_initialized < 0) + ptmalloc_init (); + + void *address = RETURN_ADDRESS (0); + size_t pagesize = GLRO (dl_pagesize); + size_t rounded_bytes = ALIGN_UP (bytes, pagesize); + + /* Check for overflow. */ + if (bytes > SIZE_MAX - 2 * pagesize - MINSIZE) + { + __set_errno (ENOMEM); + return 0; + } + + return _mid_memalign (pagesize, rounded_bytes, address); +} + +void * +__libc_calloc (size_t n, size_t elem_size) +{ + mstate av; + mchunkptr oldtop, p; + INTERNAL_SIZE_T bytes, sz, csz, oldtopsize; + void *mem; + unsigned long clearsize; + unsigned long nclears; + INTERNAL_SIZE_T *d; + + /* size_t is unsigned so the behavior on overflow is defined. */ + bytes = n * elem_size; +#define HALF_INTERNAL_SIZE_T \ + (((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T) / 2)) + if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0)) + { + if (elem_size != 0 && bytes / elem_size != n) + { + __set_errno (ENOMEM); + return 0; + } + } + + void *(*hook) (size_t, const void *) = + atomic_forced_read (__malloc_hook); + if (__builtin_expect (hook != NULL, 0)) + { + sz = bytes; + mem = (*hook)(sz, RETURN_ADDRESS (0)); + if (mem == 0) + return 0; + + return memset (mem, 0, sz); + } + + sz = bytes; + + arena_get (av, sz); + if (av) + { + /* Check if we hand out the top chunk, in which case there may be no + need to clear. */ +#if MORECORE_CLEARS + oldtop = top (av); + oldtopsize = chunksize (top (av)); +# if MORECORE_CLEARS < 2 + /* Only newly allocated memory is guaranteed to be cleared. */ + if (av == &main_arena && + oldtopsize < mp_.sbrk_base + av->max_system_mem - (char *) oldtop) + oldtopsize = (mp_.sbrk_base + av->max_system_mem - (char *) oldtop); +# endif + if (av != &main_arena) + { + heap_info *heap = heap_for_ptr (oldtop); + if (oldtopsize < (char *) heap + heap->mprotect_size - (char *) oldtop) + oldtopsize = (char *) heap + heap->mprotect_size - (char *) oldtop; + } +#endif + } + else + { + /* No usable arenas. */ + oldtop = 0; + oldtopsize = 0; + } + mem = _int_malloc (av, sz); + + + assert (!mem || chunk_is_mmapped (mem2chunk (mem)) || + av == arena_for_chunk (mem2chunk (mem))); + + if (mem == 0 && av != NULL) + { + LIBC_PROBE (memory_calloc_retry, 1, sz); + av = arena_get_retry (av, sz); + mem = _int_malloc (av, sz); + } + + if (av != NULL) + __libc_lock_unlock (av->mutex); + + /* Allocation failed even after a retry. */ + if (mem == 0) + return 0; + + p = mem2chunk (mem); + + /* Two optional cases in which clearing not necessary */ + if (chunk_is_mmapped (p)) + { + if (__builtin_expect (perturb_byte, 0)) + return memset (mem, 0, sz); + + return mem; + } + + csz = chunksize (p); + +#if MORECORE_CLEARS + if (perturb_byte == 0 && (p == oldtop && csz > oldtopsize)) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + /* Unroll clear of <= 36 bytes (72 if 8byte sizes). We know that + contents have an odd number of INTERNAL_SIZE_T-sized words; + minimally 3. */ + d = (INTERNAL_SIZE_T *) mem; + clearsize = csz - SIZE_SZ; + nclears = clearsize / sizeof (INTERNAL_SIZE_T); + assert (nclears >= 3); + + if (nclears > 9) + return memset (d, 0, clearsize); + + else + { + *(d + 0) = 0; + *(d + 1) = 0; + *(d + 2) = 0; + if (nclears > 4) + { + *(d + 3) = 0; + *(d + 4) = 0; + if (nclears > 6) + { + *(d + 5) = 0; + *(d + 6) = 0; + if (nclears > 8) + { + *(d + 7) = 0; + *(d + 8) = 0; + } + } + } + } + + return mem; +} + +/* + ------------------------------ malloc ------------------------------ + */ + +static void * +_int_malloc (mstate av, size_t bytes) +{ + INTERNAL_SIZE_T nb; /* normalized request size */ + unsigned int idx; /* associated bin index */ + mbinptr bin; /* associated bin */ + + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T size; /* its size */ + int victim_index; /* its bin index */ + + mchunkptr remainder; /* remainder from a split */ + unsigned long remainder_size; /* its size */ + + unsigned int block; /* bit map traverser */ + unsigned int bit; /* bit map traverser */ + unsigned int map; /* current word of binmap */ + + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + + const char *errstr = NULL; + + /* + Convert request size to internal form by adding SIZE_SZ bytes + overhead plus possibly more to obtain necessary alignment and/or + to obtain a size of at least MINSIZE, the smallest allocatable + size. Also, checked_request2size traps (returning 0) request sizes + that are so large that they wrap around zero when padded and + aligned. + */ + + checked_request2size (bytes, nb); + + /* There are no usable arenas. Fall back to sysmalloc to get a chunk from + mmap. */ + if (__glibc_unlikely (av == NULL)) + { + void *p = sysmalloc (nb, av); + if (p != NULL) + alloc_perturb (p, bytes); + return p; + } + + /* + If the size qualifies as a fastbin, first check corresponding bin. + This code is safe to execute even if av is not yet initialized, so we + can try it without checking, which saves some time on this fast path. + */ + + if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) + { + idx = fastbin_index (nb); + mfastbinptr *fb = &fastbin (av, idx); + mchunkptr pp = *fb; + do + { + victim = pp; + if (victim == NULL) + break; + } + while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) + != victim); + if (victim != 0) + { + if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)) + { + errstr = "malloc(): memory corruption (fast)"; + errout: + malloc_printerr (check_action, errstr, chunk2mem (victim), av); + return NULL; + } + check_remalloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + } + + /* + If a small request, check regular bin. Since these "smallbins" + hold one size each, no searching within bins is necessary. + (For a large request, we need to wait until unsorted chunks are + processed to find best fit. But for small ones, fits are exact + anyway, so we can check now, which is faster.) + */ + + if (in_smallbin_range (nb)) + { + idx = smallbin_index (nb); + bin = bin_at (av, idx); + + if ((victim = last (bin)) != bin) + { + if (victim == 0) /* initialization check */ + malloc_consolidate (av); + else + { + bck = victim->bk; + if (__glibc_unlikely (bck->fd != victim)) + { + errstr = "malloc(): smallbin double linked list corrupted"; + goto errout; + } + set_inuse_bit_at_offset (victim, nb); + bin->bk = bck; + bck->fd = bin; + + if (av != &main_arena) + set_non_main_arena (victim); + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + } + } + + /* + If this is a large request, consolidate fastbins before continuing. + While it might look excessive to kill all fastbins before + even seeing if there is space available, this avoids + fragmentation problems normally associated with fastbins. + Also, in practice, programs tend to have runs of either small or + large requests, but less often mixtures, so consolidation is not + invoked all that often in most programs. And the programs that + it is called frequently in otherwise tend to fragment. + */ + + else + { + idx = largebin_index (nb); + if (have_fastchunks (av)) + malloc_consolidate (av); + } + + /* + Process recently freed or remaindered chunks, taking one only if + it is exact fit, or, if this a small request, the chunk is remainder from + the most recent non-exact fit. Place other traversed chunks in + bins. Note that this step is the only place in any routine where + chunks are placed in bins. + + The outer loop here is needed because we might not realize until + near the end of malloc that we should have consolidated, so must + do so and retry. This happens at most once, and only when we would + otherwise need to expand memory to service a "small" request. + */ + + for (;; ) + { + int iters = 0; + while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) + { + bck = victim->bk; + if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0) + || __builtin_expect (chunksize_nomask (victim) + > av->system_mem, 0)) + malloc_printerr (check_action, "malloc(): memory corruption", + chunk2mem (victim), av); + size = chunksize (victim); + + /* + If a small request, try to use last remainder if it is the + only chunk in unsorted bin. This helps promote locality for + runs of consecutive small requests. This is the only + exception to best-fit, and applies only when there is + no exact fit for a small chunk. + */ + + if (in_smallbin_range (nb) && + bck == unsorted_chunks (av) && + victim == av->last_remainder && + (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) + { + /* split and reattach remainder */ + remainder_size = size - nb; + remainder = chunk_at_offset (victim, nb); + unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder; + av->last_remainder = remainder; + remainder->bk = remainder->fd = unsorted_chunks (av); + if (!in_smallbin_range (remainder_size)) + { + remainder->fd_nextsize = NULL; + remainder->bk_nextsize = NULL; + } + + set_head (victim, nb | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE); + set_foot (remainder, remainder_size); + + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + + /* remove from unsorted list */ + unsorted_chunks (av)->bk = bck; + bck->fd = unsorted_chunks (av); + + /* Take now instead of binning if exact fit */ + + if (size == nb) + { + set_inuse_bit_at_offset (victim, size); + if (av != &main_arena) + set_non_main_arena (victim); + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + + /* place chunk in bin */ + + if (in_smallbin_range (size)) + { + victim_index = smallbin_index (size); + bck = bin_at (av, victim_index); + fwd = bck->fd; + } + else + { + victim_index = largebin_index (size); + bck = bin_at (av, victim_index); + fwd = bck->fd; + + /* maintain large bins in sorted order */ + if (fwd != bck) + { + /* Or with inuse bit to speed comparisons */ + size |= PREV_INUSE; + /* if smaller than smallest, bypass loop below */ + assert (chunk_main_arena (bck->bk)); + if ((unsigned long) (size) + < (unsigned long) chunksize_nomask (bck->bk)) + { + fwd = bck; + bck = bck->bk; + + victim->fd_nextsize = fwd->fd; + victim->bk_nextsize = fwd->fd->bk_nextsize; + fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; + } + else + { + assert (chunk_main_arena (fwd)); + while ((unsigned long) size < chunksize_nomask (fwd)) + { + fwd = fwd->fd_nextsize; + assert (chunk_main_arena (fwd)); + } + + if ((unsigned long) size + == (unsigned long) chunksize_nomask (fwd)) + /* Always insert in the second position. */ + fwd = fwd->fd; + else + { + victim->fd_nextsize = fwd; + victim->bk_nextsize = fwd->bk_nextsize; + fwd->bk_nextsize = victim; + victim->bk_nextsize->fd_nextsize = victim; + } + bck = fwd->bk; + } + } + else + victim->fd_nextsize = victim->bk_nextsize = victim; + } + + mark_bin (av, victim_index); + victim->bk = bck; + victim->fd = fwd; + fwd->bk = victim; + bck->fd = victim; + +#define MAX_ITERS 10000 + if (++iters >= MAX_ITERS) + break; + } + + /* + If a large request, scan through the chunks of current bin in + sorted order to find smallest that fits. Use the skip list for this. + */ + + if (!in_smallbin_range (nb)) + { + bin = bin_at (av, idx); + + /* skip scan if empty or largest chunk is too small */ + if ((victim = first (bin)) != bin + && (unsigned long) chunksize_nomask (victim) + >= (unsigned long) (nb)) + { + victim = victim->bk_nextsize; + while (((unsigned long) (size = chunksize (victim)) < + (unsigned long) (nb))) + victim = victim->bk_nextsize; + + /* Avoid removing the first entry for a size so that the skip + list does not have to be rerouted. */ + if (victim != last (bin) + && chunksize_nomask (victim) + == chunksize_nomask (victim->fd)) + victim = victim->fd; + + remainder_size = size - nb; + unlink (av, victim, bck, fwd); + + /* Exhaust */ + if (remainder_size < MINSIZE) + { + set_inuse_bit_at_offset (victim, size); + if (av != &main_arena) + set_non_main_arena (victim); + } + /* Split */ + else + { + remainder = chunk_at_offset (victim, nb); + /* We cannot assume the unsorted list is empty and therefore + have to perform a complete insert here. */ + bck = unsorted_chunks (av); + fwd = bck->fd; + if (__glibc_unlikely (fwd->bk != bck)) + { + errstr = "malloc(): corrupted unsorted chunks"; + goto errout; + } + remainder->bk = bck; + remainder->fd = fwd; + bck->fd = remainder; + fwd->bk = remainder; + if (!in_smallbin_range (remainder_size)) + { + remainder->fd_nextsize = NULL; + remainder->bk_nextsize = NULL; + } + set_head (victim, nb | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE); + set_foot (remainder, remainder_size); + } + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + } + + /* + Search for a chunk by scanning bins, starting with next largest + bin. This search is strictly by best-fit; i.e., the smallest + (with ties going to approximately the least recently used) chunk + that fits is selected. + + The bitmap avoids needing to check that most blocks are nonempty. + The particular case of skipping all bins during warm-up phases + when no chunks have been returned yet is faster than it might look. + */ + + ++idx; + bin = bin_at (av, idx); + block = idx2block (idx); + map = av->binmap[block]; + bit = idx2bit (idx); + + for (;; ) + { + /* Skip rest of block if there are no more set bits in this block. */ + if (bit > map || bit == 0) + { + do + { + if (++block >= BINMAPSIZE) /* out of bins */ + goto use_top; + } + while ((map = av->binmap[block]) == 0); + + bin = bin_at (av, (block << BINMAPSHIFT)); + bit = 1; + } + + /* Advance to bin with set bit. There must be one. */ + while ((bit & map) == 0) + { + bin = next_bin (bin); + bit <<= 1; + assert (bit != 0); + } + + /* Inspect the bin. It is likely to be non-empty */ + victim = last (bin); + + /* If a false alarm (empty bin), clear the bit. */ + if (victim == bin) + { + av->binmap[block] = map &= ~bit; /* Write through */ + bin = next_bin (bin); + bit <<= 1; + } + + else + { + size = chunksize (victim); + + /* We know the first chunk in this bin is big enough to use. */ + assert ((unsigned long) (size) >= (unsigned long) (nb)); + + remainder_size = size - nb; + + /* unlink */ + unlink (av, victim, bck, fwd); + + /* Exhaust */ + if (remainder_size < MINSIZE) + { + set_inuse_bit_at_offset (victim, size); + if (av != &main_arena) + set_non_main_arena (victim); + } + + /* Split */ + else + { + remainder = chunk_at_offset (victim, nb); + + /* We cannot assume the unsorted list is empty and therefore + have to perform a complete insert here. */ + bck = unsorted_chunks (av); + fwd = bck->fd; + if (__glibc_unlikely (fwd->bk != bck)) + { + errstr = "malloc(): corrupted unsorted chunks 2"; + goto errout; + } + remainder->bk = bck; + remainder->fd = fwd; + bck->fd = remainder; + fwd->bk = remainder; + + /* advertise as last remainder */ + if (in_smallbin_range (nb)) + av->last_remainder = remainder; + if (!in_smallbin_range (remainder_size)) + { + remainder->fd_nextsize = NULL; + remainder->bk_nextsize = NULL; + } + set_head (victim, nb | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE); + set_foot (remainder, remainder_size); + } + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + } + + use_top: + /* + If large enough, split off the chunk bordering the end of memory + (held in av->top). Note that this is in accord with the best-fit + search rule. In effect, av->top is treated as larger (and thus + less well fitting) than any other available chunk since it can + be extended to be as large as necessary (up to system + limitations). + + We require that av->top always exists (i.e., has size >= + MINSIZE) after initialization, so if it would otherwise be + exhausted by current request, it is replenished. (The main + reason for ensuring it exists is that we may need MINSIZE space + to put in fenceposts in sysmalloc.) + */ + + victim = av->top; + size = chunksize (victim); + + if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) + { + remainder_size = size - nb; + remainder = chunk_at_offset (victim, nb); + av->top = remainder; + set_head (victim, nb | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE); + + check_malloced_chunk (av, victim, nb); + void *p = chunk2mem (victim); + alloc_perturb (p, bytes); + return p; + } + + /* When we are using atomic ops to free fast chunks we can get + here for all block sizes. */ + else if (have_fastchunks (av)) + { + malloc_consolidate (av); + /* restore original bin index */ + if (in_smallbin_range (nb)) + idx = smallbin_index (nb); + else + idx = largebin_index (nb); + } + + /* + Otherwise, relay to handle system-dependent cases + */ + else + { + void *p = sysmalloc (nb, av); + if (p != NULL) + alloc_perturb (p, bytes); + return p; + } + } +} + +/* + ------------------------------ free ------------------------------ + */ + +static void +_int_free (mstate av, mchunkptr p, int have_lock) +{ + INTERNAL_SIZE_T size; /* its size */ + mfastbinptr *fb; /* associated fastbin */ + mchunkptr nextchunk; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsize; /* its size */ + int nextinuse; /* true if nextchunk is used */ + INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + + const char *errstr = NULL; + int locked = 0; + + size = chunksize (p); + + /* Little security check which won't hurt performance: the + allocator never wrapps around at the end of the address space. + Therefore we can exclude some size values which might appear + here by accident or by "design" from some intruder. */ + if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0) + || __builtin_expect (misaligned_chunk (p), 0)) + { + errstr = "free(): invalid pointer"; + errout: + if (!have_lock && locked) + __libc_lock_unlock (av->mutex); + malloc_printerr (check_action, errstr, chunk2mem (p), av); + return; + } + /* We know that each chunk is at least MINSIZE bytes in size or a + multiple of MALLOC_ALIGNMENT. */ + if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size))) + { + errstr = "free(): invalid size"; + goto errout; + } + + check_inuse_chunk(av, p); + + /* + If eligible, place chunk on a fastbin so it can be found + and used quickly in malloc. + */ + + if ((unsigned long)(size) <= (unsigned long)(get_max_fast ()) + +#if TRIM_FASTBINS + /* + If TRIM_FASTBINS set, don't place chunks + bordering top into fastbins + */ + && (chunk_at_offset(p, size) != av->top) +#endif + ) { + + if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size)) + <= 2 * SIZE_SZ, 0) + || __builtin_expect (chunksize (chunk_at_offset (p, size)) + >= av->system_mem, 0)) + { + /* We might not have a lock at this point and concurrent modifications + of system_mem might have let to a false positive. Redo the test + after getting the lock. */ + if (have_lock + || ({ assert (locked == 0); + __libc_lock_lock (av->mutex); + locked = 1; + chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ + || chunksize (chunk_at_offset (p, size)) >= av->system_mem; + })) + { + errstr = "free(): invalid next size (fast)"; + goto errout; + } + if (! have_lock) + { + __libc_lock_unlock (av->mutex); + locked = 0; + } + } + + free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); + + set_fastchunks(av); + unsigned int idx = fastbin_index(size); + fb = &fastbin (av, idx); + + /* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */ + mchunkptr old = *fb, old2; + unsigned int old_idx = ~0u; + do + { + /* Check that the top of the bin is not the record we are going to add + (i.e., double free). */ + if (__builtin_expect (old == p, 0)) + { + errstr = "double free or corruption (fasttop)"; + goto errout; + } + /* Check that size of fastbin chunk at the top is the same as + size of the chunk that we are adding. We can dereference OLD + only if we have the lock, otherwise it might have already been + deallocated. See use of OLD_IDX below for the actual check. */ + if (have_lock && old != NULL) + old_idx = fastbin_index(chunksize(old)); + p->fd = old2 = old; + } + while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2); + + if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0)) + { + errstr = "invalid fastbin entry (free)"; + goto errout; + } + } + + /* + Consolidate other non-mmapped chunks as they arrive. + */ + + else if (!chunk_is_mmapped(p)) { + if (! have_lock) { + __libc_lock_lock (av->mutex); + locked = 1; + } + + nextchunk = chunk_at_offset(p, size); + + /* Lightweight tests: check whether the block is already the + top block. */ + if (__glibc_unlikely (p == av->top)) + { + errstr = "double free or corruption (top)"; + goto errout; + } + /* Or whether the next chunk is beyond the boundaries of the arena. */ + if (__builtin_expect (contiguous (av) + && (char *) nextchunk + >= ((char *) av->top + chunksize(av->top)), 0)) + { + errstr = "double free or corruption (out)"; + goto errout; + } + /* Or whether the block is actually not marked used. */ + if (__glibc_unlikely (!prev_inuse(nextchunk))) + { + errstr = "double free or corruption (!prev)"; + goto errout; + } + + nextsize = chunksize(nextchunk); + if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0) + || __builtin_expect (nextsize >= av->system_mem, 0)) + { + errstr = "free(): invalid next size (normal)"; + goto errout; + } + + free_perturb (chunk2mem(p), size - 2 * SIZE_SZ); + + /* consolidate backward */ + if (!prev_inuse(p)) { + prevsize = prev_size (p); + size += prevsize; + p = chunk_at_offset(p, -((long) prevsize)); + unlink(av, p, bck, fwd); + } + + if (nextchunk != av->top) { + /* get and clear inuse bit */ + nextinuse = inuse_bit_at_offset(nextchunk, nextsize); + + /* consolidate forward */ + if (!nextinuse) { + unlink(av, nextchunk, bck, fwd); + size += nextsize; + } else + clear_inuse_bit_at_offset(nextchunk, 0); + + /* + Place the chunk in unsorted chunk list. Chunks are + not placed into regular bins until after they have + been given one chance to be used in malloc. + */ + + bck = unsorted_chunks(av); + fwd = bck->fd; + if (__glibc_unlikely (fwd->bk != bck)) + { + errstr = "free(): corrupted unsorted chunks"; + goto errout; + } + p->fd = fwd; + p->bk = bck; + if (!in_smallbin_range(size)) + { + p->fd_nextsize = NULL; + p->bk_nextsize = NULL; + } + bck->fd = p; + fwd->bk = p; + + set_head(p, size | PREV_INUSE); + set_foot(p, size); + + check_free_chunk(av, p); + } + + /* + If the chunk borders the current high end of memory, + consolidate into top + */ + + else { + size += nextsize; + set_head(p, size | PREV_INUSE); + av->top = p; + check_chunk(av, p); + } + + /* + If freeing a large space, consolidate possibly-surrounding + chunks. Then, if the total unused topmost memory exceeds trim + threshold, ask malloc_trim to reduce top. + + Unless max_fast is 0, we don't know if there are fastbins + bordering top, so we cannot tell for sure whether threshold + has been reached unless fastbins are consolidated. But we + don't want to consolidate on each free. As a compromise, + consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD + is reached. + */ + + if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) { + if (have_fastchunks(av)) + malloc_consolidate(av); + + if (av == &main_arena) { +#ifndef MORECORE_CANNOT_TRIM + if ((unsigned long)(chunksize(av->top)) >= + (unsigned long)(mp_.trim_threshold)) + systrim(mp_.top_pad, av); +#endif + } else { + /* Always try heap_trim(), even if the top chunk is not + large, because the corresponding heap might go away. */ + heap_info *heap = heap_for_ptr(top(av)); + + assert(heap->ar_ptr == av); + heap_trim(heap, mp_.top_pad); + } + } + + if (! have_lock) { + assert (locked); + __libc_lock_unlock (av->mutex); + } + } + /* + If the chunk was allocated via mmap, release via munmap(). + */ + + else { + munmap_chunk (p); + } +} + +/* + ------------------------- malloc_consolidate ------------------------- + + malloc_consolidate is a specialized version of free() that tears + down chunks held in fastbins. Free itself cannot be used for this + purpose since, among other things, it might place chunks back onto + fastbins. So, instead, we need to use a minor variant of the same + code. + + Also, because this routine needs to be called the first time through + malloc anyway, it turns out to be the perfect place to trigger + initialization code. +*/ + +static void malloc_consolidate(mstate av) +{ + mfastbinptr* fb; /* current fastbin being consolidated */ + mfastbinptr* maxfb; /* last fastbin (for loop control) */ + mchunkptr p; /* current chunk being consolidated */ + mchunkptr nextp; /* next chunk to consolidate */ + mchunkptr unsorted_bin; /* bin header */ + mchunkptr first_unsorted; /* chunk to link to */ + + /* These have same use as in free() */ + mchunkptr nextchunk; + INTERNAL_SIZE_T size; + INTERNAL_SIZE_T nextsize; + INTERNAL_SIZE_T prevsize; + int nextinuse; + mchunkptr bck; + mchunkptr fwd; + + /* + If max_fast is 0, we know that av hasn't + yet been initialized, in which case do so below + */ + + if (get_max_fast () != 0) { + clear_fastchunks(av); + + unsorted_bin = unsorted_chunks(av); + + /* + Remove each chunk from fast bin and consolidate it, placing it + then in unsorted bin. Among other reasons for doing this, + placing in unsorted bin avoids needing to calculate actual bins + until malloc is sure that chunks aren't immediately going to be + reused anyway. + */ + + maxfb = &fastbin (av, NFASTBINS - 1); + fb = &fastbin (av, 0); + do { + p = atomic_exchange_acq (fb, NULL); + if (p != 0) { + do { + check_inuse_chunk(av, p); + nextp = p->fd; + + /* Slightly streamlined version of consolidation code in free() */ + size = chunksize (p); + nextchunk = chunk_at_offset(p, size); + nextsize = chunksize(nextchunk); + + if (!prev_inuse(p)) { + prevsize = prev_size (p); + size += prevsize; + p = chunk_at_offset(p, -((long) prevsize)); + unlink(av, p, bck, fwd); + } + + if (nextchunk != av->top) { + nextinuse = inuse_bit_at_offset(nextchunk, nextsize); + + if (!nextinuse) { + size += nextsize; + unlink(av, nextchunk, bck, fwd); + } else + clear_inuse_bit_at_offset(nextchunk, 0); + + first_unsorted = unsorted_bin->fd; + unsorted_bin->fd = p; + first_unsorted->bk = p; + + if (!in_smallbin_range (size)) { + p->fd_nextsize = NULL; + p->bk_nextsize = NULL; + } + + set_head(p, size | PREV_INUSE); + p->bk = unsorted_bin; + p->fd = first_unsorted; + set_foot(p, size); + } + + else { + size += nextsize; + set_head(p, size | PREV_INUSE); + av->top = p; + } + + } while ( (p = nextp) != 0); + + } + } while (fb++ != maxfb); + } + else { + malloc_init_state(av); + check_malloc_state(av); + } +} + +/* + ------------------------------ realloc ------------------------------ +*/ + +void* +_int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize, + INTERNAL_SIZE_T nb) +{ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + void* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + + mchunkptr remainder; /* extra space at end of newp */ + unsigned long remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + + unsigned long copysize; /* bytes to copy */ + unsigned int ncopies; /* INTERNAL_SIZE_T words to copy */ + INTERNAL_SIZE_T* s; /* copy source */ + INTERNAL_SIZE_T* d; /* copy destination */ + + const char *errstr = NULL; + + /* oldmem size */ + if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0) + || __builtin_expect (oldsize >= av->system_mem, 0)) + { + errstr = "realloc(): invalid old size"; + errout: + malloc_printerr (check_action, errstr, chunk2mem (oldp), av); + return NULL; + } + + check_inuse_chunk (av, oldp); + + /* All callers already filter out mmap'ed chunks. */ + assert (!chunk_is_mmapped (oldp)); + + next = chunk_at_offset (oldp, oldsize); + INTERNAL_SIZE_T nextsize = chunksize (next); + if (__builtin_expect (chunksize_nomask (next) <= 2 * SIZE_SZ, 0) + || __builtin_expect (nextsize >= av->system_mem, 0)) + { + errstr = "realloc(): invalid next size"; + goto errout; + } + + if ((unsigned long) (oldsize) >= (unsigned long) (nb)) + { + /* already big enough; split below */ + newp = oldp; + newsize = oldsize; + } + + else + { + /* Try to expand forward into top */ + if (next == av->top && + (unsigned long) (newsize = oldsize + nextsize) >= + (unsigned long) (nb + MINSIZE)) + { + set_head_size (oldp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0)); + av->top = chunk_at_offset (oldp, nb); + set_head (av->top, (newsize - nb) | PREV_INUSE); + check_inuse_chunk (av, oldp); + return chunk2mem (oldp); + } + + /* Try to expand forward into next chunk; split off remainder below */ + else if (next != av->top && + !inuse (next) && + (unsigned long) (newsize = oldsize + nextsize) >= + (unsigned long) (nb)) + { + newp = oldp; + unlink (av, next, bck, fwd); + } + + /* allocate, copy, free */ + else + { + newmem = _int_malloc (av, nb - MALLOC_ALIGN_MASK); + if (newmem == 0) + return 0; /* propagate failure */ + + newp = mem2chunk (newmem); + newsize = chunksize (newp); + + /* + Avoid copy if newp is next chunk after oldp. + */ + if (newp == next) + { + newsize += oldsize; + newp = oldp; + } + else + { + /* + Unroll copy of <= 36 bytes (72 if 8byte sizes) + We know that contents have an odd number of + INTERNAL_SIZE_T-sized words; minimally 3. + */ + + copysize = oldsize - SIZE_SZ; + s = (INTERNAL_SIZE_T *) (chunk2mem (oldp)); + d = (INTERNAL_SIZE_T *) (newmem); + ncopies = copysize / sizeof (INTERNAL_SIZE_T); + assert (ncopies >= 3); + + if (ncopies > 9) + memcpy (d, s, copysize); + + else + { + *(d + 0) = *(s + 0); + *(d + 1) = *(s + 1); + *(d + 2) = *(s + 2); + if (ncopies > 4) + { + *(d + 3) = *(s + 3); + *(d + 4) = *(s + 4); + if (ncopies > 6) + { + *(d + 5) = *(s + 5); + *(d + 6) = *(s + 6); + if (ncopies > 8) + { + *(d + 7) = *(s + 7); + *(d + 8) = *(s + 8); + } + } + } + } + + _int_free (av, oldp, 1); + check_inuse_chunk (av, newp); + return chunk2mem (newp); + } + } + } + + /* If possible, free extra space in old or extended chunk */ + + assert ((unsigned long) (newsize) >= (unsigned long) (nb)); + + remainder_size = newsize - nb; + + if (remainder_size < MINSIZE) /* not enough extra to split off */ + { + set_head_size (newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_inuse_bit_at_offset (newp, newsize); + } + else /* split remainder */ + { + remainder = chunk_at_offset (newp, nb); + set_head_size (newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head (remainder, remainder_size | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + /* Mark remainder as inuse so free() won't complain */ + set_inuse_bit_at_offset (remainder, remainder_size); + _int_free (av, remainder, 1); + } + + check_inuse_chunk (av, newp); + return chunk2mem (newp); +} + +/* + ------------------------------ memalign ------------------------------ + */ + +static void * +_int_memalign (mstate av, size_t alignment, size_t bytes) +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char *m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char *brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space before alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + unsigned long remainder_size; /* its size */ + INTERNAL_SIZE_T size; + + + + checked_request2size (bytes, nb); + + /* + Strategy: find a spot within that chunk that meets the alignment + request, and then possibly free the leading and trailing space. + */ + + + /* Call malloc with worst case padding to hit alignment. */ + + m = (char *) (_int_malloc (av, nb + alignment + MINSIZE)); + + if (m == 0) + return 0; /* propagate failure */ + + p = mem2chunk (m); + + if ((((unsigned long) (m)) % alignment) != 0) /* misaligned */ + + { /* + Find an aligned spot inside chunk. Since we need to give back + leading space in a chunk of at least MINSIZE, if the first + calculation places us at a spot with less than MINSIZE leader, + we can move to the next aligned spot -- we've allocated enough + total room so that this is always possible. + */ + brk = (char *) mem2chunk (((unsigned long) (m + alignment - 1)) & + - ((signed long) alignment)); + if ((unsigned long) (brk - (char *) (p)) < MINSIZE) + brk += alignment; + + newp = (mchunkptr) brk; + leadsize = brk - (char *) (p); + newsize = chunksize (p) - leadsize; + + /* For mmapped chunks, just adjust offset */ + if (chunk_is_mmapped (p)) + { + set_prev_size (newp, prev_size (p) + leadsize); + set_head (newp, newsize | IS_MMAPPED); + return chunk2mem (newp); + } + + /* Otherwise, give back leader, use the rest */ + set_head (newp, newsize | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_inuse_bit_at_offset (newp, newsize); + set_head_size (p, leadsize | (av != &main_arena ? NON_MAIN_ARENA : 0)); + _int_free (av, p, 1); + p = newp; + + assert (newsize >= nb && + (((unsigned long) (chunk2mem (p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + if (!chunk_is_mmapped (p)) + { + size = chunksize (p); + if ((unsigned long) (size) > (unsigned long) (nb + MINSIZE)) + { + remainder_size = size - nb; + remainder = chunk_at_offset (p, nb); + set_head (remainder, remainder_size | PREV_INUSE | + (av != &main_arena ? NON_MAIN_ARENA : 0)); + set_head_size (p, nb); + _int_free (av, remainder, 1); + } + } + + check_inuse_chunk (av, p); + return chunk2mem (p); +} + + +/* + ------------------------------ malloc_trim ------------------------------ + */ + +static int +mtrim (mstate av, size_t pad) +{ + /* Don't touch corrupt arenas. */ + if (arena_is_corrupt (av)) + return 0; + + /* Ensure initialization/consolidation */ + malloc_consolidate (av); + + const size_t ps = GLRO (dl_pagesize); + int psindex = bin_index (ps); + const size_t psm1 = ps - 1; + + int result = 0; + for (int i = 1; i < NBINS; ++i) + if (i == 1 || i >= psindex) + { + mbinptr bin = bin_at (av, i); + + for (mchunkptr p = last (bin); p != bin; p = p->bk) + { + INTERNAL_SIZE_T size = chunksize (p); + + if (size > psm1 + sizeof (struct malloc_chunk)) + { + /* See whether the chunk contains at least one unused page. */ + char *paligned_mem = (char *) (((uintptr_t) p + + sizeof (struct malloc_chunk) + + psm1) & ~psm1); + + assert ((char *) chunk2mem (p) + 4 * SIZE_SZ <= paligned_mem); + assert ((char *) p + size > paligned_mem); + + /* This is the size we could potentially free. */ + size -= paligned_mem - (char *) p; + + if (size > psm1) + { +#if MALLOC_DEBUG + /* When debugging we simulate destroying the memory + content. */ + memset (paligned_mem, 0x89, size & ~psm1); +#endif + __madvise (paligned_mem, size & ~psm1, MADV_DONTNEED); + + result = 1; + } + } + } + } + +#ifndef MORECORE_CANNOT_TRIM + return result | (av == &main_arena ? systrim (pad, av) : 0); + +#else + return result; +#endif +} + + +int +__malloc_trim (size_t s) +{ + int result = 0; + + if (__malloc_initialized < 0) + ptmalloc_init (); + + mstate ar_ptr = &main_arena; + do + { + __libc_lock_lock (ar_ptr->mutex); + result |= mtrim (ar_ptr, s); + __libc_lock_unlock (ar_ptr->mutex); + + ar_ptr = ar_ptr->next; + } + while (ar_ptr != &main_arena); + + return result; +} + + +/* + ------------------------- malloc_usable_size ------------------------- + */ + +static size_t +musable (void *mem) +{ + mchunkptr p; + if (mem != 0) + { + p = mem2chunk (mem); + + if (__builtin_expect (using_malloc_checking == 1, 0)) + return malloc_check_get_size (p); + + if (chunk_is_mmapped (p)) + { + if (DUMPED_MAIN_ARENA_CHUNK (p)) + return chunksize (p) - SIZE_SZ; + else + return chunksize (p) - 2 * SIZE_SZ; + } + else if (inuse (p)) + return chunksize (p) - SIZE_SZ; + } + return 0; +} + + +size_t +__malloc_usable_size (void *m) +{ + size_t result; + + result = musable (m); + return result; +} + +/* + ------------------------------ mallinfo ------------------------------ + Accumulate malloc statistics for arena AV into M. + */ + +static void +int_mallinfo (mstate av, struct mallinfo *m) +{ + size_t i; + mbinptr b; + mchunkptr p; + INTERNAL_SIZE_T avail; + INTERNAL_SIZE_T fastavail; + int nblocks; + int nfastblocks; + + /* Ensure initialization */ + if (av->top == 0) + malloc_consolidate (av); + + check_malloc_state (av); + + /* Account for top */ + avail = chunksize (av->top); + nblocks = 1; /* top always exists */ + + /* traverse fastbins */ + nfastblocks = 0; + fastavail = 0; + + for (i = 0; i < NFASTBINS; ++i) + { + for (p = fastbin (av, i); p != 0; p = p->fd) + { + ++nfastblocks; + fastavail += chunksize (p); + } + } + + avail += fastavail; + + /* traverse regular bins */ + for (i = 1; i < NBINS; ++i) + { + b = bin_at (av, i); + for (p = last (b); p != b; p = p->bk) + { + ++nblocks; + avail += chunksize (p); + } + } + + m->smblks += nfastblocks; + m->ordblks += nblocks; + m->fordblks += avail; + m->uordblks += av->system_mem - avail; + m->arena += av->system_mem; + m->fsmblks += fastavail; + if (av == &main_arena) + { + m->hblks = mp_.n_mmaps; + m->hblkhd = mp_.mmapped_mem; + m->usmblks = 0; + m->keepcost = chunksize (av->top); + } +} + + +struct mallinfo +__libc_mallinfo (void) +{ + struct mallinfo m; + mstate ar_ptr; + + if (__malloc_initialized < 0) + ptmalloc_init (); + + memset (&m, 0, sizeof (m)); + ar_ptr = &main_arena; + do + { + __libc_lock_lock (ar_ptr->mutex); + int_mallinfo (ar_ptr, &m); + __libc_lock_unlock (ar_ptr->mutex); + + ar_ptr = ar_ptr->next; + } + while (ar_ptr != &main_arena); + + return m; +} + +/* + ------------------------------ malloc_stats ------------------------------ + */ + +void +__malloc_stats (void) +{ + int i; + mstate ar_ptr; + unsigned int in_use_b = mp_.mmapped_mem, system_b = in_use_b; + + if (__malloc_initialized < 0) + ptmalloc_init (); + _IO_flockfile (stderr); + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + for (i = 0, ar_ptr = &main_arena;; i++) + { + struct mallinfo mi; + + memset (&mi, 0, sizeof (mi)); + __libc_lock_lock (ar_ptr->mutex); + int_mallinfo (ar_ptr, &mi); + fprintf (stderr, "Arena %d:\n", i); + fprintf (stderr, "system bytes = %10u\n", (unsigned int) mi.arena); + fprintf (stderr, "in use bytes = %10u\n", (unsigned int) mi.uordblks); +#if MALLOC_DEBUG > 1 + if (i > 0) + dump_heap (heap_for_ptr (top (ar_ptr))); +#endif + system_b += mi.arena; + in_use_b += mi.uordblks; + __libc_lock_unlock (ar_ptr->mutex); + ar_ptr = ar_ptr->next; + if (ar_ptr == &main_arena) + break; + } + fprintf (stderr, "Total (incl. mmap):\n"); + fprintf (stderr, "system bytes = %10u\n", system_b); + fprintf (stderr, "in use bytes = %10u\n", in_use_b); + fprintf (stderr, "max mmap regions = %10u\n", (unsigned int) mp_.max_n_mmaps); + fprintf (stderr, "max mmap bytes = %10lu\n", + (unsigned long) mp_.max_mmapped_mem); + ((_IO_FILE *) stderr)->_flags2 |= old_flags2; + _IO_funlockfile (stderr); +} + + +/* + ------------------------------ mallopt ------------------------------ + */ +static inline int +__always_inline +do_set_trim_threshold (size_t value) +{ + LIBC_PROBE (memory_mallopt_trim_threshold, 3, value, mp_.trim_threshold, + mp_.no_dyn_threshold); + mp_.trim_threshold = value; + mp_.no_dyn_threshold = 1; + return 1; +} + +static inline int +__always_inline +do_set_top_pad (size_t value) +{ + LIBC_PROBE (memory_mallopt_top_pad, 3, value, mp_.top_pad, + mp_.no_dyn_threshold); + mp_.top_pad = value; + mp_.no_dyn_threshold = 1; + return 1; +} + +static inline int +__always_inline +do_set_mmap_threshold (size_t value) +{ + /* Forbid setting the threshold too high. */ + if (value <= HEAP_MAX_SIZE / 2) + { + LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value, mp_.mmap_threshold, + mp_.no_dyn_threshold); + mp_.mmap_threshold = value; + mp_.no_dyn_threshold = 1; + return 1; + } + return 0; +} + +static inline int +__always_inline +do_set_mmaps_max (int32_t value) +{ + LIBC_PROBE (memory_mallopt_mmap_max, 3, value, mp_.n_mmaps_max, + mp_.no_dyn_threshold); + mp_.n_mmaps_max = value; + mp_.no_dyn_threshold = 1; + return 1; +} + +static inline int +__always_inline +do_set_mallopt_check (int32_t value) +{ + LIBC_PROBE (memory_mallopt_check_action, 2, value, check_action); + check_action = value; + return 1; +} + +static inline int +__always_inline +do_set_perturb_byte (int32_t value) +{ + LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte); + perturb_byte = value; + return 1; +} + +static inline int +__always_inline +do_set_arena_test (size_t value) +{ + LIBC_PROBE (memory_mallopt_arena_test, 2, value, mp_.arena_test); + mp_.arena_test = value; + return 1; +} + +static inline int +__always_inline +do_set_arena_max (size_t value) +{ + LIBC_PROBE (memory_mallopt_arena_max, 2, value, mp_.arena_max); + mp_.arena_max = value; + return 1; +} + + +int +__libc_mallopt (int param_number, int value) +{ + mstate av = &main_arena; + int res = 1; + + if (__malloc_initialized < 0) + ptmalloc_init (); + __libc_lock_lock (av->mutex); + /* Ensure initialization/consolidation */ + malloc_consolidate (av); + + LIBC_PROBE (memory_mallopt, 2, param_number, value); + + switch (param_number) + { + case M_MXFAST: + if (value >= 0 && value <= MAX_FAST_SIZE) + { + LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ()); + set_max_fast (value); + } + else + res = 0; + break; + + case M_TRIM_THRESHOLD: + do_set_trim_threshold (value); + break; + + case M_TOP_PAD: + do_set_top_pad (value); + break; + + case M_MMAP_THRESHOLD: + res = do_set_mmap_threshold (value); + break; + + case M_MMAP_MAX: + do_set_mmaps_max (value); + break; + + case M_CHECK_ACTION: + do_set_mallopt_check (value); + break; + + case M_PERTURB: + do_set_perturb_byte (value); + break; + + case M_ARENA_TEST: + if (value > 0) + do_set_arena_test (value); + break; + + case M_ARENA_MAX: + if (value > 0) + do_set_arena_max (value); + break; + } + __libc_lock_unlock (av->mutex); + return res; +} +libc_hidden_def (__libc_mallopt) + + +/* + -------------------- Alternative MORECORE functions -------------------- + */ + + +/* + General Requirements for MORECORE. + + The MORECORE function must have the following properties: + + If MORECORE_CONTIGUOUS is false: + + * MORECORE must allocate in multiples of pagesize. It will + only be called with arguments that are multiples of pagesize. + + * MORECORE(0) must return an address that is at least + MALLOC_ALIGNMENT aligned. (Page-aligning always suffices.) + + else (i.e. If MORECORE_CONTIGUOUS is true): + + * Consecutive calls to MORECORE with positive arguments + return increasing addresses, indicating that space has been + contiguously extended. + + * MORECORE need not allocate in multiples of pagesize. + Calls to MORECORE need not have args of multiples of pagesize. + + * MORECORE need not page-align. + + In either case: + + * MORECORE may allocate more memory than requested. (Or even less, + but this will generally result in a malloc failure.) + + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. This malloc does NOT call MORECORE(0) + until at least one call with positive arguments is made, so + the initial value returned is not important. + + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + + * MORECORE need not handle negative arguments -- it may instead + just return MORECORE_FAILURE when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + There is some variation across systems about the type of the + argument to sbrk/MORECORE. If size_t is unsigned, then it cannot + actually be size_t, because sbrk supports negative args, so it is + normally the signed type of the same width as size_t (sometimes + declared as "intptr_t", and sometimes "ptrdiff_t"). It doesn't much + matter though. Internally, we use "long" as arguments, which should + work across all reasonable possibilities. + + Additionally, if MORECORE ever returns failure for a positive + request, then mmap is used as a noncontiguous system allocator. This + is a useful backup strategy for systems with holes in address spaces + -- in this case sbrk cannot contiguously expand the heap, but mmap + may be able to map noncontiguous space. + + If you'd like mmap to ALWAYS be used, you can define MORECORE to be + a function that always returns MORECORE_FAILURE. + + If you are using this malloc with something other than sbrk (or its + emulation) to supply memory regions, you probably want to set + MORECORE_CONTIGUOUS as false. As an example, here is a custom + allocator kindly contributed for pre-OSX macOS. It uses virtually + but not necessarily physically contiguous non-paged memory (locked + in, present and won't get swapped out). You can use it by + uncommenting this section, adding some #includes, and setting up the + appropriate defines above: + + *#define MORECORE osMoreCore + *#define MORECORE_CONTIGUOUS 0 + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + *#define MAX_POOL_ENTRIES 100 + *#define MINIMUM_MORECORE_SIZE (64 * 1024) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MORECORE_FAILURE; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((unsigned long) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MORECORE_FAILURE; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + * ptr = 0; + } + } + + */ + + +/* Helper code. */ + +extern char **__libc_argv attribute_hidden; + +static void +malloc_printerr (int action, const char *str, void *ptr, mstate ar_ptr) +{ + /* Avoid using this arena in future. We do not attempt to synchronize this + with anything else because we minimally want to ensure that __libc_message + gets its resources safely without stumbling on the current corruption. */ + if (ar_ptr) + set_arena_corrupt (ar_ptr); + + if ((action & 5) == 5) + __libc_message (action & 2, "%s\n", str); + else if (action & 1) + { + char buf[2 * sizeof (uintptr_t) + 1]; + + buf[sizeof (buf) - 1] = '\0'; + char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0); + while (cp > buf) + *--cp = '0'; + + __libc_message (action & 2, "*** Error in `%s': %s: 0x%s ***\n", + __libc_argv[0] ? : "<unknown>", str, cp); + } + else if (action & 2) + abort (); +} + +/* We need a wrapper function for one of the additions of POSIX. */ +int +__posix_memalign (void **memptr, size_t alignment, size_t size) +{ + void *mem; + + /* Test whether the SIZE argument is valid. It must be a power of + two multiple of sizeof (void *). */ + if (alignment % sizeof (void *) != 0 + || !powerof2 (alignment / sizeof (void *)) + || alignment == 0) + return EINVAL; + + + void *address = RETURN_ADDRESS (0); + mem = _mid_memalign (alignment, size, address); + + if (mem != NULL) + { + *memptr = mem; + return 0; + } + + return ENOMEM; +} +weak_alias (__posix_memalign, posix_memalign) + + +int +__malloc_info (int options, FILE *fp) +{ + /* For now, at least. */ + if (options != 0) + return EINVAL; + + int n = 0; + size_t total_nblocks = 0; + size_t total_nfastblocks = 0; + size_t total_avail = 0; + size_t total_fastavail = 0; + size_t total_system = 0; + size_t total_max_system = 0; + size_t total_aspace = 0; + size_t total_aspace_mprotect = 0; + + + + if (__malloc_initialized < 0) + ptmalloc_init (); + + fputs ("<malloc version=\"1\">\n", fp); + + /* Iterate over all arenas currently in use. */ + mstate ar_ptr = &main_arena; + do + { + fprintf (fp, "<heap nr=\"%d\">\n<sizes>\n", n++); + + size_t nblocks = 0; + size_t nfastblocks = 0; + size_t avail = 0; + size_t fastavail = 0; + struct + { + size_t from; + size_t to; + size_t total; + size_t count; + } sizes[NFASTBINS + NBINS - 1]; +#define nsizes (sizeof (sizes) / sizeof (sizes[0])) + + __libc_lock_lock (ar_ptr->mutex); + + for (size_t i = 0; i < NFASTBINS; ++i) + { + mchunkptr p = fastbin (ar_ptr, i); + if (p != NULL) + { + size_t nthissize = 0; + size_t thissize = chunksize (p); + + while (p != NULL) + { + ++nthissize; + p = p->fd; + } + + fastavail += nthissize * thissize; + nfastblocks += nthissize; + sizes[i].from = thissize - (MALLOC_ALIGNMENT - 1); + sizes[i].to = thissize; + sizes[i].count = nthissize; + } + else + sizes[i].from = sizes[i].to = sizes[i].count = 0; + + sizes[i].total = sizes[i].count * sizes[i].to; + } + + + mbinptr bin; + struct malloc_chunk *r; + + for (size_t i = 1; i < NBINS; ++i) + { + bin = bin_at (ar_ptr, i); + r = bin->fd; + sizes[NFASTBINS - 1 + i].from = ~((size_t) 0); + sizes[NFASTBINS - 1 + i].to = sizes[NFASTBINS - 1 + i].total + = sizes[NFASTBINS - 1 + i].count = 0; + + if (r != NULL) + while (r != bin) + { + size_t r_size = chunksize_nomask (r); + ++sizes[NFASTBINS - 1 + i].count; + sizes[NFASTBINS - 1 + i].total += r_size; + sizes[NFASTBINS - 1 + i].from + = MIN (sizes[NFASTBINS - 1 + i].from, r_size); + sizes[NFASTBINS - 1 + i].to = MAX (sizes[NFASTBINS - 1 + i].to, + r_size); + + r = r->fd; + } + + if (sizes[NFASTBINS - 1 + i].count == 0) + sizes[NFASTBINS - 1 + i].from = 0; + nblocks += sizes[NFASTBINS - 1 + i].count; + avail += sizes[NFASTBINS - 1 + i].total; + } + + __libc_lock_unlock (ar_ptr->mutex); + + total_nfastblocks += nfastblocks; + total_fastavail += fastavail; + + total_nblocks += nblocks; + total_avail += avail; + + for (size_t i = 0; i < nsizes; ++i) + if (sizes[i].count != 0 && i != NFASTBINS) + fprintf (fp, " \ + <size from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n", + sizes[i].from, sizes[i].to, sizes[i].total, sizes[i].count); + + if (sizes[NFASTBINS].count != 0) + fprintf (fp, "\ + <unsorted from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n", + sizes[NFASTBINS].from, sizes[NFASTBINS].to, + sizes[NFASTBINS].total, sizes[NFASTBINS].count); + + total_system += ar_ptr->system_mem; + total_max_system += ar_ptr->max_system_mem; + + fprintf (fp, + "</sizes>\n<total type=\"fast\" count=\"%zu\" size=\"%zu\"/>\n" + "<total type=\"rest\" count=\"%zu\" size=\"%zu\"/>\n" + "<system type=\"current\" size=\"%zu\"/>\n" + "<system type=\"max\" size=\"%zu\"/>\n", + nfastblocks, fastavail, nblocks, avail, + ar_ptr->system_mem, ar_ptr->max_system_mem); + + if (ar_ptr != &main_arena) + { + heap_info *heap = heap_for_ptr (top (ar_ptr)); + fprintf (fp, + "<aspace type=\"total\" size=\"%zu\"/>\n" + "<aspace type=\"mprotect\" size=\"%zu\"/>\n", + heap->size, heap->mprotect_size); + total_aspace += heap->size; + total_aspace_mprotect += heap->mprotect_size; + } + else + { + fprintf (fp, + "<aspace type=\"total\" size=\"%zu\"/>\n" + "<aspace type=\"mprotect\" size=\"%zu\"/>\n", + ar_ptr->system_mem, ar_ptr->system_mem); + total_aspace += ar_ptr->system_mem; + total_aspace_mprotect += ar_ptr->system_mem; + } + + fputs ("</heap>\n", fp); + ar_ptr = ar_ptr->next; + } + while (ar_ptr != &main_arena); + + fprintf (fp, + "<total type=\"fast\" count=\"%zu\" size=\"%zu\"/>\n" + "<total type=\"rest\" count=\"%zu\" size=\"%zu\"/>\n" + "<total type=\"mmap\" count=\"%d\" size=\"%zu\"/>\n" + "<system type=\"current\" size=\"%zu\"/>\n" + "<system type=\"max\" size=\"%zu\"/>\n" + "<aspace type=\"total\" size=\"%zu\"/>\n" + "<aspace type=\"mprotect\" size=\"%zu\"/>\n" + "</malloc>\n", + total_nfastblocks, total_fastavail, total_nblocks, total_avail, + mp_.n_mmaps, mp_.mmapped_mem, + total_system, total_max_system, + total_aspace, total_aspace_mprotect); + + return 0; +} +weak_alias (__malloc_info, malloc_info) + + +strong_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc) +strong_alias (__libc_free, __free) strong_alias (__libc_free, free) +strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc) +strong_alias (__libc_memalign, __memalign) +weak_alias (__libc_memalign, memalign) +strong_alias (__libc_realloc, __realloc) strong_alias (__libc_realloc, realloc) +strong_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc) +strong_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc) +strong_alias (__libc_mallinfo, __mallinfo) +weak_alias (__libc_mallinfo, mallinfo) +strong_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt) + +weak_alias (__malloc_stats, malloc_stats) +weak_alias (__malloc_usable_size, malloc_usable_size) +weak_alias (__malloc_trim, malloc_trim) + +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26) +compat_symbol (libc, __libc_free, cfree, GLIBC_2_0); +#endif + +/* ------------------------------------------------------------ + History: + + [see ftp://g.oswego.edu/pub/misc/malloc.c for the history of dlmalloc] + + */ +/* + * Local variables: + * c-basic-offset: 2 + * End: + */ diff --git a/REORG.TODO/malloc/malloc.h b/REORG.TODO/malloc/malloc.h new file mode 100644 index 0000000000..339ab64c7d --- /dev/null +++ b/REORG.TODO/malloc/malloc.h @@ -0,0 +1,164 @@ +/* Prototypes and definition for malloc implementation. + Copyright (C) 1996-2017 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/>. */ + +#ifndef _MALLOC_H +#define _MALLOC_H 1 + +#include <features.h> +#include <stddef.h> +#include <stdio.h> + +#ifdef _LIBC +# define __MALLOC_HOOK_VOLATILE +# define __MALLOC_DEPRECATED +#else +# define __MALLOC_HOOK_VOLATILE volatile +# define __MALLOC_DEPRECATED __attribute_deprecated__ +#endif + + +__BEGIN_DECLS + +/* Allocate SIZE bytes of memory. */ +extern void *malloc (size_t __size) __THROW __attribute_malloc__ __wur; + +/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */ +extern void *calloc (size_t __nmemb, size_t __size) +__THROW __attribute_malloc__ __wur; + +/* Re-allocate the previously allocated block in __ptr, making the new + block SIZE bytes long. */ +/* __attribute_malloc__ is not used, because if realloc returns + the same pointer that was passed to it, aliasing needs to be allowed + between objects pointed by the old and new pointers. */ +extern void *realloc (void *__ptr, size_t __size) +__THROW __attribute_warn_unused_result__; + +/* Re-allocate the previously allocated block in PTR, making the new + block large enough for NMEMB elements of SIZE bytes each. */ +/* __attribute_malloc__ is not used, because if reallocarray returns + the same pointer that was passed to it, aliasing needs to be allowed + between objects pointed by the old and new pointers. */ +extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size) +__THROW __attribute_warn_unused_result__; + +/* Free a block allocated by `malloc', `realloc' or `calloc'. */ +extern void free (void *__ptr) __THROW; + +/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */ +extern void *memalign (size_t __alignment, size_t __size) +__THROW __attribute_malloc__ __wur; + +/* Allocate SIZE bytes on a page boundary. */ +extern void *valloc (size_t __size) __THROW __attribute_malloc__ __wur; + +/* Equivalent to valloc(minimum-page-that-holds(n)), that is, round up + __size to nearest pagesize. */ +extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur; + +/* Underlying allocation function; successive calls should return + contiguous pieces of memory. */ +extern void *(*__morecore) (ptrdiff_t __size); + +/* Default value of `__morecore'. */ +extern void *__default_morecore (ptrdiff_t __size) +__THROW __attribute_malloc__; + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo +{ + int arena; /* non-mmapped space allocated from system */ + int ordblks; /* number of free chunks */ + int smblks; /* number of fastbin blocks */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* space in mmapped regions */ + int usmblks; /* always 0, preserved for backwards compatibility */ + int fsmblks; /* space available in freed fastbin blocks */ + int uordblks; /* total allocated space */ + int fordblks; /* total free space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* Returns a copy of the updated current mallinfo. */ +extern struct mallinfo mallinfo (void) __THROW; + +/* SVID2/XPG mallopt options */ +#ifndef M_MXFAST +# define M_MXFAST 1 /* maximum request size for "fastbins" */ +#endif +#ifndef M_NLBLKS +# define M_NLBLKS 2 /* UNUSED in this malloc */ +#endif +#ifndef M_GRAIN +# define M_GRAIN 3 /* UNUSED in this malloc */ +#endif +#ifndef M_KEEP +# define M_KEEP 4 /* UNUSED in this malloc */ +#endif + +/* mallopt options that actually do something */ +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 +#define M_CHECK_ACTION -5 +#define M_PERTURB -6 +#define M_ARENA_TEST -7 +#define M_ARENA_MAX -8 + +/* General SVID/XPG interface to tunable parameters. */ +extern int mallopt (int __param, int __val) __THROW; + +/* Release all but __pad bytes of freed top-most memory back to the + system. Return 1 if successful, else 0. */ +extern int malloc_trim (size_t __pad) __THROW; + +/* Report the number of usable allocated bytes associated with allocated + chunk __ptr. */ +extern size_t malloc_usable_size (void *__ptr) __THROW; + +/* Prints brief summary statistics on stderr. */ +extern void malloc_stats (void) __THROW; + +/* Output information about state of allocator to stream FP. */ +extern int malloc_info (int __options, FILE *__fp) __THROW; + +/* Hooks for debugging and user-defined versions. */ +extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr, + const void *) +__MALLOC_DEPRECATED; +extern void *(*__MALLOC_HOOK_VOLATILE __malloc_hook)(size_t __size, + const void *) +__MALLOC_DEPRECATED; +extern void *(*__MALLOC_HOOK_VOLATILE __realloc_hook)(void *__ptr, + size_t __size, + const void *) +__MALLOC_DEPRECATED; +extern void *(*__MALLOC_HOOK_VOLATILE __memalign_hook)(size_t __alignment, + size_t __size, + const void *) +__MALLOC_DEPRECATED; +extern void (*__MALLOC_HOOK_VOLATILE __after_morecore_hook) (void); + +/* Activate a standard set of debugging hooks. */ +extern void __malloc_check_init (void) __THROW __MALLOC_DEPRECATED; + + +__END_DECLS +#endif /* malloc.h */ diff --git a/REORG.TODO/malloc/mallocbug.c b/REORG.TODO/malloc/mallocbug.c new file mode 100644 index 0000000000..7d19b6fc65 --- /dev/null +++ b/REORG.TODO/malloc/mallocbug.c @@ -0,0 +1,70 @@ +/* Reproduce a GNU malloc bug. */ +#include <malloc.h> +#include <stdio.h> +#include <string.h> + +#define size_t unsigned int + +/* Defined as global variables to avoid warnings about unused variables. */ +char *dummy0; +char *dummy1; +char *fill_info_table1; + + +int +main (int argc, char *argv[]) +{ + char *over_top; + size_t over_top_size = 0x3000; + char *over_top_dup; + size_t over_top_dup_size = 0x7000; + char *x; + size_t i; + + /* Here's what memory is supposed to look like (hex): + size contents + 3000 original_info_table, later fill_info_table1 + 3fa000 dummy0 + 3fa000 dummy1 + 6000 info_table_2 + 3000 over_top + + */ + /* mem: original_info_table */ + dummy0 = malloc (0x3fa000); + /* mem: original_info_table, dummy0 */ + dummy1 = malloc (0x3fa000); + /* mem: free, dummy0, dummy1, info_table_2 */ + fill_info_table1 = malloc (0x3000); + /* mem: fill_info_table1, dummy0, dummy1, info_table_2 */ + + x = malloc (0x1000); + free (x); + /* mem: fill_info_table1, dummy0, dummy1, info_table_2, freexx */ + + /* This is what loses; info_table_2 and freexx get combined unbeknownst + to mmalloc, and mmalloc puts over_top in a section of memory which + is on the free list as part of another block (where info_table_2 had + been). */ + over_top = malloc (over_top_size); + over_top_dup = malloc (over_top_dup_size); + memset (over_top, 0, over_top_size); + memset (over_top_dup, 1, over_top_dup_size); + + for (i = 0; i < over_top_size; ++i) + if (over_top[i] != 0) + { + printf ("FAIL: malloc expands info table\n"); + return 0; + } + + for (i = 0; i < over_top_dup_size; ++i) + if (over_top_dup[i] != 1) + { + printf ("FAIL: malloc expands info table\n"); + return 0; + } + + printf ("PASS: malloc expands info table\n"); + return 0; +} diff --git a/REORG.TODO/malloc/mcheck-init.c b/REORG.TODO/malloc/mcheck-init.c new file mode 100644 index 0000000000..6d2492ef7e --- /dev/null +++ b/REORG.TODO/malloc/mcheck-init.c @@ -0,0 +1,30 @@ +/* Copyright (C) 1991-2017 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/>. */ + +/* The object of this file should be installed as libmcheck.a, + so one can do -lmcheck to turn on mcheck. */ + +#include <malloc.h> +#include <mcheck.h> + +static void +turn_on_mcheck (void) +{ + mcheck (NULL); +} + +void (*__malloc_initialize_hook) (void) = turn_on_mcheck; diff --git a/REORG.TODO/malloc/mcheck.c b/REORG.TODO/malloc/mcheck.c new file mode 100644 index 0000000000..5128d687fa --- /dev/null +++ b/REORG.TODO/malloc/mcheck.c @@ -0,0 +1,416 @@ +/* Standard debugging hooks for `malloc'. + Copyright (C) 1990-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written May 1989 by Mike Haertel. + + 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/>. */ + +#ifndef _MALLOC_INTERNAL +# define _MALLOC_INTERNAL +# include <malloc.h> +# include <mcheck.h> +# include <stdint.h> +# include <stdio.h> +# include <libintl.h> +# include <errno.h> +#endif + +/* Old hook values. */ +static void (*old_free_hook)(__ptr_t ptr, const __ptr_t); +static __ptr_t (*old_malloc_hook) (size_t size, const __ptr_t); +static __ptr_t (*old_memalign_hook) (size_t alignment, size_t size, + const __ptr_t); +static __ptr_t (*old_realloc_hook) (__ptr_t ptr, size_t size, + const __ptr_t); + +/* Function to call when something awful happens. */ +static void (*abortfunc) (enum mcheck_status); + +/* Arbitrary magical numbers. */ +#define MAGICWORD 0xfedabeeb +#define MAGICFREE 0xd8675309 +#define MAGICBYTE ((char) 0xd7) +#define MALLOCFLOOD ((char) 0x93) +#define FREEFLOOD ((char) 0x95) + +struct hdr +{ + size_t size; /* Exact size requested by user. */ + unsigned long int magic; /* Magic number to check header integrity. */ + struct hdr *prev; + struct hdr *next; + __ptr_t block; /* Real block allocated, for memalign. */ + unsigned long int magic2; /* Extra, keeps us doubleword aligned. */ +}; + +/* This is the beginning of the list of all memory blocks allocated. + It is only constructed if the pedantic testing is requested. */ +static struct hdr *root; + +static int mcheck_used; + +/* Nonzero if pedentic checking of all blocks is requested. */ +static int pedantic; + +#if defined _LIBC || defined STDC_HEADERS || defined USG +# include <string.h> +# define flood memset +#else +static void flood (__ptr_t, int, size_t); +static void +flood (__ptr_t ptr, int val, size_t size) +{ + char *cp = ptr; + while (size--) + *cp++ = val; +} +#endif + +static enum mcheck_status +checkhdr (const struct hdr *hdr) +{ + enum mcheck_status status; + + if (!mcheck_used) + /* Maybe the mcheck used is disabled? This happens when we find + an error and report it. */ + return MCHECK_OK; + + switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next)) + { + default: + status = MCHECK_HEAD; + break; + case MAGICFREE: + status = MCHECK_FREE; + break; + case MAGICWORD: + if (((char *) &hdr[1])[hdr->size] != MAGICBYTE) + status = MCHECK_TAIL; + else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD) + status = MCHECK_HEAD; + else + status = MCHECK_OK; + break; + } + if (status != MCHECK_OK) + { + mcheck_used = 0; + (*abortfunc) (status); + mcheck_used = 1; + } + return status; +} + +void +mcheck_check_all (void) +{ + /* Walk through all the active blocks and test whether they were tampered + with. */ + struct hdr *runp = root; + + /* Temporarily turn off the checks. */ + pedantic = 0; + + while (runp != NULL) + { + (void) checkhdr (runp); + + runp = runp->next; + } + + /* Turn checks on again. */ + pedantic = 1; +} +#ifdef _LIBC +libc_hidden_def (mcheck_check_all) +#endif + +static void +unlink_blk (struct hdr *ptr) +{ + if (ptr->next != NULL) + { + ptr->next->prev = ptr->prev; + ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev + + (uintptr_t) ptr->next->next); + } + if (ptr->prev != NULL) + { + ptr->prev->next = ptr->next; + ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev + + (uintptr_t) ptr->prev->next); + } + else + root = ptr->next; +} + +static void +link_blk (struct hdr *hdr) +{ + hdr->prev = NULL; + hdr->next = root; + root = hdr; + hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next; + + /* And the next block. */ + if (hdr->next != NULL) + { + hdr->next->prev = hdr; + hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr + + (uintptr_t) hdr->next->next); + } +} +static void +freehook (__ptr_t ptr, const __ptr_t caller) +{ + if (pedantic) + mcheck_check_all (); + if (ptr) + { + struct hdr *hdr = ((struct hdr *) ptr) - 1; + checkhdr (hdr); + hdr->magic = MAGICFREE; + hdr->magic2 = MAGICFREE; + unlink_blk (hdr); + hdr->prev = hdr->next = NULL; + flood (ptr, FREEFLOOD, hdr->size); + ptr = hdr->block; + } + __free_hook = old_free_hook; + if (old_free_hook != NULL) + (*old_free_hook)(ptr, caller); + else + free (ptr); + __free_hook = freehook; +} + +static __ptr_t +mallochook (size_t size, const __ptr_t caller) +{ + struct hdr *hdr; + + if (pedantic) + mcheck_check_all (); + + if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1)) + { + __set_errno (ENOMEM); + return NULL; + } + + __malloc_hook = old_malloc_hook; + if (old_malloc_hook != NULL) + hdr = (struct hdr *) (*old_malloc_hook)(sizeof (struct hdr) + size + 1, + caller); + else + hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1); + __malloc_hook = mallochook; + if (hdr == NULL) + return NULL; + + hdr->size = size; + link_blk (hdr); + hdr->block = hdr; + hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD; + ((char *) &hdr[1])[size] = MAGICBYTE; + flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size); + return (__ptr_t) (hdr + 1); +} + +static __ptr_t +memalignhook (size_t alignment, size_t size, + const __ptr_t caller) +{ + struct hdr *hdr; + size_t slop; + char *block; + + if (pedantic) + mcheck_check_all (); + + slop = (sizeof *hdr + alignment - 1) & - alignment; + + if (size > ~((size_t) 0) - (slop + 1)) + { + __set_errno (ENOMEM); + return NULL; + } + + __memalign_hook = old_memalign_hook; + if (old_memalign_hook != NULL) + block = (*old_memalign_hook)(alignment, slop + size + 1, caller); + else + block = memalign (alignment, slop + size + 1); + __memalign_hook = memalignhook; + if (block == NULL) + return NULL; + + hdr = ((struct hdr *) (block + slop)) - 1; + + hdr->size = size; + link_blk (hdr); + hdr->block = (__ptr_t) block; + hdr->magic2 = (uintptr_t) block ^ MAGICWORD; + ((char *) &hdr[1])[size] = MAGICBYTE; + flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size); + return (__ptr_t) (hdr + 1); +} + +static __ptr_t +reallochook (__ptr_t ptr, size_t size, const __ptr_t caller) +{ + if (size == 0) + { + freehook (ptr, caller); + return NULL; + } + + struct hdr *hdr; + size_t osize; + + if (pedantic) + mcheck_check_all (); + + if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1)) + { + __set_errno (ENOMEM); + return NULL; + } + + if (ptr) + { + hdr = ((struct hdr *) ptr) - 1; + osize = hdr->size; + + checkhdr (hdr); + unlink_blk (hdr); + if (size < osize) + flood ((char *) ptr + size, FREEFLOOD, osize - size); + } + else + { + osize = 0; + hdr = NULL; + } + __free_hook = old_free_hook; + __malloc_hook = old_malloc_hook; + __memalign_hook = old_memalign_hook; + __realloc_hook = old_realloc_hook; + if (old_realloc_hook != NULL) + hdr = (struct hdr *) (*old_realloc_hook)((__ptr_t) hdr, + sizeof (struct hdr) + size + 1, + caller); + else + hdr = (struct hdr *) realloc ((__ptr_t) hdr, + sizeof (struct hdr) + size + 1); + __free_hook = freehook; + __malloc_hook = mallochook; + __memalign_hook = memalignhook; + __realloc_hook = reallochook; + if (hdr == NULL) + return NULL; + + hdr->size = size; + link_blk (hdr); + hdr->block = hdr; + hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD; + ((char *) &hdr[1])[size] = MAGICBYTE; + if (size > osize) + flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize); + return (__ptr_t) (hdr + 1); +} + +__attribute__ ((noreturn)) +static void +mabort (enum mcheck_status status) +{ + const char *msg; + switch (status) + { + case MCHECK_OK: + msg = _ ("memory is consistent, library is buggy\n"); + break; + case MCHECK_HEAD: + msg = _ ("memory clobbered before allocated block\n"); + break; + case MCHECK_TAIL: + msg = _ ("memory clobbered past end of allocated block\n"); + break; + case MCHECK_FREE: + msg = _ ("block freed twice\n"); + break; + default: + msg = _ ("bogus mcheck_status, library is buggy\n"); + break; + } +#ifdef _LIBC + __libc_fatal (msg); +#else + fprintf (stderr, "mcheck: %s", msg); + fflush (stderr); + abort (); +#endif +} + +/* Memory barrier so that GCC does not optimize out the argument. */ +#define malloc_opt_barrier(x) \ + ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; }) + +int +mcheck (void (*func) (enum mcheck_status)) +{ + abortfunc = (func != NULL) ? func : &mabort; + + /* These hooks may not be safely inserted if malloc is already in use. */ + if (__malloc_initialized <= 0 && !mcheck_used) + { + /* We call malloc() once here to ensure it is initialized. */ + void *p = malloc (0); + /* GCC might optimize out the malloc/free pair without a barrier. */ + p = malloc_opt_barrier (p); + free (p); + + old_free_hook = __free_hook; + __free_hook = freehook; + old_malloc_hook = __malloc_hook; + __malloc_hook = mallochook; + old_memalign_hook = __memalign_hook; + __memalign_hook = memalignhook; + old_realloc_hook = __realloc_hook; + __realloc_hook = reallochook; + mcheck_used = 1; + } + + return mcheck_used ? 0 : -1; +} +#ifdef _LIBC +libc_hidden_def (mcheck) +#endif + +int +mcheck_pedantic (void (*func) (enum mcheck_status)) +{ + int res = mcheck (func); + if (res == 0) + pedantic = 1; + return res; +} + +enum mcheck_status +mprobe (__ptr_t ptr) +{ + return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED; +} diff --git a/REORG.TODO/malloc/mcheck.h b/REORG.TODO/malloc/mcheck.h new file mode 100644 index 0000000000..3d56bef238 --- /dev/null +++ b/REORG.TODO/malloc/mcheck.h @@ -0,0 +1,60 @@ +/* Copyright (C) 1996-2017 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/>. */ + +#ifndef _MCHECK_H +#define _MCHECK_H 1 + +#include <features.h> + +__BEGIN_DECLS + +/* Return values for `mprobe': these are the kinds of inconsistencies that + `mcheck' enables detection of. */ +enum mcheck_status +{ + MCHECK_DISABLED = -1, /* Consistency checking is not turned on. */ + MCHECK_OK, /* Block is fine. */ + MCHECK_FREE, /* Block freed twice. */ + MCHECK_HEAD, /* Memory before the block was clobbered. */ + MCHECK_TAIL /* Memory after the block was clobbered. */ +}; + + +/* Activate a standard collection of debugging hooks. This must be called + before `malloc' is ever called. ABORTFUNC is called with an error code + (see enum above) when an inconsistency is detected. If ABORTFUNC is + null, the standard function prints on stderr and then calls `abort'. */ +extern int mcheck (void (*__abortfunc)(enum mcheck_status)) __THROW; + +/* Similar to `mcheck' but performs checks for all block whenever one of + the memory handling functions is called. This can be very slow. */ +extern int mcheck_pedantic (void (*__abortfunc)(enum mcheck_status)) __THROW; + +/* Force check of all blocks now. */ +extern void mcheck_check_all (void); + +/* Check for aberrations in a particular malloc'd block. You must have + called `mcheck' already. These are the same checks that `mcheck' does + when you free or reallocate a block. */ +extern enum mcheck_status mprobe (void *__ptr) __THROW; + +/* Activate a standard collection of tracing hooks. */ +extern void mtrace (void) __THROW; +extern void muntrace (void) __THROW; + +__END_DECLS +#endif /* mcheck.h */ diff --git a/REORG.TODO/malloc/memusage.c b/REORG.TODO/malloc/memusage.c new file mode 100644 index 0000000000..3deca2a406 --- /dev/null +++ b/REORG.TODO/malloc/memusage.c @@ -0,0 +1,936 @@ +/* Profile heap and stack memory usage of running program. + Copyright (C) 1998-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + 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/>. */ + +#include <assert.h> +#include <atomic.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include <memusage.h> + +/* Pointer to the real functions. These are determined used `dlsym' + when really needed. */ +static void *(*mallocp)(size_t); +static void *(*reallocp) (void *, size_t); +static void *(*callocp) (size_t, size_t); +static void (*freep) (void *); + +static void *(*mmapp) (void *, size_t, int, int, int, off_t); +static void *(*mmap64p) (void *, size_t, int, int, int, off64_t); +static int (*munmapp) (void *, size_t); +static void *(*mremapp) (void *, size_t, size_t, int, void *); + +enum +{ + idx_malloc = 0, + idx_realloc, + idx_calloc, + idx_free, + idx_mmap_r, + idx_mmap_w, + idx_mmap_a, + idx_mremap, + idx_munmap, + idx_last +}; + + +struct header +{ + size_t length; + size_t magic; +}; + +#define MAGIC 0xfeedbeaf + + +static memusage_cntr_t calls[idx_last]; +static memusage_cntr_t failed[idx_last]; +static memusage_size_t total[idx_last]; +static memusage_size_t grand_total; +static memusage_cntr_t histogram[65536 / 16]; +static memusage_cntr_t large; +static memusage_cntr_t calls_total; +static memusage_cntr_t inplace; +static memusage_cntr_t decreasing; +static memusage_cntr_t realloc_free; +static memusage_cntr_t inplace_mremap; +static memusage_cntr_t decreasing_mremap; +static memusage_size_t current_heap; +static memusage_size_t peak_use[3]; +static __thread uintptr_t start_sp; + +/* A few macros to make the source more readable. */ +#define peak_heap peak_use[0] +#define peak_stack peak_use[1] +#define peak_total peak_use[2] + +#define DEFAULT_BUFFER_SIZE 32768 +static size_t buffer_size; + +static int fd = -1; + +static bool not_me; +static int initialized; +static bool trace_mmap; +extern const char *__progname; + +struct entry +{ + uint64_t heap; + uint64_t stack; + uint32_t time_low; + uint32_t time_high; +}; + +static struct entry buffer[2 * DEFAULT_BUFFER_SIZE]; +static uatomic32_t buffer_cnt; +static struct entry first; + + +/* Update the global data after a successful function call. */ +static void +update_data (struct header *result, size_t len, size_t old_len) +{ + if (result != NULL) + { + /* Record the information we need and mark the block using a + magic number. */ + result->length = len; + result->magic = MAGIC; + } + + /* Compute current heap usage and compare it with the maximum value. */ + memusage_size_t heap + = catomic_exchange_and_add (¤t_heap, len - old_len) + len - old_len; + catomic_max (&peak_heap, heap); + + /* Compute current stack usage and compare it with the maximum + value. The base stack pointer might not be set if this is not + the main thread and it is the first call to any of these + functions. */ + if (__glibc_unlikely (!start_sp)) + start_sp = GETSP (); + + uintptr_t sp = GETSP (); +#ifdef STACK_GROWS_UPWARD + /* This can happen in threads where we didn't catch the thread's + stack early enough. */ + if (__glibc_unlikely (sp < start_sp)) + start_sp = sp; + size_t current_stack = sp - start_sp; +#else + /* This can happen in threads where we didn't catch the thread's + stack early enough. */ + if (__glibc_unlikely (sp > start_sp)) + start_sp = sp; + size_t current_stack = start_sp - sp; +#endif + catomic_max (&peak_stack, current_stack); + + /* Add up heap and stack usage and compare it with the maximum value. */ + catomic_max (&peak_total, heap + current_stack); + + /* Store the value only if we are writing to a file. */ + if (fd != -1) + { + uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1); + if (idx + 1 >= 2 * buffer_size) + { + /* We try to reset the counter to the correct range. If + this fails because of another thread increasing the + counter it does not matter since that thread will take + care of the correction. */ + uatomic32_t reset = (idx + 1) % (2 * buffer_size); + catomic_compare_and_exchange_val_acq (&buffer_cnt, reset, idx + 1); + if (idx >= 2 * buffer_size) + idx = reset - 1; + } + assert (idx < 2 * DEFAULT_BUFFER_SIZE); + + buffer[idx].heap = current_heap; + buffer[idx].stack = current_stack; + GETTIME (buffer[idx].time_low, buffer[idx].time_high); + + /* Write out buffer if it is full. */ + if (idx + 1 == buffer_size) + write (fd, buffer, buffer_size * sizeof (struct entry)); + else if (idx + 1 == 2 * buffer_size) + write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry)); + } +} + + +/* Interrupt handler. */ +static void +int_handler (int signo) +{ + /* Nothing gets allocated. Just record the stack pointer position. */ + update_data (NULL, 0, 0); +} + + +/* Find out whether this is the program we are supposed to profile. + For this the name in the variable `__progname' must match the one + given in the environment variable MEMUSAGE_PROG_NAME. If the variable + is not present every program assumes it should be profiling. + + If this is the program open a file descriptor to the output file. + We will write to it whenever the buffer overflows. The name of the + output file is determined by the environment variable MEMUSAGE_OUTPUT. + + If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical + value determines the size of the internal buffer. The number gives + the number of elements in the buffer. By setting the number to one + one effectively selects unbuffered operation. + + If MEMUSAGE_NO_TIMER is not present an alarm handler is installed + which at the highest possible frequency records the stack pointer. */ +static void +me (void) +{ + const char *env = getenv ("MEMUSAGE_PROG_NAME"); + size_t prog_len = strlen (__progname); + + initialized = -1; + mallocp = (void *(*)(size_t))dlsym (RTLD_NEXT, "malloc"); + reallocp = (void *(*)(void *, size_t))dlsym (RTLD_NEXT, "realloc"); + callocp = (void *(*)(size_t, size_t))dlsym (RTLD_NEXT, "calloc"); + freep = (void (*)(void *))dlsym (RTLD_NEXT, "free"); + + mmapp = (void *(*)(void *, size_t, int, int, int, off_t))dlsym (RTLD_NEXT, + "mmap"); + mmap64p = + (void *(*)(void *, size_t, int, int, int, off64_t))dlsym (RTLD_NEXT, + "mmap64"); + mremapp = (void *(*)(void *, size_t, size_t, int, void *))dlsym (RTLD_NEXT, + "mremap"); + munmapp = (int (*)(void *, size_t))dlsym (RTLD_NEXT, "munmap"); + initialized = 1; + + if (env != NULL) + { + /* Check for program name. */ + size_t len = strlen (env); + if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0 + || (prog_len != len && __progname[prog_len - len - 1] != '/')) + not_me = true; + } + + /* Only open the file if it's really us. */ + if (!not_me && fd == -1) + { + const char *outname; + + if (!start_sp) + start_sp = GETSP (); + + outname = getenv ("MEMUSAGE_OUTPUT"); + if (outname != NULL && outname[0] != '\0' + && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT)) + { + fd = creat64 (outname, 0666); + + if (fd == -1) + /* Don't do anything in future calls if we cannot write to + the output file. */ + not_me = true; + else + { + /* Write the first entry. */ + first.heap = 0; + first.stack = 0; + GETTIME (first.time_low, first.time_high); + /* Write it two times since we need the starting and end time. */ + write (fd, &first, sizeof (first)); + write (fd, &first, sizeof (first)); + + /* Determine the buffer size. We use the default if the + environment variable is not present. */ + buffer_size = DEFAULT_BUFFER_SIZE; + const char *str_buffer_size = getenv ("MEMUSAGE_BUFFER_SIZE"); + if (str_buffer_size != NULL) + { + buffer_size = atoi (str_buffer_size); + if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE) + buffer_size = DEFAULT_BUFFER_SIZE; + } + + /* Possibly enable timer-based stack pointer retrieval. */ + if (getenv ("MEMUSAGE_NO_TIMER") == NULL) + { + struct sigaction act; + + act.sa_handler = (sighandler_t) &int_handler; + act.sa_flags = SA_RESTART; + sigfillset (&act.sa_mask); + + if (sigaction (SIGPROF, &act, NULL) >= 0) + { + struct itimerval timer; + + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 1; + timer.it_interval = timer.it_value; + setitimer (ITIMER_PROF, &timer, NULL); + } + } + } + } + + if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL) + trace_mmap = true; + } +} + + +/* Record the initial stack position. */ +static void +__attribute__ ((constructor)) +init (void) +{ + start_sp = GETSP (); + if (!initialized) + me (); +} + + +/* `malloc' replacement. We keep track of the memory usage if this is the + correct program. */ +void * +malloc (size_t len) +{ + struct header *result = NULL; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + return (*mallocp)(len); + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_malloc]); + /* Keep track of total memory consumption for `malloc'. */ + catomic_add (&total[idx_malloc], len); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, len); + /* Remember the size of the request. */ + if (len < 65536) + catomic_increment (&histogram[len / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + catomic_increment (&calls_total); + + /* Do the real work. */ + result = (struct header *) (*mallocp)(len + sizeof (struct header)); + if (result == NULL) + { + catomic_increment (&failed[idx_malloc]); + return NULL; + } + + /* Update the allocation data and write out the records if necessary. */ + update_data (result, len, 0); + + /* Return the pointer to the user buffer. */ + return (void *) (result + 1); +} + + +/* `realloc' replacement. We keep track of the memory usage if this is the + correct program. */ +void * +realloc (void *old, size_t len) +{ + struct header *result = NULL; + struct header *real; + size_t old_len; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + return (*reallocp)(old, len); + + if (old == NULL) + { + /* This is really a `malloc' call. */ + real = NULL; + old_len = 0; + } + else + { + real = ((struct header *) old) - 1; + if (real->magic != MAGIC) + /* This is no memory allocated here. */ + return (*reallocp)(old, len); + + old_len = real->length; + } + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_realloc]); + if (len > old_len) + { + /* Keep track of total memory consumption for `realloc'. */ + catomic_add (&total[idx_realloc], len - old_len); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, len - old_len); + } + + if (len == 0 && old != NULL) + { + /* Special case. */ + catomic_increment (&realloc_free); + /* Keep track of total memory freed using `free'. */ + catomic_add (&total[idx_free], real->length); + + /* Update the allocation data and write out the records if necessary. */ + update_data (NULL, 0, old_len); + + /* Do the real work. */ + (*freep) (real); + + return NULL; + } + + /* Remember the size of the request. */ + if (len < 65536) + catomic_increment (&histogram[len / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + catomic_increment (&calls_total); + + /* Do the real work. */ + result = (struct header *) (*reallocp)(real, len + sizeof (struct header)); + if (result == NULL) + { + catomic_increment (&failed[idx_realloc]); + return NULL; + } + + /* Record whether the reduction/increase happened in place. */ + if (real == result) + catomic_increment (&inplace); + /* Was the buffer increased? */ + if (old_len > len) + catomic_increment (&decreasing); + + /* Update the allocation data and write out the records if necessary. */ + update_data (result, len, old_len); + + /* Return the pointer to the user buffer. */ + return (void *) (result + 1); +} + + +/* `calloc' replacement. We keep track of the memory usage if this is the + correct program. */ +void * +calloc (size_t n, size_t len) +{ + struct header *result; + size_t size = n * len; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + return (*callocp)(n, len); + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_calloc]); + /* Keep track of total memory consumption for `calloc'. */ + catomic_add (&total[idx_calloc], size); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, size); + /* Remember the size of the request. */ + if (size < 65536) + catomic_increment (&histogram[size / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + ++calls_total; + + /* Do the real work. */ + result = (struct header *) (*mallocp)(size + sizeof (struct header)); + if (result == NULL) + { + catomic_increment (&failed[idx_calloc]); + return NULL; + } + + /* Update the allocation data and write out the records if necessary. */ + update_data (result, size, 0); + + /* Do what `calloc' would have done and return the buffer to the caller. */ + return memset (result + 1, '\0', size); +} + + +/* `free' replacement. We keep track of the memory usage if this is the + correct program. */ +void +free (void *ptr) +{ + struct header *real; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return; + + me (); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + { + (*freep) (ptr); + return; + } + + /* `free (NULL)' has no effect. */ + if (ptr == NULL) + { + catomic_increment (&calls[idx_free]); + return; + } + + /* Determine the pointer to the header. */ + real = ((struct header *) ptr) - 1; + if (real->magic != MAGIC) + { + /* This block wasn't allocated here. */ + (*freep) (ptr); + return; + } + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_free]); + /* Keep track of total memory freed using `free'. */ + catomic_add (&total[idx_free], real->length); + + /* Update the allocation data and write out the records if necessary. */ + update_data (NULL, 0, real->length); + + /* Do the real work. */ + (*freep) (real); +} + + +/* `mmap' replacement. We do not have to keep track of the size since + `munmap' will get it as a parameter. */ +void * +mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset) +{ + void *result = NULL; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* Always get a block. We don't need extra memory. */ + result = (*mmapp)(start, len, prot, flags, fd, offset); + + if (!not_me && trace_mmap) + { + int idx = (flags & MAP_ANON + ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r); + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx]); + /* Keep track of total memory consumption for `malloc'. */ + catomic_add (&total[idx], len); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, len); + /* Remember the size of the request. */ + if (len < 65536) + catomic_increment (&histogram[len / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + catomic_increment (&calls_total); + + /* Check for failures. */ + if (result == NULL) + catomic_increment (&failed[idx]); + else if (idx == idx_mmap_w) + /* Update the allocation data and write out the records if + necessary. Note the first parameter is NULL which means + the size is not tracked. */ + update_data (NULL, len, 0); + } + + /* Return the pointer to the user buffer. */ + return result; +} + + +/* `mmap64' replacement. We do not have to keep track of the size since + `munmap' will get it as a parameter. */ +void * +mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset) +{ + void *result = NULL; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* Always get a block. We don't need extra memory. */ + result = (*mmap64p)(start, len, prot, flags, fd, offset); + + if (!not_me && trace_mmap) + { + int idx = (flags & MAP_ANON + ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r); + + /* Keep track of number of calls. */ + catomic_increment (&calls[idx]); + /* Keep track of total memory consumption for `malloc'. */ + catomic_add (&total[idx], len); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, len); + /* Remember the size of the request. */ + if (len < 65536) + catomic_increment (&histogram[len / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + catomic_increment (&calls_total); + + /* Check for failures. */ + if (result == NULL) + catomic_increment (&failed[idx]); + else if (idx == idx_mmap_w) + /* Update the allocation data and write out the records if + necessary. Note the first parameter is NULL which means + the size is not tracked. */ + update_data (NULL, len, 0); + } + + /* Return the pointer to the user buffer. */ + return result; +} + + +/* `mremap' replacement. We do not have to keep track of the size since + `munmap' will get it as a parameter. */ +void * +mremap (void *start, size_t old_len, size_t len, int flags, ...) +{ + void *result = NULL; + va_list ap; + + va_start (ap, flags); + void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL; + va_end (ap); + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return NULL; + + me (); + } + + /* Always get a block. We don't need extra memory. */ + result = (*mremapp)(start, old_len, len, flags, newaddr); + + if (!not_me && trace_mmap) + { + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_mremap]); + if (len > old_len) + { + /* Keep track of total memory consumption for `malloc'. */ + catomic_add (&total[idx_mremap], len - old_len); + /* Keep track of total memory requirement. */ + catomic_add (&grand_total, len - old_len); + } + /* Remember the size of the request. */ + if (len < 65536) + catomic_increment (&histogram[len / 16]); + else + catomic_increment (&large); + /* Total number of calls of any of the functions. */ + catomic_increment (&calls_total); + + /* Check for failures. */ + if (result == NULL) + catomic_increment (&failed[idx_mremap]); + else + { + /* Record whether the reduction/increase happened in place. */ + if (start == result) + catomic_increment (&inplace_mremap); + /* Was the buffer increased? */ + if (old_len > len) + catomic_increment (&decreasing_mremap); + + /* Update the allocation data and write out the records if + necessary. Note the first parameter is NULL which means + the size is not tracked. */ + update_data (NULL, len, old_len); + } + } + + /* Return the pointer to the user buffer. */ + return result; +} + + +/* `munmap' replacement. */ +int +munmap (void *start, size_t len) +{ + int result; + + /* Determine real implementation if not already happened. */ + if (__glibc_unlikely (initialized <= 0)) + { + if (initialized == -1) + return -1; + + me (); + } + + /* Do the real work. */ + result = (*munmapp)(start, len); + + if (!not_me && trace_mmap) + { + /* Keep track of number of calls. */ + catomic_increment (&calls[idx_munmap]); + + if (__glibc_likely (result == 0)) + { + /* Keep track of total memory freed using `free'. */ + catomic_add (&total[idx_munmap], len); + + /* Update the allocation data and write out the records if + necessary. */ + update_data (NULL, 0, len); + } + else + catomic_increment (&failed[idx_munmap]); + } + + return result; +} + + +/* Write some statistics to standard error. */ +static void +__attribute__ ((destructor)) +dest (void) +{ + int percent, cnt; + unsigned long int maxcalls; + + /* If we haven't done anything here just return. */ + if (not_me) + return; + + /* If we should call any of the memory functions don't do any profiling. */ + not_me = true; + + /* Finish the output file. */ + if (fd != -1) + { + /* Write the partially filled buffer. */ + if (buffer_cnt > buffer_size) + write (fd, buffer + buffer_size, + (buffer_cnt - buffer_size) * sizeof (struct entry)); + else + write (fd, buffer, buffer_cnt * sizeof (struct entry)); + + /* Go back to the beginning of the file. We allocated two records + here when we opened the file. */ + lseek (fd, 0, SEEK_SET); + /* Write out a record containing the total size. */ + first.stack = peak_total; + write (fd, &first, sizeof (struct entry)); + /* Write out another record containing the maximum for heap and + stack. */ + first.heap = peak_heap; + first.stack = peak_stack; + GETTIME (first.time_low, first.time_high); + write (fd, &first, sizeof (struct entry)); + + /* Close the file. */ + close (fd); + fd = -1; + } + + /* Write a colorful statistic. */ + fprintf (stderr, "\n\ +\e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\ +\e[04;34m total calls total memory failed calls\e[0m\n\ +\e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\ +\e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove:%ld, dec:%ld, free:%ld)\n\ +\e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\ +\e[00;34m free|\e[0m %10lu %12llu\n", + (unsigned long long int) grand_total, (unsigned long int) peak_heap, + (unsigned long int) peak_stack, + (unsigned long int) calls[idx_malloc], + (unsigned long long int) total[idx_malloc], + failed[idx_malloc] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_malloc], + (unsigned long int) calls[idx_realloc], + (unsigned long long int) total[idx_realloc], + failed[idx_realloc] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_realloc], + (unsigned long int) inplace, + (unsigned long int) decreasing, + (unsigned long int) realloc_free, + (unsigned long int) calls[idx_calloc], + (unsigned long long int) total[idx_calloc], + failed[idx_calloc] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_calloc], + (unsigned long int) calls[idx_free], + (unsigned long long int) total[idx_free]); + + if (trace_mmap) + fprintf (stderr, "\ +\e[00;34mmmap(r)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\ +\e[00;34mmmap(w)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\ +\e[00;34mmmap(a)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\ +\e[00;34m mremap|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove: %ld, dec:%ld)\n\ +\e[00;34m munmap|\e[0m %10lu %12llu %s%12lu\e[00;00m\n", + (unsigned long int) calls[idx_mmap_r], + (unsigned long long int) total[idx_mmap_r], + failed[idx_mmap_r] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_mmap_r], + (unsigned long int) calls[idx_mmap_w], + (unsigned long long int) total[idx_mmap_w], + failed[idx_mmap_w] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_mmap_w], + (unsigned long int) calls[idx_mmap_a], + (unsigned long long int) total[idx_mmap_a], + failed[idx_mmap_a] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_mmap_a], + (unsigned long int) calls[idx_mremap], + (unsigned long long int) total[idx_mremap], + failed[idx_mremap] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_mremap], + (unsigned long int) inplace_mremap, + (unsigned long int) decreasing_mremap, + (unsigned long int) calls[idx_munmap], + (unsigned long long int) total[idx_munmap], + failed[idx_munmap] ? "\e[01;41m" : "", + (unsigned long int) failed[idx_munmap]); + + /* Write out a histoogram of the sizes of the allocations. */ + fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n"); + + /* Determine the maximum of all calls for each size range. */ + maxcalls = large; + for (cnt = 0; cnt < 65536; cnt += 16) + if (histogram[cnt / 16] > maxcalls) + maxcalls = histogram[cnt / 16]; + + for (cnt = 0; cnt < 65536; cnt += 16) + /* Only write out the nonzero entries. */ + if (histogram[cnt / 16] != 0) + { + percent = (histogram[cnt / 16] * 100) / calls_total; + fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15, + (unsigned long int) histogram[cnt / 16]); + if (percent == 0) + fputs (" <1% \e[41;37m", stderr); + else + fprintf (stderr, "%3d%% \e[41;37m", percent); + + /* Draw a bar with a length corresponding to the current + percentage. */ + percent = (histogram[cnt / 16] * 50) / maxcalls; + while (percent-- > 0) + fputc ('=', stderr); + fputs ("\e[0;0m\n", stderr); + } + + if (large != 0) + { + percent = (large * 100) / calls_total; + fprintf (stderr, " large %12lu ", (unsigned long int) large); + if (percent == 0) + fputs (" <1% \e[41;37m", stderr); + else + fprintf (stderr, "%3d%% \e[41;37m", percent); + percent = (large * 50) / maxcalls; + while (percent-- > 0) + fputc ('=', stderr); + fputs ("\e[0;0m\n", stderr); + } + + /* Any following malloc/free etc. calls should generate statistics again, + because otherwise freeing something that has been malloced before + this destructor (including struct header in front of it) wouldn't + be properly freed. */ + not_me = false; +} diff --git a/REORG.TODO/malloc/memusage.sh b/REORG.TODO/malloc/memusage.sh new file mode 100755 index 0000000000..93ece87a31 --- /dev/null +++ b/REORG.TODO/malloc/memusage.sh @@ -0,0 +1,274 @@ +#! @BASH@ +# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# Contributed by Ulrich Drepper <drepper@gnu.org>, 1999. + +# 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/>. + +memusageso='@SLIBDIR@/libmemusage.so' +memusagestat='@BINDIR@/memusagestat' +TEXTDOMAIN=libc + +# Print usage message. +do_usage() { + printf >&2 $"Try \`%s --help' or \`%s --usage' for more information.\n" memusage memusage + exit 1 +} + +# Message for missing argument. +do_missing_arg() { + printf >&2 $"%s: option '%s' requires an argument\n" memusage "$1" + do_usage +} + +# Print help message +do_help() { + echo $"Usage: memusage [OPTION]... PROGRAM [PROGRAMOPTION]... +Profile memory usage of PROGRAM. + + -n,--progname=NAME Name of the program file to profile + -p,--png=FILE Generate PNG graphic and store it in FILE + -d,--data=FILE Generate binary data file and store it in FILE + -u,--unbuffered Don't buffer output + -b,--buffer=SIZE Collect SIZE entries before writing them out + --no-timer Don't collect additional information through timer + -m,--mmap Also trace mmap & friends + + -?,--help Print this help and exit + --usage Give a short usage message + -V,--version Print version information and exit + + The following options only apply when generating graphical output: + -t,--time-based Make graph linear in time + -T,--total Also draw graph of total memory use + --title=STRING Use STRING as title of the graph + -x,--x-size=SIZE Make graphic SIZE pixels wide + -y,--y-size=SIZE Make graphic SIZE pixels high + +Mandatory arguments to long options are also mandatory for any corresponding +short options. + +" + printf $"For bug reporting instructions, please see:\\n%s.\\n" \ + "@REPORT_BUGS_TO@" + exit 0 +} + +do_version() { + echo 'memusage @PKGVERSION@@VERSION@' + printf $"Copyright (C) %s Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +" "2017" + printf $"Written by %s. +" "Ulrich Drepper" + exit 0 +} + +# These variables are local +buffer= +data= +memusagestat_args= +notimer= +png= +progname= +tracemmap= + +# Process arguments. But stop as soon as the program name is found. +while test $# -gt 0; do + case "$1" in + -V | --v | --ve | --ver | --vers | --versi | --versio | --version) + do_version + ;; + -\? | --h | --he | --hel | --help) + do_help + ;; + --us | --usa | --usag | --usage) + echo $"Syntax: memusage [--data=FILE] [--progname=NAME] [--png=FILE] [--unbuffered] + [--buffer=SIZE] [--no-timer] [--time-based] [--total] + [--title=STRING] [--x-size=SIZE] [--y-size=SIZE] + PROGRAM [PROGRAMOPTION]..." + exit 0 + ;; + -n | --pr | --pro | --prog | --progn | --progna | --prognam | --progname) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + progname="$1" + ;; + --pr=* | --pro=* | --prog=* | --progn=* | --progna=* | --prognam=* | --progname=*) + progname=${1##*=} + ;; + -p | --pn | --png) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + png="$1" + ;; + --pn=* | --png=*) + png=${1##*=} + ;; + -d | --d | --da | --dat | --data) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + data="$1" + ;; + --d=* | --da=* | --dat=* | --data=*) + data=${1##*=} + ;; + -u | --un | --unb | --unbu | --unbuf | --unbuff | --unbuffe | --unbuffer | --unbuffere | --unbuffered) + buffer=1 + ;; + -b | --b | --bu | --buf | --buff | --buffe | --buffer) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + buffer="$1" + ;; + --b=* | --bu=* | --buf=* | --buff=* | --buffe=* | --buffer=*) + buffer=${1##*=} + ;; + --n | --no | --no- | --no-t | --no-ti | --no-tim | --no-time | --no-timer) + notimer=yes + ;; + -m | --m | --mm | --mma | --mmap) + tracemmap=yes + ;; + -t | --tim | --time | --time- | --time-b | --time-ba | --time-bas | --time-base | --time-based) + memusagestat_args="$memusagestat_args -t" + ;; + -T | --to | --tot | --tota | --total) + memusagestat_args="$memusagestat_args -T" + ;; + --tit | --titl | --title) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -s $1" + ;; + --tit=* | --titl=* | --title=*) + memusagestat_args="$memusagestat_args -s ${1##*=}" + ;; + -x | --x | --x- | --x-s | --x-si | --x-siz | --x-size) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -x $1" + ;; + --x=* | --x-=* | --x-s=* | --x-si=* | --x-siz=* | --x-size=*) + memusagestat_args="$memusagestat_args -x ${1##*=}" + ;; + -y | --y | --y- | --y-s | --y-si | --y-siz | --y-size) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -y $1" + ;; + --y=* | --y-=* | --y-s=* | --y-si=* | --y-siz=* | --y-size=*) + memusagestat_args="$memusagestat_args -y ${1##*=}" + ;; + --p | --p=* | --t | --t=* | --ti | --ti=* | --u) + echo >&2 $"memusage: option \`${1##*=}' is ambiguous" + do_usage + ;; + --) + # Stop processing arguments. + shift + break + ;; + --*) + echo >&2 $"memusage: unrecognized option \`$1'" + do_usage + ;; + *) + # Unknown option. This means the rest is the program name and parameters. + break + ;; + esac + shift +done + +# See whether any arguments are left. +if test $# -eq 0; then + echo >&2 $"No program name given" + do_usage +fi + +# This will be in the environment. +add_env="LD_PRELOAD=$memusageso" + +# Generate data file name. +datafile= +if test -n "$data"; then + datafile="$data" +elif test -n "$png"; then + datafile=$(mktemp -t memusage.XXXXXX) || exit + trap 'rm -f "$datafile"; exit 1' HUP INT QUIT TERM PIPE +fi +if test -n "$datafile"; then + add_env="$add_env MEMUSAGE_OUTPUT=$datafile" +fi + +# Set program name. +if test -n "$progname"; then + add_env="$add_env MEMUSAGE_PROG_NAME=$progname" +fi + +# Set buffer size. +if test -n "$buffer"; then + add_env="$add_env MEMUSAGE_BUFFER_SIZE=$buffer" +fi + +# Disable timers. +if test -n "$notimer"; then + add_env="$add_env MEMUSAGE_NO_TIMER=yes" +fi + +# Trace mmap. +if test -n "$tracemmap"; then + add_env="$add_env MEMUSAGE_TRACE_MMAP=yes" +fi + +# Execute the program itself. +eval $add_env '"$@"' +result=$? + +# Generate the PNG data file if wanted and there is something to generate +# it from. +if test -n "$png" -a -n "$datafile" -a -s "$datafile"; then + # Append extension .png if it isn't already there. + case $png in + *.png) ;; + *) png="$png.png" ;; + esac + $memusagestat $memusagestat_args "$datafile" "$png" +fi + +if test -z "$data" -a -n "$datafile"; then + rm -f "$datafile" +fi + +exit $result +# Local Variables: +# mode:ksh +# End: diff --git a/REORG.TODO/malloc/memusagestat.c b/REORG.TODO/malloc/memusagestat.c new file mode 100644 index 0000000000..0bd158bd1e --- /dev/null +++ b/REORG.TODO/malloc/memusagestat.c @@ -0,0 +1,587 @@ +/* Generate graphic from memory profiling data. + Copyright (C) 1998-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#define _FILE_OFFSET_BITS 64 + +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <gd.h> +#include <gdfontl.h> +#include <gdfonts.h> + +#include "../version.h" +#define PACKAGE _libc_intl_domainname + +/* Default size of the generated image. */ +#define XSIZE 800 +#define YSIZE 600 + +#ifndef N_ +# define N_(Arg) Arg +#endif + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { "output", 'o', N_ ("FILE"), 0, N_ ("Name output file") }, + { "string", 's', N_ ("STRING"), 0, N_ ("Title string used in output graphic") }, + { "time", 't', NULL, 0, N_ ("\ +Generate output linear to time (default is linear to number of function calls)\ +") }, + { "total", 'T', NULL, 0, + N_ ("Also draw graph for total memory consumption") }, + { "x-size", 'x', N_ ("VALUE"), 0, + N_ ("Make output graphic VALUE pixels wide") }, + { "y-size", 'y', "VALUE", 0, N_ ("Make output graphic VALUE pixels high") }, + { NULL, 0, NULL, 0, NULL } +}; + +/* Short description of program. */ +static const char doc[] = N_ ("Generate graphic from memory profiling data"); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_ ("DATAFILE [OUTFILE]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Function to print some extra text in the help message. */ +static char *more_help (int key, const char *text, void *input); + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, more_help +}; + + +struct entry +{ + uint64_t heap; + uint64_t stack; + uint32_t time_low; + uint32_t time_high; +}; + + +/* Size of the image. */ +static size_t xsize; +static size_t ysize; + +/* Name of the output file. */ +static char *outname; + +/* Title string for the graphic. */ +static const char *string; + +/* Nonzero if graph should be generated linear in time. */ +static int time_based; + +/* Nonzero if graph to display total use of memory should be drawn as well. */ +static int also_total = 0; + + +int +main (int argc, char *argv[]) +{ + int remaining; + const char *inname; + gdImagePtr im_out; + int grey, blue, red, green, yellow, black; + int fd; + struct stat st; + size_t maxsize_heap; + size_t maxsize_stack; + size_t maxsize_total; + uint64_t total; + uint64_t cnt, cnt2; + FILE *outfile; + char buf[30]; + size_t last_heap; + size_t last_stack; + size_t last_total; + struct entry headent[2]; + uint64_t start_time; + uint64_t end_time; + uint64_t total_time; + const char *heap_format, *stack_format; + int heap_scale, stack_scale, line; + + outname = NULL; + xsize = XSIZE; + ysize = YSIZE; + string = NULL; + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + if (remaining >= argc || remaining + 2 < argc) + { + argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } + + inname = argv[remaining++]; + + if (remaining < argc) + outname = argv[remaining]; + else if (outname == NULL) + { + size_t len = strlen (inname); + outname = alloca (len + 5); + stpcpy (stpcpy (outname, inname), ".png"); + } + + /* Open for read/write since we try to repair the file in case the + application hasn't terminated cleanly. */ + fd = open (inname, O_RDWR); + if (fd == -1) + error (EXIT_FAILURE, errno, "cannot open input file"); + if (fstat (fd, &st) != 0) + { + close (fd); + error (EXIT_FAILURE, errno, "cannot get size of input file"); + } + /* Test whether the file contains only full records. */ + if ((st.st_size % sizeof (struct entry)) != 0 + /* The file must at least contain the two administrative records. */ + || st.st_size < 2 * sizeof (struct entry)) + { + close (fd); + error (EXIT_FAILURE, 0, "input file has incorrect size"); + } + /* Compute number of data entries. */ + total = st.st_size / sizeof (struct entry) - 2; + + /* Read the administrative information. */ + read (fd, headent, sizeof (headent)); + maxsize_heap = headent[1].heap; + maxsize_stack = headent[1].stack; + maxsize_total = headent[0].stack; + + if (maxsize_heap == 0 && maxsize_stack == 0) + { + /* The program aborted before memusage was able to write the + information about the maximum heap and stack use. Repair + the file now. */ + struct entry next; + + while (1) + { + if (read (fd, &next, sizeof (next)) == 0) + break; + if (next.heap > maxsize_heap) + maxsize_heap = next.heap; + if (next.stack > maxsize_stack) + maxsize_stack = next.stack; + if (maxsize_heap + maxsize_stack > maxsize_total) + maxsize_total = maxsize_heap + maxsize_stack; + } + + headent[0].stack = maxsize_total; + headent[1].heap = maxsize_heap; + headent[1].stack = maxsize_stack; + headent[1].time_low = next.time_low; + headent[1].time_high = next.time_high; + + /* Write the computed values in the file. */ + lseek (fd, 0, SEEK_SET); + write (fd, headent, 2 * sizeof (struct entry)); + } + + if (also_total) + { + /* We use one scale and since we also draw the total amount of + memory used we have to adapt the maximum. */ + maxsize_heap = maxsize_total; + maxsize_stack = maxsize_total; + } + + start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low; + end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low; + total_time = end_time - start_time; + + if (xsize < 100) + xsize = 100; + if (ysize < 80) + ysize = 80; + + /* Create output image with the specified size. */ + im_out = gdImageCreate (xsize, ysize); + + /* First color allocated is background. */ + grey = gdImageColorAllocate (im_out, 224, 224, 224); + + /* Set transparent color. */ + gdImageColorTransparent (im_out, grey); + + /* These are all the other colors we need (in the moment). */ + red = gdImageColorAllocate (im_out, 255, 0, 0); + green = gdImageColorAllocate (im_out, 0, 130, 0); + blue = gdImageColorAllocate (im_out, 0, 0, 255); + yellow = gdImageColorAllocate (im_out, 154, 205, 50); + black = gdImageColorAllocate (im_out, 0, 0, 0); + + gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue); + + if (maxsize_heap < 1024) + { + heap_format = "%Zu"; + heap_scale = 1; + } + else if (maxsize_heap < 1024 * 1024 * 100) + { + heap_format = "%Zuk"; + heap_scale = 1024; + } + else + { + heap_format = "%ZuM"; + heap_scale = 1024 * 1024; + } + + if (maxsize_stack < 1024) + { + stack_format = "%Zu"; + stack_scale = 1; + } + else if (maxsize_stack < 1024 * 1024 * 100) + { + stack_format = "%Zuk"; + stack_scale = 1024; + } + else + { + stack_format = "%ZuM"; + stack_scale = 1024 * 1024; + } + + gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0", + blue); + snprintf (buf, sizeof (buf), heap_format, 0); + gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26, + ysize - 26, (unsigned char *) buf, red); + snprintf (buf, sizeof (buf), stack_format, 0); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26, + (unsigned char *) buf, green); + + if (string != NULL) + gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2, + 2, (unsigned char *) string, green); + + gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10, + (unsigned char *) "allocated", red); + gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10, + (unsigned char *) "memory", red); + + gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10, + (unsigned char *) "used", green); + gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10, + (unsigned char *) "stack", green); + + snprintf (buf, sizeof (buf), heap_format, maxsize_heap / heap_scale); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, + (unsigned char *) buf, red); + snprintf (buf, sizeof (buf), stack_format, maxsize_stack / stack_scale); + gdImageString (im_out, gdFontSmall, xsize - 37, 14, + (unsigned char *) buf, green); + + for (line = 1; line <= 3; ++line) + { + if (maxsize_heap > 0) + { + cnt = (((ysize - 40) * (maxsize_heap / 4 * line / heap_scale)) + / (maxsize_heap / heap_scale)); + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), heap_format, + maxsize_heap / 4 * line / heap_scale); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, (unsigned char *) buf, red); + } + else + cnt = 0; + + if (maxsize_stack > 0) + cnt2 = (((ysize - 40) * (maxsize_stack / 4 * line / stack_scale)) + / (maxsize_stack / stack_scale)); + else + cnt2 = 0; + + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), stack_format, maxsize_stack / 4 * line / + stack_scale); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + (unsigned char *) buf, green); + } + + snprintf (buf, sizeof (buf), "%llu", (unsigned long long) total); + gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14, + (unsigned char *) buf, blue); + + if (!time_based) + { + uint64_t previously = start_time; + + gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2, + ysize - 12, + (unsigned char *) "# memory handling function calls", + blue); + + + last_stack = last_heap = last_total = ysize - 20; + for (cnt = 1; cnt <= total; ++cnt) + { + struct entry entry; + size_t new[2]; + uint64_t now; + + read (fd, &entry, sizeof (entry)); + + now = ((uint64_t) entry.time_high) << 32 | entry.time_low; + + if ((((previously - start_time) * 100) / total_time) % 10 < 5) + gdImageFilledRectangle (im_out, + 40 + ((cnt - 1) * (xsize - 80)) / total, + ysize - 19, + 39 + (cnt * (xsize - 80)) / total, + ysize - 14, yellow); + previously = now; + + if (also_total && maxsize_heap > 0) + { + size_t new3; + + new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * (entry.heap + entry.stack)) + / maxsize_heap); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_total, + 40 + ((xsize - 80) * cnt) / total, new3, + black); + last_total = new3; + } + + if (maxsize_heap > 0) + { + new[0] = ((ysize - 20) + - ((((unsigned long long int) (ysize - 40)) + * entry.heap) / maxsize_heap)); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_heap, 40 + ((xsize - 80) * cnt) / total, + new[0], red); + last_heap = new[0]; + } + + if (maxsize_stack > 0) + { + new[1] = ((ysize - 20) + - ((((unsigned long long int) (ysize - 40)) + * entry.stack) / maxsize_stack)); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_stack, 40 + ((xsize - 80) * cnt) / total, + new[1], green); + last_stack = new[1]; + } + } + + cnt = 0; + while (cnt < total) + { + gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20, + 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue); + cnt += MAX (1, total / 20); + } + gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15, + blue); + } + else + { + uint64_t next_tick = MAX (1, total / 20); + size_t last_xpos = 40; + + gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2, + ysize - 12, + (unsigned char *) " \ +# memory handling function calls / time", blue); + + for (cnt = 0; cnt < 20; cnt += 2) + gdImageFilledRectangle (im_out, + 40 + (cnt * (xsize - 80)) / 20, ysize - 19, + 39 + ((cnt + 1) * (xsize - 80)) / 20, + ysize - 14, yellow); + + last_stack = last_heap = last_total = ysize - 20; + for (cnt = 1; cnt <= total; ++cnt) + { + struct entry entry; + size_t new[2]; + size_t xpos; + uint64_t now; + + read (fd, &entry, sizeof (entry)); + + now = ((uint64_t) entry.time_high) << 32 | entry.time_low; + xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time; + + if (cnt == next_tick) + { + gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue); + next_tick += MAX (1, total / 20); + } + + if (also_total && maxsize_heap > 0) + { + size_t new3; + + new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * (entry.heap + entry.stack)) + / maxsize_heap); + gdImageLine (im_out, last_xpos, last_total, xpos, new3, black); + last_total = new3; + } + + if (maxsize_heap > 0) + { + new[0] = ((ysize - 20) + - ((((unsigned long long int) (ysize - 40)) + * entry.heap) / maxsize_heap)); + gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red); + last_heap = new[0]; + } + + if (maxsize_stack > 0) + { + new[1] = ((ysize - 20) + - ((((unsigned long long int) (ysize - 40)) + * entry.stack) / maxsize_stack)); + gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], + green); + last_stack = new[1]; + } + + last_xpos = xpos; + } + } + + /* Write out the result. */ + outfile = fopen (outname, "w"); + if (outfile == NULL) + error (EXIT_FAILURE, errno, "cannot open output file"); + + gdImagePng (im_out, outfile); + + fclose (outfile); + + gdImageDestroy (im_out); + + return 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'o': + outname = arg; + break; + case 's': + string = arg; + break; + case 't': + time_based = 1; + break; + case 'T': + also_total = 1; + break; + case 'x': + xsize = atoi (arg); + if (xsize == 0) + xsize = XSIZE; + break; + case 'y': + ysize = atoi (arg); + if (ysize == 0) + ysize = XSIZE; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static char * +more_help (int key, const char *text, void *input) +{ + char *tp; + + switch (key) + { + case ARGP_KEY_HELP_EXTRA: + /* We print some extra information. */ + if (asprintf (&tp, gettext ("\ +For bug reporting instructions, please see:\n\ +%s.\n"), REPORT_BUGS_TO) < 0) + return NULL; + + return tp; + + default: + break; + } + return (char *) text; +} + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "memusagestat %s%s\n", PKGVERSION, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Free Software Foundation, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2017"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} diff --git a/REORG.TODO/malloc/morecore.c b/REORG.TODO/malloc/morecore.c new file mode 100644 index 0000000000..edfceda926 --- /dev/null +++ b/REORG.TODO/malloc/morecore.c @@ -0,0 +1,53 @@ +/* Copyright (C) 1991-2017 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/>. */ + +#ifndef _MALLOC_INTERNAL +# define _MALLOC_INTERNAL +# include <malloc.h> +#endif + +#ifndef __GNU_LIBRARY__ +# define __sbrk sbrk +#endif + +#ifdef __GNU_LIBRARY__ +/* It is best not to declare this and cast its result on foreign operating + systems with potentially hostile include files. */ + +# include <stddef.h> +# include <stdlib.h> +extern void *__sbrk (ptrdiff_t increment) __THROW; +libc_hidden_proto (__sbrk) +#endif + +#ifndef NULL +# define NULL 0 +#endif + +/* Allocate INCREMENT more bytes of data space, + and return the start of data space, or NULL on errors. + If INCREMENT is negative, shrink data space. */ +void * +__default_morecore (ptrdiff_t increment) +{ + void *result = (void *) __sbrk (increment); + if (result == (void *) -1) + return NULL; + + return result; +} +libc_hidden_def (__default_morecore) diff --git a/REORG.TODO/malloc/mtrace.c b/REORG.TODO/malloc/mtrace.c new file mode 100644 index 0000000000..02c53eb9fe --- /dev/null +++ b/REORG.TODO/malloc/mtrace.c @@ -0,0 +1,348 @@ +/* More debugging hooks for `malloc'. + Copyright (C) 1991-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written April 2, 1991 by John Gilmore of Cygnus Support. + Based on mcheck.c by Mike Haertel. + + 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/>. */ + +#ifndef _MALLOC_INTERNAL +# define _MALLOC_INTERNAL +# include <malloc.h> +# include <mcheck.h> +# include <libc-lock.h> +#endif + +#include <dlfcn.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <_itoa.h> + +#include <libc-internal.h> + +#include <libio/iolibio.h> +#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) +#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp) + +#include <kernel-features.h> + +#define TRACE_BUFFER_SIZE 512 + +static FILE *mallstream; +static const char mallenv[] = "MALLOC_TRACE"; +static char *malloc_trace_buffer; + +__libc_lock_define_initialized (static, lock); + +/* Address to breakpoint on accesses to... */ +__ptr_t mallwatch; + +/* Old hook values. */ +static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t); +static __ptr_t (*tr_old_malloc_hook) (size_t size, const __ptr_t); +static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, size_t size, + const __ptr_t); +static __ptr_t (*tr_old_memalign_hook) (size_t __alignment, size_t __size, + const __ptr_t); + +/* This function is called when the block being alloc'd, realloc'd, or + freed has an address matching the variable "mallwatch". In a debugger, + set "mallwatch" to the address of interest, then put a breakpoint on + tr_break. */ + +extern void tr_break (void) __THROW; +libc_hidden_proto (tr_break) +void +tr_break (void) +{ +} +libc_hidden_def (tr_break) + +static void internal_function +tr_where (const __ptr_t caller, Dl_info *info) +{ + if (caller != NULL) + { + if (info != NULL) + { + char *buf = (char *) ""; + if (info->dli_sname != NULL) + { + size_t len = strlen (info->dli_sname); + buf = alloca (len + 6 + 2 * sizeof (void *)); + + buf[0] = '('; + __stpcpy (_fitoa (caller >= (const __ptr_t) info->dli_saddr + ? caller - (const __ptr_t) info->dli_saddr + : (const __ptr_t) info->dli_saddr - caller, + __stpcpy (__mempcpy (buf + 1, info->dli_sname, + len), + caller >= (__ptr_t) info->dli_saddr + ? "+0x" : "-0x"), + 16, 0), + ")"); + } + + fprintf (mallstream, "@ %s%s%s[%p] ", + info->dli_fname ? : "", info->dli_fname ? ":" : "", + buf, caller); + } + else + fprintf (mallstream, "@ [%p] ", caller); + } +} + +static Dl_info * +lock_and_info (const __ptr_t caller, Dl_info *mem) +{ + if (caller == NULL) + return NULL; + + Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL; + + __libc_lock_lock (lock); + + return res; +} + +static void +tr_freehook (__ptr_t ptr, const __ptr_t caller) +{ + if (ptr == NULL) + return; + + Dl_info mem; + Dl_info *info = lock_and_info (caller, &mem); + tr_where (caller, info); + /* Be sure to print it first. */ + fprintf (mallstream, "- %p\n", ptr); + if (ptr == mallwatch) + { + __libc_lock_unlock (lock); + tr_break (); + __libc_lock_lock (lock); + } + __free_hook = tr_old_free_hook; + if (tr_old_free_hook != NULL) + (*tr_old_free_hook)(ptr, caller); + else + free (ptr); + __free_hook = tr_freehook; + __libc_lock_unlock (lock); +} + +static __ptr_t +tr_mallochook (size_t size, const __ptr_t caller) +{ + __ptr_t hdr; + + Dl_info mem; + Dl_info *info = lock_and_info (caller, &mem); + + __malloc_hook = tr_old_malloc_hook; + if (tr_old_malloc_hook != NULL) + hdr = (__ptr_t) (*tr_old_malloc_hook)(size, caller); + else + hdr = (__ptr_t) malloc (size); + __malloc_hook = tr_mallochook; + + tr_where (caller, info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); + + __libc_lock_unlock (lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +static __ptr_t +tr_reallochook (__ptr_t ptr, size_t size, const __ptr_t caller) +{ + __ptr_t hdr; + + if (ptr == mallwatch) + tr_break (); + + Dl_info mem; + Dl_info *info = lock_and_info (caller, &mem); + + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + if (tr_old_realloc_hook != NULL) + hdr = (__ptr_t) (*tr_old_realloc_hook)(ptr, size, caller); + else + hdr = (__ptr_t) realloc (ptr, size); + __free_hook = tr_freehook; + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + + tr_where (caller, info); + if (hdr == NULL) + { + if (size != 0) + /* Failed realloc. */ + fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size); + else + fprintf (mallstream, "- %p\n", ptr); + } + else if (ptr == NULL) + fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); + else + { + fprintf (mallstream, "< %p\n", ptr); + tr_where (caller, info); + fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size); + } + + __libc_lock_unlock (lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + +static __ptr_t +tr_memalignhook (size_t alignment, size_t size, const __ptr_t caller) +{ + __ptr_t hdr; + + Dl_info mem; + Dl_info *info = lock_and_info (caller, &mem); + + __memalign_hook = tr_old_memalign_hook; + __malloc_hook = tr_old_malloc_hook; + if (tr_old_memalign_hook != NULL) + hdr = (__ptr_t) (*tr_old_memalign_hook)(alignment, size, caller); + else + hdr = (__ptr_t) memalign (alignment, size); + __memalign_hook = tr_memalignhook; + __malloc_hook = tr_mallochook; + + tr_where (caller, info); + /* We could be printing a NULL here; that's OK. */ + fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size); + + __libc_lock_unlock (lock); + + if (hdr == mallwatch) + tr_break (); + + return hdr; +} + + +#ifdef _LIBC + +/* This function gets called to make sure all memory the library + allocates get freed and so does not irritate the user when studying + the mtrace output. */ +static void __libc_freeres_fn_section +release_libc_mem (void) +{ + /* Only call the free function if we still are running in mtrace mode. */ + if (mallstream != NULL) + __libc_freeres (); +} +#endif + + +/* We enable tracing if either the environment variable MALLOC_TRACE + is set, or if the variable mallwatch has been patched to an address + that the debugging user wants us to stop on. When patching mallwatch, + don't forget to set a breakpoint on tr_break! */ + +void +mtrace (void) +{ +#ifdef _LIBC + static int added_atexit_handler; +#endif + char *mallfile; + + /* Don't panic if we're called more than once. */ + if (mallstream != NULL) + return; + +#ifdef _LIBC + /* When compiling the GNU libc we use the secure getenv function + which prevents the misuse in case of SUID or SGID enabled + programs. */ + mallfile = __libc_secure_getenv (mallenv); +#else + mallfile = getenv (mallenv); +#endif + if (mallfile != NULL || mallwatch != NULL) + { + char *mtb = malloc (TRACE_BUFFER_SIZE); + if (mtb == NULL) + return; + + mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce"); + if (mallstream != NULL) + { + /* Be sure it doesn't malloc its buffer! */ + malloc_trace_buffer = mtb; + setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); + fprintf (mallstream, "= Start\n"); + tr_old_free_hook = __free_hook; + __free_hook = tr_freehook; + tr_old_malloc_hook = __malloc_hook; + __malloc_hook = tr_mallochook; + tr_old_realloc_hook = __realloc_hook; + __realloc_hook = tr_reallochook; + tr_old_memalign_hook = __memalign_hook; + __memalign_hook = tr_memalignhook; +#ifdef _LIBC + if (!added_atexit_handler) + { + extern void *__dso_handle __attribute__ ((__weak__)); + added_atexit_handler = 1; + __cxa_atexit ((void (*)(void *))release_libc_mem, NULL, + &__dso_handle ? __dso_handle : NULL); + } +#endif + } + else + free (mtb); + } +} + +void +muntrace (void) +{ + if (mallstream == NULL) + return; + + /* Do the reverse of what done in mtrace: first reset the hooks and + MALLSTREAM, and only after that write the trailer and close the + file. */ + FILE *f = mallstream; + mallstream = NULL; + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + __memalign_hook = tr_old_memalign_hook; + + fprintf (f, "= End\n"); + fclose (f); +} diff --git a/REORG.TODO/malloc/mtrace.pl b/REORG.TODO/malloc/mtrace.pl new file mode 100644 index 0000000000..4acbd81680 --- /dev/null +++ b/REORG.TODO/malloc/mtrace.pl @@ -0,0 +1,237 @@ +#! @PERL@ +eval "exec @PERL@ -S $0 $@" + if 0; +# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# Contributed by Ulrich Drepper <drepper@gnu.org>, 1997. +# Based on the mtrace.awk script. + +# 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/>. + +$VERSION = "@VERSION@"; +$PKGVERSION = "@PKGVERSION@"; +$REPORT_BUGS_TO = '@REPORT_BUGS_TO@'; +$progname = $0; + +sub usage { + print "Usage: mtrace [OPTION]... [Binary] MtraceData\n"; + print " --help print this help, then exit\n"; + print " --version print version number, then exit\n"; + print "\n"; + print "For bug reporting instructions, please see:\n"; + print "$REPORT_BUGS_TO.\n"; + exit 0; +} + +# We expect two arguments: +# #1: the complete path to the binary +# #2: the mtrace data filename +# The usual options are also recognized. + +arglist: while (@ARGV) { + if ($ARGV[0] eq "--v" || $ARGV[0] eq "--ve" || $ARGV[0] eq "--ver" || + $ARGV[0] eq "--vers" || $ARGV[0] eq "--versi" || + $ARGV[0] eq "--versio" || $ARGV[0] eq "--version") { + print "mtrace $PKGVERSION$VERSION\n"; + print "Copyright (C) 2017 Free Software Foundation, Inc.\n"; + print "This is free software; see the source for copying conditions. There is NO\n"; + print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; + print "Written by Ulrich Drepper <drepper\@gnu.org>\n"; + + exit 0; + } elsif ($ARGV[0] eq "--h" || $ARGV[0] eq "--he" || $ARGV[0] eq "--hel" || + $ARGV[0] eq "--help") { + &usage; + } elsif ($ARGV[0] =~ /^-/) { + print "$progname: unrecognized option `$ARGV[0]'\n"; + print "Try `$progname --help' for more information.\n"; + exit 1; + } else { + last arglist; + } +} + +if ($#ARGV == 0) { + $binary=""; + $data=$ARGV[0]; +} elsif ($#ARGV == 1) { + $binary=$ARGV[0]; + $data=$ARGV[1]; + + if ($binary =~ /^.*[\/].*$/) { + $prog = $binary; + } else { + $prog = "./$binary"; + } + if (open (LOCS, "env LD_TRACE_LOADED_OBJECTS=1 $prog |")) { + while (<LOCS>) { + chop; + if (/^.*=> (.*) .(0x[0123456789abcdef]*).$/) { + $locs{$1} = $2; + } + } + close (LOCS); + } +} else { + die "Wrong number of arguments, run $progname --help for help."; +} + +sub location { + my $str = pop(@_); + return $str if ($str eq ""); + if ($str =~ /.*[[](0x[^]]*)]:(.)*/) { + my $addr = $1; + my $fct = $2; + return $cache{$addr} if (exists $cache{$addr}); + if ($binary ne "" && open (ADDR, "addr2line -e $binary $addr|")) { + my $line = <ADDR>; + chomp $line; + close (ADDR); + if ($line ne '??:0') { + $cache{$addr} = $line; + return $cache{$addr}; + } + } + $cache{$addr} = $str = "$fct @ $addr"; + } elsif ($str =~ /^(.*):.*[[](0x[^]]*)]$/) { + my $prog = $1; + my $addr = $2; + my $searchaddr; + return $cache{$addr} if (exists $cache{$addr}); + if ($locs{$prog} ne "") { + $searchaddr = sprintf "%#x", $addr - $locs{$prog}; + } else { + $searchaddr = $addr; + $prog = $binary; + } + if ($binary ne "" && open (ADDR, "addr2line -e $prog $searchaddr|")) { + my $line = <ADDR>; + chomp $line; + close (ADDR); + if ($line ne '??:0') { + $cache{$addr} = $line; + return $cache{$addr}; + } + } + $cache{$addr} = $str = $addr; + } elsif ($str =~ /^.*[[](0x[^]]*)]$/) { + my $addr = $1; + return $cache{$addr} if (exists $cache{$addr}); + if ($binary ne "" && open (ADDR, "addr2line -e $binary $addr|")) { + my $line = <ADDR>; + chomp $line; + close (ADDR); + if ($line ne '??:0') { + $cache{$addr} = $line; + return $cache{$addr}; + } + } + $cache{$addr} = $str = $addr; + } + return $str; +} + +$nr=0; +open(DATA, "<$data") || die "Cannot open mtrace data file"; +while (<DATA>) { + my @cols = split (' '); + my $n, $where; + if ($cols[0] eq "@") { + # We have address and/or function name. + $where=$cols[1]; + $n=2; + } else { + $where=""; + $n=0; + } + + $allocaddr=$cols[$n + 1]; + $howmuch=hex($cols[$n + 2]); + + ++$nr; + SWITCH: { + if ($cols[$n] eq "+") { + if (defined $allocated{$allocaddr}) { + printf ("+ %#0@XXX@x Alloc %d duplicate: %s %s\n", + hex($allocaddr), $nr, &location($addrwas{$allocaddr}), + $where); + } elsif ($allocaddr =~ /^0x/) { + $allocated{$allocaddr}=$howmuch; + $addrwas{$allocaddr}=$where; + } + last SWITCH; + } + if ($cols[$n] eq "-") { + if (defined $allocated{$allocaddr}) { + undef $allocated{$allocaddr}; + undef $addrwas{$allocaddr}; + } else { + printf ("- %#0@XXX@x Free %d was never alloc'd %s\n", + hex($allocaddr), $nr, &location($where)); + } + last SWITCH; + } + if ($cols[$n] eq "<") { + if (defined $allocated{$allocaddr}) { + undef $allocated{$allocaddr}; + undef $addrwas{$allocaddr}; + } else { + printf ("- %#0@XXX@x Realloc %d was never alloc'd %s\n", + hex($allocaddr), $nr, &location($where)); + } + last SWITCH; + } + if ($cols[$n] eq ">") { + if (defined $allocated{$allocaddr}) { + printf ("+ %#0@XXX@x Realloc %d duplicate: %#010x %s %s\n", + hex($allocaddr), $nr, $allocated{$allocaddr}, + &location($addrwas{$allocaddr}), &location($where)); + } else { + $allocated{$allocaddr}=$howmuch; + $addrwas{$allocaddr}=$where; + } + last SWITCH; + } + if ($cols[$n] eq "=") { + # Ignore "= Start". + last SWITCH; + } + if ($cols[$n] eq "!") { + # Ignore failed realloc for now. + last SWITCH; + } + } +} +close (DATA); + +# Now print all remaining entries. +@addrs= keys %allocated; +$anything=0; +if ($#addrs >= 0) { + foreach $addr (sort @addrs) { + if (defined $allocated{$addr}) { + if ($anything == 0) { + print "\nMemory not freed:\n-----------------\n"; + print ' ' x (@XXX@ - 7), "Address Size Caller\n"; + $anything=1; + } + printf ("%#0@XXX@x %#8x at %s\n", hex($addr), $allocated{$addr}, + &location($addrwas{$addr})); + } + } +} +print "No memory leaks.\n" if ($anything == 0); + +exit $anything != 0; diff --git a/REORG.TODO/malloc/obstack.c b/REORG.TODO/malloc/obstack.c new file mode 100644 index 0000000000..4ac8938c73 --- /dev/null +++ b/REORG.TODO/malloc/obstack.c @@ -0,0 +1,423 @@ +/* obstack.c - subroutines used implicitly by object stack macros + Copyright (C) 1988-2017 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/>. */ + + +#ifdef _LIBC +# include <obstack.h> +# include <shlib-compat.h> +#else +# include <config.h> +# include "obstack.h" +#endif + +/* NOTE BEFORE MODIFYING THIS FILE: This version number must be + incremented whenever callers compiled using an old obstack.h can no + longer properly call the functions in this obstack.c. */ +#define OBSTACK_INTERFACE_VERSION 1 + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself, and the installed library + supports the same library interface we do. This code is part of the GNU + C Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand 'configure --with-gnu-libc' and omit the object + files, it is simpler to just do this in the source for each such file. */ + +#include <stdio.h> /* Random thing to get __GNU_LIBRARY__. */ +#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1 +# include <gnu-versions.h> +# if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#include <stddef.h> + +#ifndef ELIDE_CODE + + +# include <stdint.h> + +/* Determine default alignment. */ +union fooround +{ + uintmax_t i; + long double d; + void *p; +}; +struct fooalign +{ + char c; + union fooround u; +}; +/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT. + But in fact it might be less smart and round addresses to as much as + DEFAULT_ROUNDING. So we prepare for it to do that. */ +enum +{ + DEFAULT_ALIGNMENT = offsetof (struct fooalign, u), + DEFAULT_ROUNDING = sizeof (union fooround) +}; + +/* When we copy a long block of data, this is the unit to do it with. + On some machines, copying successive ints does not work; + in such a case, redefine COPYING_UNIT to 'long' (if that works) + or 'char' as a last resort. */ +# ifndef COPYING_UNIT +# define COPYING_UNIT int +# endif + + +/* The functions allocating more room by calling 'obstack_chunk_alloc' + jump to the handler pointed to by 'obstack_alloc_failed_handler'. + This can be set to a user defined function which should either + abort gracefully or use longjump - but shouldn't return. This + variable by default points to the internal function + 'print_and_abort'. */ +static _Noreturn void print_and_abort (void); +void (*obstack_alloc_failed_handler) (void) = print_and_abort; + +/* Exit value used when 'print_and_abort' is used. */ +# include <stdlib.h> +# ifdef _LIBC +int obstack_exit_failure = EXIT_FAILURE; +# else +# include "exitfail.h" +# define obstack_exit_failure exit_failure +# endif + +# ifdef _LIBC +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +/* A looong time ago (before 1994, anyway; we're not sure) this global variable + was used by non-GNU-C macros to avoid multiple evaluation. The GNU C + library still exports it because somebody might use it. */ +struct obstack *_obstack_compat = 0; +compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0); +# endif +# endif + +/* Define a macro that either calls functions with the traditional malloc/free + calling interface, or calls functions with the mmalloc/mfree interface + (that adds an extra first argument), based on the state of use_extra_arg. + For free, do not use ?:, since some compilers, like the MIPS compilers, + do not allow (expr) ? void : void. */ + +# define CALL_CHUNKFUN(h, size) \ + (((h)->use_extra_arg) \ + ? (*(h)->chunkfun)((h)->extra_arg, (size)) \ + : (*(struct _obstack_chunk *(*)(long))(h)->chunkfun)((size))) + +# define CALL_FREEFUN(h, old_chunk) \ + do { \ + if ((h)->use_extra_arg) \ + (*(h)->freefun)((h)->extra_arg, (old_chunk)); \ + else \ + (*(void (*)(void *))(h)->freefun)((old_chunk)); \ + } while (0) + + +/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default). + Objects start on multiples of ALIGNMENT (0 means use default). + CHUNKFUN is the function to use to allocate chunks, + and FREEFUN the function to free them. + + Return nonzero if successful, calls obstack_alloc_failed_handler if + allocation fails. */ + +int +_obstack_begin (struct obstack *h, + int size, int alignment, + void *(*chunkfun) (long), + void (*freefun) (void *)) +{ + struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*) (void *, long)) chunkfun; + h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->use_extra_arg = 0; + + chunk = h->chunk = CALL_CHUNKFUN (h, h->chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +int +_obstack_begin_1 (struct obstack *h, int size, int alignment, + void *(*chunkfun) (void *, long), + void (*freefun) (void *, void *), + void *arg) +{ + struct _obstack_chunk *chunk; /* points to new chunk */ + + if (alignment == 0) + alignment = DEFAULT_ALIGNMENT; + if (size == 0) + /* Default size is what GNU malloc can fit in a 4096-byte block. */ + { + /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc. + Use the values for range checking, because if range checking is off, + the extra bytes won't be missed terribly, but if range checking is on + and we used a larger request, a whole extra 4096 bytes would be + allocated. + + These number are irrelevant to the new GNU malloc. I suspect it is + less sensitive to the size of the request. */ + int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1)) + + 4 + DEFAULT_ROUNDING - 1) + & ~(DEFAULT_ROUNDING - 1)); + size = 4096 - extra; + } + + h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun; + h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun; + h->chunk_size = size; + h->alignment_mask = alignment - 1; + h->extra_arg = arg; + h->use_extra_arg = 1; + + chunk = h->chunk = CALL_CHUNKFUN (h, h->chunk_size); + if (!chunk) + (*obstack_alloc_failed_handler) (); + h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents, + alignment - 1); + h->chunk_limit = chunk->limit + = (char *) chunk + h->chunk_size; + chunk->prev = 0; + /* The initial chunk now contains no empty object. */ + h->maybe_empty_object = 0; + h->alloc_failed = 0; + return 1; +} + +/* Allocate a new current chunk for the obstack *H + on the assumption that LENGTH bytes need to be added + to the current object, or a new object of length LENGTH allocated. + Copies any partial object from the end of the old chunk + to the beginning of the new one. */ + +void +_obstack_newchunk (struct obstack *h, int length) +{ + struct _obstack_chunk *old_chunk = h->chunk; + struct _obstack_chunk *new_chunk; + long new_size; + long obj_size = h->next_free - h->object_base; + long i; + long already; + char *object_base; + + /* Compute size for new chunk. */ + new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100; + if (new_size < h->chunk_size) + new_size = h->chunk_size; + + /* Allocate and initialize the new chunk. */ + new_chunk = CALL_CHUNKFUN (h, new_size); + if (!new_chunk) + (*obstack_alloc_failed_handler)(); + h->chunk = new_chunk; + new_chunk->prev = old_chunk; + new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size; + + /* Compute an aligned object_base in the new chunk */ + object_base = + __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask); + + /* Move the existing object to the new chunk. + Word at a time is fast and is safe if the object + is sufficiently aligned. */ + if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT) + { + for (i = obj_size / sizeof (COPYING_UNIT) - 1; + i >= 0; i--) + ((COPYING_UNIT *) object_base)[i] + = ((COPYING_UNIT *) h->object_base)[i]; + /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT, + but that can cross a page boundary on a machine + which does not do strict alignment for COPYING_UNITS. */ + already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT); + } + else + already = 0; + /* Copy remaining bytes one by one. */ + for (i = already; i < obj_size; i++) + object_base[i] = h->object_base[i]; + + /* If the object just copied was the only data in OLD_CHUNK, + free that chunk and remove it from the chain. + But not if that chunk might contain an empty object. */ + if (!h->maybe_empty_object + && (h->object_base + == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents, + h->alignment_mask))) + { + new_chunk->prev = old_chunk->prev; + CALL_FREEFUN (h, old_chunk); + } + + h->object_base = object_base; + h->next_free = h->object_base + obj_size; + /* The new chunk certainly contains no empty object yet. */ + h->maybe_empty_object = 0; +} +# ifdef _LIBC +libc_hidden_def (_obstack_newchunk) +# endif + +/* Return nonzero if object OBJ has been allocated from obstack H. + This is here for debugging. + If you use it in a program, you are probably losing. */ + +/* Suppress -Wmissing-prototypes warning. We don't want to declare this in + obstack.h because it is just for debugging. */ +int _obstack_allocated_p (struct obstack *h, void *obj) __attribute_pure__; + +int +_obstack_allocated_p (struct obstack *h, void *obj) +{ + struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = (h)->chunk; + /* We use >= rather than > since the object cannot be exactly at + the beginning of the chunk but might be an empty object exactly + at the end of an adjacent chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + lp = plp; + } + return lp != 0; +} + +/* Free objects in obstack H, including OBJ and everything allocate + more recently than OBJ. If OBJ is zero, free everything in H. */ + +# undef obstack_free + +void +__obstack_free (struct obstack *h, void *obj) +{ + struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ + struct _obstack_chunk *plp; /* point to previous chunk if any */ + + lp = h->chunk; + /* We use >= because there cannot be an object at the beginning of a chunk. + But there can be an empty object at that address + at the end of another chunk. */ + while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) + { + plp = lp->prev; + CALL_FREEFUN (h, lp); + lp = plp; + /* If we switch chunks, we can't tell whether the new current + chunk contains an empty object, so assume that it may. */ + h->maybe_empty_object = 1; + } + if (lp) + { + h->object_base = h->next_free = (char *) (obj); + h->chunk_limit = lp->limit; + h->chunk = lp; + } + else if (obj != 0) + /* obj is not in any of the chunks! */ + abort (); +} + +# ifdef _LIBC +/* Older versions of libc used a function _obstack_free intended to be + called by non-GCC compilers. */ +strong_alias (obstack_free, _obstack_free) +# endif + +int +_obstack_memory_used (struct obstack *h) +{ + struct _obstack_chunk *lp; + int nbytes = 0; + + for (lp = h->chunk; lp != 0; lp = lp->prev) + { + nbytes += lp->limit - (char *) lp; + } + return nbytes; +} + +/* Define the error handler. */ +# ifdef _LIBC +# include <libintl.h> +# else +# include "gettext.h" +# endif +# ifndef _ +# define _(msgid) gettext (msgid) +# endif + +# ifdef _LIBC +# include <libio/iolibio.h> +# endif + +static _Noreturn void +print_and_abort (void) +{ + /* Don't change any of these strings. Yes, it would be possible to add + the newline to the string and use fputs or so. But this must not + happen because the "memory exhausted" message appears in other places + like this and the translation should be reused instead of creating + a very similar string which requires a separate translation. */ +# ifdef _LIBC + (void) __fxprintf (NULL, "%s\n", _("memory exhausted")); +# else + fprintf (stderr, "%s\n", _("memory exhausted")); +# endif + exit (obstack_exit_failure); +} + +#endif /* !ELIDE_CODE */ diff --git a/REORG.TODO/malloc/obstack.h b/REORG.TODO/malloc/obstack.h new file mode 100644 index 0000000000..538837eaa6 --- /dev/null +++ b/REORG.TODO/malloc/obstack.h @@ -0,0 +1,515 @@ +/* obstack.h - object stack macros + Copyright (C) 1988-2017 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/>. */ + +/* Summary: + + All the apparent functions defined here are macros. The idea + is that you would use these pre-tested macros to solve a + very specific set of problems, and they would run fast. + Caution: no side-effects in arguments please!! They may be + evaluated MANY times!! + + These macros operate a stack of objects. Each object starts life + small, and may grow to maturity. (Consider building a word syllable + by syllable.) An object can move while it is growing. Once it has + been "finished" it never changes address again. So the "top of the + stack" is typically an immature growing object, while the rest of the + stack is of mature, fixed size and fixed address objects. + + These routines grab large chunks of memory, using a function you + supply, called 'obstack_chunk_alloc'. On occasion, they free chunks, + by calling 'obstack_chunk_free'. You must define them and declare + them before using any obstack macros. + + Each independent stack is represented by a 'struct obstack'. + Each of the obstack macros expects a pointer to such a structure + as the first argument. + + One motivation for this package is the problem of growing char strings + in symbol tables. Unless you are "fascist pig with a read-only mind" + --Gosper's immortal quote from HAKMEM item 154, out of context--you + would not like to put any arbitrary upper limit on the length of your + symbols. + + In practice this often means you will build many short symbols and a + few long symbols. At the time you are reading a symbol you don't know + how long it is. One traditional method is to read a symbol into a + buffer, realloc()ating the buffer every time you try to read a symbol + that is longer than the buffer. This is beaut, but you still will + want to copy the symbol from the buffer to a more permanent + symbol-table entry say about half the time. + + With obstacks, you can work differently. Use one obstack for all symbol + names. As you read a symbol, grow the name in the obstack gradually. + When the name is complete, finalize it. Then, if the symbol exists already, + free the newly read name. + + The way we do this is to take a large chunk, allocating memory from + low addresses. When you want to build a symbol in the chunk you just + add chars above the current "high water mark" in the chunk. When you + have finished adding chars, because you got to the end of the symbol, + you know how long the chars are, and you can create a new object. + Mostly the chars will not burst over the highest address of the chunk, + because you would typically expect a chunk to be (say) 100 times as + long as an average object. + + In case that isn't clear, when we have enough chars to make up + the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) + so we just point to it where it lies. No moving of chars is + needed and this is the second win: potentially long strings need + never be explicitly shuffled. Once an object is formed, it does not + change its address during its lifetime. + + When the chars burst over a chunk boundary, we allocate a larger + chunk, and then copy the partly formed object from the end of the old + chunk to the beginning of the new larger chunk. We then carry on + accreting characters to the end of the object as we normally would. + + A special macro is provided to add a single char at a time to a + growing object. This allows the use of register variables, which + break the ordinary 'growth' macro. + + Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can "unwind" an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) + */ + + +/* Don't do the contents of this file more than once. */ + +#ifndef _OBSTACK_H +#define _OBSTACK_H 1 + +/* We need the type of a pointer subtraction. If __PTRDIFF_TYPE__ is + defined, as with GNU C, use that; that way we don't pollute the + namespace with <stddef.h>'s symbols. Otherwise, include <stddef.h> + and use ptrdiff_t. */ + +#ifdef __PTRDIFF_TYPE__ +# define PTR_INT_TYPE __PTRDIFF_TYPE__ +#else +# include <stddef.h> +# define PTR_INT_TYPE ptrdiff_t +#endif + +/* If B is the base of an object addressed by P, return the result of + aligning P to the next multiple of A + 1. B and P must be of type + char *. A + 1 must be a power of 2. */ + +#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A))) + +/* Similar to _BPTR_ALIGN (B, P, A), except optimize the common case + where pointers can be converted to integers, aligned as integers, + and converted back again. If PTR_INT_TYPE is narrower than a + pointer (e.g., the AS/400), play it safe and compute the alignment + relative to B. Otherwise, use the faster strategy of computing the + alignment relative to 0. */ + +#define __PTR_ALIGN(B, P, A) \ + __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \ + P, A) + +#include <string.h> + +#ifndef __attribute_pure__ +# define __attribute_pure__ _GL_ATTRIBUTE_PURE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + union + { + PTR_INT_TYPE tempint; + void *tempptr; + } temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ + /* These prototypes vary based on 'use_extra_arg', and we use + casts to the prototypeless function type in all assignments, + but having prototypes here quiets -Wstrict-prototypes. */ + struct _obstack_chunk *(*chunkfun) (void *, long); + void (*freefun) (void *, struct _obstack_chunk *); + void *extra_arg; /* first arg for chunk alloc/dealloc funcs */ + unsigned use_extra_arg : 1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object : 1; /* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ + unsigned alloc_failed : 1; /* No longer used, as we now call the failed + handler on error, but retained for binary + compatibility. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +extern void _obstack_newchunk (struct obstack *, int); +extern int _obstack_begin (struct obstack *, int, int, + void *(*)(long), void (*)(void *)); +extern int _obstack_begin_1 (struct obstack *, int, int, + void *(*)(void *, long), + void (*)(void *, void *), void *); +extern int _obstack_memory_used (struct obstack *) __attribute_pure__; + +/* The default name of the function for freeing a chunk is 'obstack_free', + but gnulib users can override this by defining '__obstack_free'. */ +#ifndef __obstack_free +# define __obstack_free obstack_free +#endif +extern void __obstack_free (struct obstack *, void *); + + +/* Error handler called when 'obstack_chunk_alloc' failed to allocate + more memory. This can be set to a user defined function which + should either abort gracefully or use longjump - but shouldn't + return. The default action is to print a message and abort. */ +extern void (*obstack_alloc_failed_handler) (void); + +/* Exit value used when 'print_and_abort' is used. */ +extern int obstack_exit_failure; + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((void *) (h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +/* To prevent prototype warnings provide complete argument list. */ +#define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*)(long))obstack_chunk_alloc, \ + (void (*)(void *))obstack_chunk_free) + +#define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*)(long))obstack_chunk_alloc, \ + (void (*)(void *))obstack_chunk_free) + +#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*)(long))(chunkfun), \ + (void (*)(void *))(freefun)) + +#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*)(void *, long))(chunkfun), \ + (void (*)(void *, void *))(freefun), (arg)) + +#define obstack_chunkfun(h, newchunkfun) \ + ((h)->chunkfun = (struct _obstack_chunk *(*)(void *, long))(newchunkfun)) + +#define obstack_freefun(h, newfreefun) \ + ((h)->freefun = (void (*)(void *, struct _obstack_chunk *))(newfreefun)) + +#define obstack_1grow_fast(h, achar) (*((h)->next_free)++ = (achar)) + +#define obstack_blank_fast(h, n) ((h)->next_free += (n)) + +#define obstack_memory_used(h) _obstack_memory_used (h) + +#if defined __GNUC__ +# if ! (2 < __GNUC__ + (8 <= __GNUC_MINOR__)) +# define __extension__ +# endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the 'temp' slot, to make faster code. */ + +# define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +# define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +# define obstack_make_room(OBSTACK, length) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + (void) 0; }) + +# define obstack_empty_p(OBSTACK) \ + __extension__ \ + ({ struct obstack const *__o = (OBSTACK); \ + (__o->chunk->prev == 0 \ + && __o->next_free == __PTR_ALIGN ((char *) __o->chunk, \ + __o->chunk->contents, \ + __o->alignment_mask)); }) + +# define obstack_grow(OBSTACK, where, length) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len > __o->chunk_limit) \ + _obstack_newchunk (__o, __len); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + (void) 0; }) + +# define obstack_grow0(OBSTACK, where, length) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, __len + 1); \ + memcpy (__o->next_free, where, __len); \ + __o->next_free += __len; \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +# define obstack_1grow(OBSTACK, datum) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, 1); \ + obstack_1grow_fast (__o, datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers + or ints, and that the data added so far to the current object + shares that much alignment. */ + +# define obstack_ptr_grow(OBSTACK, datum) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (void *) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (void *)); \ + obstack_ptr_grow_fast (__o, datum); }) \ + +# define obstack_int_grow(OBSTACK, datum) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (int) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (int)); \ + obstack_int_grow_fast (__o, datum); }) + +# define obstack_ptr_grow_fast(OBSTACK, aptr) \ + __extension__ \ + ({ struct obstack *__o1 = (OBSTACK); \ + void *__p1 = __o1->next_free; \ + *(const void **) __p1 = (aptr); \ + __o1->next_free += sizeof (const void *); \ + (void) 0; }) + +# define obstack_int_grow_fast(OBSTACK, aint) \ + __extension__ \ + ({ struct obstack *__o1 = (OBSTACK); \ + void *__p1 = __o1->next_free; \ + *(int *) __p1 = (aint); \ + __o1->next_free += sizeof (int); \ + (void) 0; }) + +# define obstack_blank(OBSTACK, length) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + obstack_blank_fast (__o, __len); \ + (void) 0; }) + +# define obstack_alloc(OBSTACK, length) \ + __extension__ \ + ({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +# define obstack_copy(OBSTACK, where, length) \ + __extension__ \ + ({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +# define obstack_copy0(OBSTACK, where, length) \ + __extension__ \ + ({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +# define obstack_finish(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o1 = (OBSTACK); \ + void *__value = (void *) __o1->object_base; \ + if (__o1->next_free == __value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __PTR_ALIGN (__o1->object_base, __o1->next_free, \ + __o1->alignment_mask); \ + if (__o1->next_free - (char *) __o1->chunk \ + > __o1->chunk_limit - (char *) __o1->chunk) \ + __o1->next_free = __o1->chunk_limit; \ + __o1->object_base = __o1->next_free; \ + __value; }) + +# define obstack_free(OBSTACK, OBJ) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *) __o->chunk && __obj < (void *) __o->chunk_limit) \ + __o->next_free = __o->object_base = (char *) __obj; \ + else (__obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ */ + +# define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +# define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +# define obstack_empty_p(h) \ + ((h)->chunk->prev == 0 \ + && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk, \ + (h)->chunk->contents, \ + (h)->alignment_mask)) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ + +# define obstack_make_room(h, length) \ + ((h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0)) + +# define obstack_grow(h, where, length) \ + ((h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint) + +# define obstack_grow0(h, where, length) \ + ((h)->temp.tempint = (length), \ + (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0), \ + memcpy ((h)->next_free, where, (h)->temp.tempint), \ + (h)->next_free += (h)->temp.tempint, \ + *((h)->next_free)++ = 0) + +# define obstack_1grow(h, datum) \ + ((((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + obstack_1grow_fast (h, datum)) + +# define obstack_ptr_grow(h, datum) \ + ((((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + obstack_ptr_grow_fast (h, datum)) + +# define obstack_int_grow(h, datum) \ + ((((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + obstack_int_grow_fast (h, datum)) + +# define obstack_ptr_grow_fast(h, aptr) \ + (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr)) + +# define obstack_int_grow_fast(h, aint) \ + (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint)) + +# define obstack_blank(h, length) \ + ((h)->temp.tempint = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint) \ + ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \ + obstack_blank_fast (h, (h)->temp.tempint)) + +# define obstack_alloc(h, length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +# define obstack_copy(h, where, length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_copy0(h, where, length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_finish(h) \ + (((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp.tempptr = (h)->object_base, \ + (h)->next_free \ + = __PTR_ALIGN ((h)->object_base, (h)->next_free, \ + (h)->alignment_mask), \ + (((h)->next_free - (char *) (h)->chunk \ + > (h)->chunk_limit - (char *) (h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + (h)->temp.tempptr) + +# define obstack_free(h, obj) \ + ((h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \ + ((((h)->temp.tempint > 0 \ + && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk)) \ + ? (void) ((h)->next_free = (h)->object_base \ + = (h)->temp.tempint + (char *) (h)->chunk) \ + : (__obstack_free) (h, (h)->temp.tempint + (char *) (h)->chunk))) + +#endif /* not __GNUC__ */ + +#ifdef __cplusplus +} /* C++ */ +#endif + +#endif /* obstack.h */ diff --git a/REORG.TODO/malloc/reallocarray.c b/REORG.TODO/malloc/reallocarray.c new file mode 100644 index 0000000000..07562c30c9 --- /dev/null +++ b/REORG.TODO/malloc/reallocarray.c @@ -0,0 +1,37 @@ +/* Change the size of an allocated block. + Copyright (C) 2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <malloc.h> +#include <malloc/malloc-internal.h> + +void * +__libc_reallocarray (void *optr, size_t nmemb, size_t elem_size) +{ + size_t bytes; + if (check_mul_overflow_size_t (nmemb, elem_size, &bytes)) + { + __set_errno (ENOMEM); + return 0; + } + else + return realloc (optr, bytes); +} +libc_hidden_def (__libc_reallocarray) + +weak_alias (__libc_reallocarray, reallocarray) diff --git a/REORG.TODO/malloc/scratch_buffer_grow.c b/REORG.TODO/malloc/scratch_buffer_grow.c new file mode 100644 index 0000000000..22bae506a1 --- /dev/null +++ b/REORG.TODO/malloc/scratch_buffer_grow.c @@ -0,0 +1,52 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015-2017 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/>. */ + +#include <scratch_buffer.h> +#include <errno.h> + +bool +__libc_scratch_buffer_grow (struct scratch_buffer *buffer) +{ + void *new_ptr; + size_t new_length = buffer->length * 2; + + /* Discard old buffer. */ + scratch_buffer_free (buffer); + + /* Check for overflow. */ + if (__glibc_likely (new_length >= buffer->length)) + new_ptr = malloc (new_length); + else + { + __set_errno (ENOMEM); + new_ptr = NULL; + } + + if (__glibc_unlikely (new_ptr == NULL)) + { + /* Buffer must remain valid to free. */ + scratch_buffer_init (buffer); + return false; + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_grow); diff --git a/REORG.TODO/malloc/scratch_buffer_grow_preserve.c b/REORG.TODO/malloc/scratch_buffer_grow_preserve.c new file mode 100644 index 0000000000..18543ef85b --- /dev/null +++ b/REORG.TODO/malloc/scratch_buffer_grow_preserve.c @@ -0,0 +1,63 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015-2017 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/>. */ + +#include <scratch_buffer.h> +#include <errno.h> +#include <string.h> + +bool +__libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer) +{ + size_t new_length = 2 * buffer->length; + void *new_ptr; + + if (buffer->data == buffer->__space) + { + /* Move buffer to the heap. No overflow is possible because + buffer->length describes a small buffer on the stack. */ + new_ptr = malloc (new_length); + if (new_ptr == NULL) + return false; + memcpy (new_ptr, buffer->__space, buffer->length); + } + else + { + /* Buffer was already on the heap. Check for overflow. */ + if (__glibc_likely (new_length >= buffer->length)) + new_ptr = realloc (buffer->data, new_length); + else + { + __set_errno (ENOMEM); + new_ptr = NULL; + } + + if (__glibc_unlikely (new_ptr == NULL)) + { + /* Deallocate, but buffer must remain valid to free. */ + free (buffer->data); + scratch_buffer_init (buffer); + return false; + } + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_grow_preserve); diff --git a/REORG.TODO/malloc/scratch_buffer_set_array_size.c b/REORG.TODO/malloc/scratch_buffer_set_array_size.c new file mode 100644 index 0000000000..8ab6d9d300 --- /dev/null +++ b/REORG.TODO/malloc/scratch_buffer_set_array_size.c @@ -0,0 +1,60 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015-2017 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/>. */ + +#include <scratch_buffer.h> +#include <errno.h> +#include <limits.h> + +bool +__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer, + size_t nelem, size_t size) +{ + size_t new_length = nelem * size; + + /* Avoid overflow check if both values are small. */ + if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0 + && nelem != 0 && size != new_length / nelem) + { + /* Overflow. Discard the old buffer, but it must remain valid + to free. */ + scratch_buffer_free (buffer); + scratch_buffer_init (buffer); + __set_errno (ENOMEM); + return false; + } + + if (new_length <= buffer->length) + return true; + + /* Discard old buffer. */ + scratch_buffer_free (buffer); + + char *new_ptr = malloc (new_length); + if (new_ptr == NULL) + { + /* Buffer must remain valid to free. */ + scratch_buffer_init (buffer); + return false; + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_set_array_size); diff --git a/REORG.TODO/malloc/set-freeres.c b/REORG.TODO/malloc/set-freeres.c new file mode 100644 index 0000000000..c9cdd819e7 --- /dev/null +++ b/REORG.TODO/malloc/set-freeres.c @@ -0,0 +1,49 @@ +/* Copyright (C) 1997-2017 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/>. */ + +#include <atomic.h> +#include <stdlib.h> +#include <set-hooks.h> +#include <libc-internal.h> + +#include "../libio/libioP.h" + +DEFINE_HOOK (__libc_subfreeres, (void)); + +symbol_set_define (__libc_freeres_ptrs); + +void __libc_freeres_fn_section +__libc_freeres (void) +{ + /* This function might be called from different places. So better + protect for multiple executions since these are fatal. */ + static long int already_called; + + if (!atomic_compare_and_exchange_bool_acq (&already_called, 1, 0)) + { + void *const *p; + + _IO_cleanup (); + + RUN_HOOK (__libc_subfreeres, ()); + + for (p = symbol_set_first_element (__libc_freeres_ptrs); + !symbol_set_end_p (__libc_freeres_ptrs, p); ++p) + free (*p); + } +} +libc_hidden_def (__libc_freeres) diff --git a/REORG.TODO/malloc/thread-freeres.c b/REORG.TODO/malloc/thread-freeres.c new file mode 100644 index 0000000000..675c12dc3a --- /dev/null +++ b/REORG.TODO/malloc/thread-freeres.c @@ -0,0 +1,31 @@ +/* Free resources stored in thread-local variables on thread exit. + Copyright (C) 2003-2017 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/>. */ + +#include <stdlib.h> +#include <libc-internal.h> +#include <set-hooks.h> + +#ifdef _LIBC_REENTRANT +DEFINE_HOOK (__libc_thread_subfreeres, (void)); + +void __attribute__ ((section ("__libc_thread_freeres_fn"))) +__libc_thread_freeres (void) +{ + RUN_HOOK (__libc_thread_subfreeres, ()); +} +#endif diff --git a/REORG.TODO/malloc/tst-calloc.c b/REORG.TODO/malloc/tst-calloc.c new file mode 100644 index 0000000000..c4236809f3 --- /dev/null +++ b/REORG.TODO/malloc/tst-calloc.c @@ -0,0 +1,128 @@ +/* Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>. + + 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/>. */ + +#include <errno.h> +#include <error.h> +#include <limits.h> +#include <malloc.h> +#include <stdlib.h> +#include <stdio.h> + + +/* Number of samples per size. */ +#define N 50000 + + +static void +fixed_test (int size) +{ + char *ptrs[N]; + int i; + + for (i = 0; i < N; ++i) + { + int j; + + ptrs[i] = (char *) calloc (1, size); + + if (ptrs[i] == NULL) + break; + + for (j = 0; j < size; ++j) + { + if (ptrs[i][j] != '\0') + error (EXIT_FAILURE, 0, + "byte not cleared (size %d, element %d, byte %d)", + size, i, j); + ptrs[i][j] = '\xff'; + } + } + + while (i-- > 0) + free (ptrs[i]); +} + + +static void +random_test (void) +{ + char *ptrs[N]; + int i; + + for (i = 0; i < N; ++i) + { + int j; + int n = 1 + random () % 10; + int elem = 1 + random () % 100; + int size = n * elem; + + ptrs[i] = (char *) calloc (n, elem); + + if (ptrs[i] == NULL) + break; + + for (j = 0; j < size; ++j) + { + if (ptrs[i][j] != '\0') + error (EXIT_FAILURE, 0, + "byte not cleared (size %d, element %d, byte %d)", + size, i, j); + ptrs[i][j] = '\xff'; + } + } + + while (i-- > 0) + free (ptrs[i]); +} + + +static void +null_test (void) +{ + /* If the size is 0 the result is implementation defined. Just make + sure the program doesn't crash. */ + calloc (0, 0); + calloc (0, UINT_MAX); + calloc (UINT_MAX, 0); + calloc (0, ~((size_t) 0)); + calloc (~((size_t) 0), 0); +} + + +static int +do_test (void) +{ + /* We are allocating blocks with `calloc' and check whether every + block is completely cleared. We first try this for some fixed + times and then with random size. */ + fixed_test (15); + fixed_test (5); + fixed_test (17); + fixed_test (6); + fixed_test (31); + fixed_test (96); + + random_test (); + + null_test (); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-dynarray-at-fail.c b/REORG.TODO/malloc/tst-dynarray-at-fail.c new file mode 100644 index 0000000000..bc1a48c5cb --- /dev/null +++ b/REORG.TODO/malloc/tst-dynarray-at-fail.c @@ -0,0 +1,125 @@ +/* Test reporting of out-of-bounds access for dynamic arrays. + Copyright (C) 2017 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/>. */ + +#include "tst-dynarray-shared.h" + +#include <signal.h> +#include <stdint.h> +#include <string.h> +#include <support/capture_subprocess.h> +#include <support/check.h> + +/* Run CALLBACK and check that the data on standard error equals + EXPECTED. */ +static void +check (const char *test, void (*callback) (void *), size_t index, + const char *expected) +{ + struct support_capture_subprocess result + = support_capture_subprocess (callback, &index); + if (strcmp (result.err.buffer, expected) != 0) + { + support_record_failure (); + printf ("error: test %s (%zu) unexpected standard error data\n" + " expected: %s\n" + " actual: %s\n", + test, index, expected, result.err.buffer); + } + TEST_VERIFY (strlen (result.out.buffer) == 0); + TEST_VERIFY (WIFSIGNALED (result.status)); + if (WIFSIGNALED (result.status)) + TEST_VERIFY (WTERMSIG (result.status) == SIGABRT); + support_capture_subprocess_free (&result); +} + +/* Try indexing an empty array. */ +static void +test_empty (void *closure) +{ + size_t *pindex = closure; + struct dynarray_int dyn; + dynarray_int_init (&dyn); + dynarray_int_at (&dyn, *pindex); +} + +/* Try indexing a one-element array. */ +static void +test_one (void *closure) +{ + size_t *pindex = closure; + struct dynarray_int dyn; + dynarray_int_init (&dyn); + TEST_VERIFY (dynarray_int_resize (&dyn, 1)); + dynarray_int_at (&dyn, *pindex); +} + +/* Try indexing a longer array. */ +static void +test_many (void *closure) +{ + size_t *pindex = closure; + struct dynarray_int dyn; + dynarray_int_init (&dyn); + TEST_VERIFY (dynarray_int_resize (&dyn, 5371)); + dynarray_int_at (&dyn, *pindex); +} + +/* (size_t) -1 for use in string literals. */ +#if SIZE_WIDTH == 32 +# define MINUS_1 "4294967295" +#elif SIZE_WIDTH == 64 +# define MINUS_1 "18446744073709551615" +#else +# error "unknown value for SIZE_WIDTH" +#endif + +static int +do_test (void) +{ + TEST_VERIFY (setenv ("LIBC_FATAL_STDERR_", "1", 1) == 0); + + check ("test_empty", test_empty, 0, + "Fatal glibc error: array index 0 not less than array length 0\n"); + check ("test_empty", test_empty, 1, + "Fatal glibc error: array index 1 not less than array length 0\n"); + check ("test_empty", test_empty, -1, + "Fatal glibc error: array index " MINUS_1 + " not less than array length 0\n"); + + check ("test_one", test_one, 1, + "Fatal glibc error: array index 1 not less than array length 1\n"); + check ("test_one", test_one, 2, + "Fatal glibc error: array index 2 not less than array length 1\n"); + check ("test_one", test_one, -1, + "Fatal glibc error: array index " MINUS_1 + " not less than array length 1\n"); + + check ("test_many", test_many, 5371, + "Fatal glibc error: array index 5371" + " not less than array length 5371\n"); + check ("test_many", test_many, 5372, + "Fatal glibc error: array index 5372" + " not less than array length 5371\n"); + check ("test_many", test_many, -1, + "Fatal glibc error: array index " MINUS_1 + " not less than array length 5371\n"); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-dynarray-fail.c b/REORG.TODO/malloc/tst-dynarray-fail.c new file mode 100644 index 0000000000..508dbae93e --- /dev/null +++ b/REORG.TODO/malloc/tst-dynarray-fail.c @@ -0,0 +1,418 @@ +/* Test allocation failures with dynamic arrays. + Copyright (C) 2017 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 is separate from tst-dynarray because it cannot run under + valgrind. */ + +#include "tst-dynarray-shared.h" + +#include <mcheck.h> +#include <stdio.h> +#include <support/check.h> +#include <support/support.h> +#include <support/xunistd.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <unistd.h> + +/* Data structure to fill up the heap. */ +struct heap_filler +{ + struct heap_filler *next; +}; + +/* Allocate objects until the heap is full. */ +static struct heap_filler * +fill_heap (void) +{ + size_t pad = 4096; + struct heap_filler *head = NULL; + while (true) + { + struct heap_filler *new_head = malloc (sizeof (*new_head) + pad); + if (new_head == NULL) + { + if (pad > 0) + { + /* Try again with smaller allocations. */ + pad = 0; + continue; + } + else + break; + } + new_head->next = head; + head = new_head; + } + return head; +} + +/* Free the heap-filling allocations, so that we can continue testing + and detect memory leaks elsewhere. */ +static void +free_fill_heap (struct heap_filler *head) +{ + while (head != NULL) + { + struct heap_filler *next = head->next; + free (head); + head = next; + } +} + +/* Check allocation failures for int arrays (without an element free + function). */ +static void +test_int_fail (void) +{ + /* Exercise failure in add/emplace. + + do_add: Use emplace (false) or add (true) to add elements. + do_finalize: Perform finalization at the end (instead of free). */ + for (int do_add = 0; do_add < 2; ++do_add) + for (int do_finalize = 0; do_finalize < 2; ++do_finalize) + { + struct dynarray_int dyn; + dynarray_int_init (&dyn); + size_t count = 0; + while (true) + { + if (do_add) + { + dynarray_int_add (&dyn, 0); + if (dynarray_int_has_failed (&dyn)) + break; + } + else + { + int *place = dynarray_int_emplace (&dyn); + if (place == NULL) + break; + TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn)); + *place = 0; + } + ++count; + } + printf ("info: %s: failure after %zu elements\n", __func__, count); + TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn)); + if (do_finalize) + { + struct int_array result = { (int *) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1); + TEST_VERIFY_EXIT (result.length == (size_t) -1); + } + else + dynarray_int_free (&dyn); + CHECK_INIT_STATE (int, &dyn); + } + + /* Exercise failure in finalize. */ + for (int do_add = 0; do_add < 2; ++do_add) + { + struct dynarray_int dyn; + dynarray_int_init (&dyn); + for (unsigned int i = 0; i < 10000; ++i) + { + if (do_add) + { + dynarray_int_add (&dyn, i); + TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn)); + } + else + { + int *place = dynarray_int_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + *place = i; + } + } + TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn)); + struct heap_filler *heap_filler = fill_heap (); + struct int_array result = { (int *) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1); + TEST_VERIFY_EXIT (result.length == (size_t) -1); + CHECK_INIT_STATE (int, &dyn); + free_fill_heap (heap_filler); + } + + /* Exercise failure in resize. */ + { + struct dynarray_int dyn; + dynarray_int_init (&dyn); + struct heap_filler *heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_int_resize (&dyn, 1000)); + TEST_VERIFY (dynarray_int_has_failed (&dyn)); + free_fill_heap (heap_filler); + + dynarray_int_init (&dyn); + TEST_VERIFY (dynarray_int_resize (&dyn, 1)); + heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_int_resize (&dyn, 1000)); + TEST_VERIFY (dynarray_int_has_failed (&dyn)); + free_fill_heap (heap_filler); + + dynarray_int_init (&dyn); + TEST_VERIFY (dynarray_int_resize (&dyn, 1000)); + heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_int_resize (&dyn, 2000)); + TEST_VERIFY (dynarray_int_has_failed (&dyn)); + free_fill_heap (heap_filler); + } +} + +/* Check allocation failures for char * arrays (which automatically + free the pointed-to strings). */ +static void +test_str_fail (void) +{ + /* Exercise failure in add/emplace. + + do_add: Use emplace (false) or add (true) to add elements. + do_finalize: Perform finalization at the end (instead of free). */ + for (int do_add = 0; do_add < 2; ++do_add) + for (int do_finalize = 0; do_finalize < 2; ++do_finalize) + { + struct dynarray_str dyn; + dynarray_str_init (&dyn); + size_t count = 0; + while (true) + { + char **place; + if (do_add) + { + dynarray_str_add (&dyn, NULL); + if (dynarray_str_has_failed (&dyn)) + break; + else + place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1); + } + else + { + place = dynarray_str_emplace (&dyn); + if (place == NULL) + break; + } + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + TEST_VERIFY_EXIT (*place == NULL); + *place = strdup ("placeholder"); + if (*place == NULL) + { + /* Second loop to wait for failure of + dynarray_str_emplace. */ + while (true) + { + if (do_add) + { + dynarray_str_add (&dyn, NULL); + if (dynarray_str_has_failed (&dyn)) + break; + } + else + { + char **place = dynarray_str_emplace (&dyn); + if (place == NULL) + break; + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + *place = NULL; + } + ++count; + } + break; + } + ++count; + } + printf ("info: %s: failure after %zu elements\n", __func__, count); + TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn)); + if (do_finalize) + { + struct str_array result = { (char **) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1); + TEST_VERIFY_EXIT (result.length == (size_t) -1); + } + else + dynarray_str_free (&dyn); + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch); + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0); + TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0); + } + + /* Exercise failure in finalize. */ + for (int do_add = 0; do_add < 2; ++do_add) + { + struct dynarray_str dyn; + dynarray_str_init (&dyn); + for (unsigned int i = 0; i < 1000; ++i) + { + if (do_add) + dynarray_str_add (&dyn, xstrdup ("placeholder")); + else + { + char **place = dynarray_str_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + TEST_VERIFY_EXIT (*place == NULL); + *place = xstrdup ("placeholder"); + } + } + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + struct heap_filler *heap_filler = fill_heap (); + struct str_array result = { (char **) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1); + TEST_VERIFY_EXIT (result.length == (size_t) -1); + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch); + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0); + TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0); + free_fill_heap (heap_filler); + } + + /* Exercise failure in resize. */ + { + struct dynarray_str dyn; + dynarray_str_init (&dyn); + struct heap_filler *heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_str_resize (&dyn, 1000)); + TEST_VERIFY (dynarray_str_has_failed (&dyn)); + free_fill_heap (heap_filler); + + dynarray_str_init (&dyn); + TEST_VERIFY (dynarray_str_resize (&dyn, 1)); + *dynarray_str_at (&dyn, 0) = xstrdup ("allocated"); + heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_str_resize (&dyn, 1000)); + TEST_VERIFY (dynarray_str_has_failed (&dyn)); + free_fill_heap (heap_filler); + + dynarray_str_init (&dyn); + TEST_VERIFY (dynarray_str_resize (&dyn, 1000)); + *dynarray_str_at (&dyn, 0) = xstrdup ("allocated"); + heap_filler = fill_heap (); + TEST_VERIFY (!dynarray_str_resize (&dyn, 2000)); + TEST_VERIFY (dynarray_str_has_failed (&dyn)); + free_fill_heap (heap_filler); + } +} + +/* Test if mmap can allocate a page. This is necessary because + setrlimit does not fail even if it reduces the RLIMIT_AS limit + below what is currently needed by the process. */ +static bool +mmap_works (void) +{ + void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (ptr == MAP_FAILED) + return false; + xmunmap (ptr, 1); + return true; +} + +/* Set the RLIMIT_AS limit to the value in *LIMIT. */ +static void +xsetrlimit_as (const struct rlimit *limit) +{ + if (setrlimit (RLIMIT_AS, limit) != 0) + FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m", + (unsigned long) limit->rlim_cur); +} + +/* Approximately this many bytes can be allocated after + reduce_rlimit_as has run. */ +enum { as_limit_reserve = 2 * 1024 * 1024 }; + +/* Limit the size of the process, so that memory allocation in + allocate_thread will eventually fail, without impacting the entire + system. By default, a dynamic limit which leaves room for 2 MiB is + activated. The TEST_RLIMIT_AS environment variable overrides + it. */ +static void +reduce_rlimit_as (void) +{ + struct rlimit limit; + if (getrlimit (RLIMIT_AS, &limit) != 0) + FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m"); + + /* Use the TEST_RLIMIT_AS setting if available. */ + { + long target = 0; + const char *variable = "TEST_RLIMIT_AS"; + const char *target_str = getenv (variable); + if (target_str != NULL) + { + target = atoi (target_str); + if (target <= 0) + FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str); + printf ("info: setting RLIMIT_AS to %ld MiB\n", target); + target *= 1024 * 1024; /* Convert to megabytes. */ + limit.rlim_cur = target; + xsetrlimit_as (&limit); + return; + } + } + + /* Otherwise, try to find the limit with a binary search. */ + unsigned long low = 1 << 20; + limit.rlim_cur = low; + xsetrlimit_as (&limit); + + /* Find working upper limit. */ + unsigned long high = 1 << 30; + while (true) + { + limit.rlim_cur = high; + xsetrlimit_as (&limit); + if (mmap_works ()) + break; + if (2 * high < high) + FAIL_EXIT1 ("cannot find upper AS limit"); + high *= 2; + } + + /* Perform binary search. */ + while ((high - low) > 128 * 1024) + { + unsigned long middle = (low + high) / 2; + limit.rlim_cur = middle; + xsetrlimit_as (&limit); + if (mmap_works ()) + high = middle; + else + low = middle; + } + + unsigned long target = high + as_limit_reserve; + limit.rlim_cur = target; + xsetrlimit_as (&limit); + printf ("info: RLIMIT_AS limit: %lu bytes\n", target); +} + +static int +do_test (void) +{ + mtrace (); + reduce_rlimit_as (); + test_int_fail (); + test_str_fail (); + return 0; +} + +#define TIMEOUT 90 +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-dynarray-shared.h b/REORG.TODO/malloc/tst-dynarray-shared.h new file mode 100644 index 0000000000..faba66f580 --- /dev/null +++ b/REORG.TODO/malloc/tst-dynarray-shared.h @@ -0,0 +1,77 @@ +/* Shared definitions for dynarray tests. + Copyright (C) 2017 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/>. */ + +#include <stddef.h> + +struct int_array +{ + int *array; + size_t length; +}; + +#define DYNARRAY_STRUCT dynarray_int +#define DYNARRAY_ELEMENT int +#define DYNARRAY_PREFIX dynarray_int_ +#define DYNARRAY_FINAL_TYPE struct int_array +#include <malloc/dynarray-skeleton.c> + +struct str_array +{ + char **array; + size_t length; +}; + +#define DYNARRAY_STRUCT dynarray_str +#define DYNARRAY_ELEMENT char * +#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr) +#define DYNARRAY_PREFIX dynarray_str_ +#define DYNARRAY_FINAL_TYPE struct str_array +#include <malloc/dynarray-skeleton.c> + +/* Check that *DYN is equivalent to its initial state. */ +#define CHECK_INIT_STATE(type, dyn) \ + ({ \ + TEST_VERIFY_EXIT (!dynarray_##type##_has_failed (dyn)); \ + TEST_VERIFY_EXIT (dynarray_##type##_size (dyn) == 0); \ + TEST_VERIFY_EXIT ((dyn)->dynarray_header.array \ + == (dyn)->scratch); \ + TEST_VERIFY_EXIT ((dyn)->dynarray_header.allocated > 0); \ + (void) 0; \ + }) + +/* Check that *DYN behaves as if it is in its initial state. */ +#define CHECK_EMPTY(type, dyn) \ + ({ \ + CHECK_INIT_STATE (type, (dyn)); \ + dynarray_##type##_free (dyn); \ + CHECK_INIT_STATE (type, (dyn)); \ + dynarray_##type##_clear (dyn); \ + CHECK_INIT_STATE (type, (dyn)); \ + dynarray_##type##_remove_last (dyn); \ + CHECK_INIT_STATE (type, (dyn)); \ + dynarray_##type##_mark_failed (dyn); \ + TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \ + dynarray_##type##_clear (dyn); \ + TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \ + dynarray_##type##_remove_last (dyn); \ + TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \ + TEST_VERIFY_EXIT (dynarray_##type##_emplace (dyn) == NULL); \ + dynarray_##type##_free (dyn); \ + CHECK_INIT_STATE (type, (dyn)); \ + (void) 0; \ + }) diff --git a/REORG.TODO/malloc/tst-dynarray.c b/REORG.TODO/malloc/tst-dynarray.c new file mode 100644 index 0000000000..7aee85aa39 --- /dev/null +++ b/REORG.TODO/malloc/tst-dynarray.c @@ -0,0 +1,517 @@ +/* Test for dynamic arrays. + Copyright (C) 2017 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/>. */ + +#include "tst-dynarray-shared.h" + +#define DYNARRAY_STRUCT dynarray_long +#define DYNARRAY_ELEMENT long +#define DYNARRAY_PREFIX dynarray_long_ +#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 17) +#include <malloc/dynarray-skeleton.c> + +struct long_array +{ + long *array; + size_t length; +}; + +#define DYNARRAY_STRUCT dynarray_long_noscratch +#define DYNARRAY_ELEMENT long +#define DYNARRAY_PREFIX dynarray_long_noscratch_ +#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 23) +#define DYNARRAY_FINAL_TYPE struct long_array +#define DYNARRAY_INITIAL_SIZE 0 +#include <malloc/dynarray-skeleton.c> + +#define DYNARRAY_STRUCT zstr +#define DYNARRAY_ELEMENT char +#define DYNARRAY_PREFIX zstr_ +#define DYNARRAY_INITIAL_SIZE 128 +#include <malloc/dynarray-skeleton.c> + +#include <malloc.h> +#include <mcheck.h> +#include <stdint.h> +#include <support/check.h> +#include <support/support.h> + +enum { max_count = 20 }; + +/* Test dynamic arrays with int elements (no automatic deallocation + for elements). */ +static void +test_int (void) +{ + /* Empty array. */ + { + struct dynarray_int dyn; + dynarray_int_init (&dyn); + CHECK_EMPTY (int, &dyn); + } + + /* Empty array with finalization. */ + { + struct dynarray_int dyn; + dynarray_int_init (&dyn); + CHECK_INIT_STATE (int, &dyn); + struct int_array result = { (int *) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result)); + CHECK_INIT_STATE (int, &dyn); + TEST_VERIFY_EXIT (result.array == NULL); + TEST_VERIFY_EXIT (result.length == 0); + } + + /* Non-empty array tests. + + do_add: Switch between emplace (false) and add (true). + do_finalize: Perform finalize call at the end. + do_clear: Perform clear call at the end. + do_remove_last: Perform remove_last call after adding elements. + count: Number of elements added to the array. */ + for (int do_add = 0; do_add < 2; ++do_add) + for (int do_finalize = 0; do_finalize < 2; ++do_finalize) + for (int do_clear = 0; do_clear < 2; ++do_clear) + for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last) + for (unsigned int count = 0; count < max_count; ++count) + { + if (do_remove_last && count == 0) + continue; + unsigned int base = count * count; + struct dynarray_int dyn; + dynarray_int_init (&dyn); + for (unsigned int i = 0; i < count; ++i) + { + if (do_add) + dynarray_int_add (&dyn, base + i); + else + { + int *place = dynarray_int_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + *place = base + i; + } + TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn)); + TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == i + 1); + TEST_VERIFY_EXIT (dynarray_int_size (&dyn) + <= dyn.dynarray_header.allocated); + } + TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == count); + TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated); + unsigned final_count; + bool heap_array = dyn.dynarray_header.array != dyn.scratch; + if (do_remove_last) + { + dynarray_int_remove_last (&dyn); + if (count == 0) + final_count = 0; + else + final_count = count - 1; + } + else + final_count = count; + if (do_clear) + { + dynarray_int_clear (&dyn); + final_count = 0; + } + TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn)); + TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch) + == heap_array); + TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == final_count); + TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count); + if (!do_clear) + for (unsigned int i = 0; i < final_count; ++i) + TEST_VERIFY_EXIT (*dynarray_int_at (&dyn, i) == base + i); + if (do_finalize) + { + struct int_array result = { (int *) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result)); + CHECK_INIT_STATE (int, &dyn); + TEST_VERIFY_EXIT (result.length == final_count); + if (final_count == 0) + TEST_VERIFY_EXIT (result.array == NULL); + else + { + TEST_VERIFY_EXIT (result.array != NULL); + TEST_VERIFY_EXIT (result.array != (int *) (uintptr_t) -1); + TEST_VERIFY_EXIT + (malloc_usable_size (result.array) + >= final_count * sizeof (result.array[0])); + for (unsigned int i = 0; i < final_count; ++i) + TEST_VERIFY_EXIT (result.array[i] == base + i); + free (result.array); + } + } + else /* !do_finalize */ + { + dynarray_int_free (&dyn); + CHECK_INIT_STATE (int, &dyn); + } + } +} + +/* Test dynamic arrays with char * elements (with automatic + deallocation of the pointed-to strings). */ +static void +test_str (void) +{ + /* Empty array. */ + { + struct dynarray_str dyn; + dynarray_str_init (&dyn); + CHECK_EMPTY (str, &dyn); + } + + /* Empty array with finalization. */ + { + struct dynarray_str dyn; + dynarray_str_init (&dyn); + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + struct str_array result = { (char **) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result)); + CHECK_INIT_STATE (str, &dyn); + TEST_VERIFY_EXIT (result.array == NULL); + TEST_VERIFY_EXIT (result.length == 0); + } + + /* Non-empty array tests. + + do_add: Switch between emplace (false) and add (true). + do_finalize: Perform finalize call at the end. + do_clear: Perform clear call at the end. + do_remove_last: Perform remove_last call after adding elements. + count: Number of elements added to the array. */ + for (int do_add = 0; do_add < 2; ++do_add) + for (int do_finalize = 0; do_finalize < 2; ++do_finalize) + for (int do_clear = 0; do_clear < 2; ++do_clear) + for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last) + for (unsigned int count = 0; count < max_count; ++count) + { + if (do_remove_last && count == 0) + continue; + unsigned int base = count * count; + struct dynarray_str dyn; + dynarray_str_init (&dyn); + for (unsigned int i = 0; i < count; ++i) + { + char *item = xasprintf ("%d", base + i); + if (do_add) + dynarray_str_add (&dyn, item); + else + { + char **place = dynarray_str_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + TEST_VERIFY_EXIT (*place == NULL); + *place = item; + } + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == i + 1); + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) + <= dyn.dynarray_header.allocated); + } + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == count); + TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated); + unsigned final_count; + bool heap_array = dyn.dynarray_header.array != dyn.scratch; + if (do_remove_last) + { + dynarray_str_remove_last (&dyn); + if (count == 0) + final_count = 0; + else + final_count = count - 1; + } + else + final_count = count; + if (do_clear) + { + dynarray_str_clear (&dyn); + final_count = 0; + } + TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn)); + TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch) + == heap_array); + TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == final_count); + TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count); + if (!do_clear) + for (unsigned int i = 0; i < count - do_remove_last; ++i) + { + char *expected = xasprintf ("%d", base + i); + const char *actual = *dynarray_str_at (&dyn, i); + TEST_VERIFY_EXIT (strcmp (actual, expected) == 0); + free (expected); + } + if (do_finalize) + { + struct str_array result = { (char **) (uintptr_t) -1, -1 }; + TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result)); + CHECK_INIT_STATE (str, &dyn); + TEST_VERIFY_EXIT (result.length == final_count); + if (final_count == 0) + TEST_VERIFY_EXIT (result.array == NULL); + else + { + TEST_VERIFY_EXIT (result.array != NULL); + TEST_VERIFY_EXIT (result.array + != (char **) (uintptr_t) -1); + TEST_VERIFY_EXIT (result.length + == count - do_remove_last); + TEST_VERIFY_EXIT + (malloc_usable_size (result.array) + >= final_count * sizeof (result.array[0])); + for (unsigned int i = 0; i < count - do_remove_last; ++i) + { + char *expected = xasprintf ("%d", base + i); + char *actual = result.array[i]; + TEST_VERIFY_EXIT (strcmp (actual, expected) == 0); + free (expected); + free (actual); + } + free (result.array); + } + } + else /* !do_finalize */ + { + dynarray_str_free (&dyn); + CHECK_INIT_STATE (str, &dyn); + } + } + + /* Test resizing. */ + { + enum { count = 2131 }; + struct dynarray_str dyn; + dynarray_str_init (&dyn); + + /* From length 0 to length 1. */ + TEST_VERIFY (dynarray_str_resize (&dyn, 1)); + TEST_VERIFY (dynarray_str_size (&dyn) == 1); + TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL); + *dynarray_str_at (&dyn, 0) = xstrdup ("allocated"); + dynarray_str_free (&dyn); + + /* From length 0 to length 1 and 2. */ + TEST_VERIFY (dynarray_str_resize (&dyn, 1)); + TEST_VERIFY (dynarray_str_size (&dyn) == 1); + TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL); + *dynarray_str_at (&dyn, 0) = xstrdup ("allocated0"); + TEST_VERIFY (dynarray_str_resize (&dyn, 2)); + TEST_VERIFY (dynarray_str_size (&dyn) == 2); + TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0); + TEST_VERIFY (*dynarray_str_at (&dyn, 1) == NULL); + *dynarray_str_at (&dyn, 1) = xstrdup ("allocated1"); + TEST_VERIFY (dynarray_str_resize (&dyn, count)); + TEST_VERIFY (dynarray_str_size (&dyn) == count); + TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0); + TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0); + for (int i = 2; i < count; ++i) + TEST_VERIFY (*dynarray_str_at (&dyn, i) == NULL); + *dynarray_str_at (&dyn, count - 1) = xstrdup ("allocated2"); + TEST_VERIFY (dynarray_str_resize (&dyn, 3)); + TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0); + TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0); + TEST_VERIFY (*dynarray_str_at (&dyn, 2) == NULL); + dynarray_str_free (&dyn); + } +} + +/* Verify that DYNARRAY_ELEMENT_INIT has an effect. */ +static void +test_long_init (void) +{ + enum { count = 2131 }; + { + struct dynarray_long dyn; + dynarray_long_init (&dyn); + for (int i = 0; i < count; ++i) + { + long *place = dynarray_long_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + TEST_VERIFY (*place == 17); + } + TEST_VERIFY (dynarray_long_size (&dyn) == count); + for (int i = 0; i < count; ++i) + TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17); + dynarray_long_free (&dyn); + + TEST_VERIFY (dynarray_long_resize (&dyn, 1)); + TEST_VERIFY (dynarray_long_size (&dyn) == 1); + TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17); + *dynarray_long_at (&dyn, 0) = 18; + dynarray_long_free (&dyn); + TEST_VERIFY (dynarray_long_resize (&dyn, 1)); + TEST_VERIFY (dynarray_long_size (&dyn) == 1); + TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17); + TEST_VERIFY (dynarray_long_resize (&dyn, 2)); + TEST_VERIFY (dynarray_long_size (&dyn) == 2); + TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17); + TEST_VERIFY (*dynarray_long_at (&dyn, 1) == 17); + *dynarray_long_at (&dyn, 0) = 18; + TEST_VERIFY (dynarray_long_resize (&dyn, count)); + TEST_VERIFY (dynarray_long_size (&dyn) == count); + TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 18); + for (int i = 1; i < count; ++i) + TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17); + dynarray_long_free (&dyn); + } + + /* Similar, but without an on-stack scratch region + (DYNARRAY_INITIAL_SIZE is 0). */ + { + struct dynarray_long_noscratch dyn; + dynarray_long_noscratch_init (&dyn); + struct long_array result; + TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result)); + TEST_VERIFY (result.array == NULL); + TEST_VERIFY (result.length == 0); + + /* Test with one element. */ + { + long *place = dynarray_long_noscratch_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + TEST_VERIFY (*place == 23); + } + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23); + TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array != NULL); + TEST_VERIFY (result.length == 1); + TEST_VERIFY (result.array[0] == 23); + free (result.array); + + for (int i = 0; i < count; ++i) + { + long *place = dynarray_long_noscratch_emplace (&dyn); + TEST_VERIFY_EXIT (place != NULL); + TEST_VERIFY (*place == 23); + if (i == 0) + *place = 29; + } + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 29); + for (int i = 1; i < count; ++i) + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23); + TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result)); + TEST_VERIFY_EXIT (result.array != NULL); + TEST_VERIFY (result.length == count); + TEST_VERIFY (result.array[0] == 29); + for (int i = 1; i < count; ++i) + TEST_VERIFY (result.array[i] == 23); + free (result.array); + + TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1)); + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23); + *dynarray_long_noscratch_at (&dyn, 0) = 24; + dynarray_long_noscratch_free (&dyn); + TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1)); + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23); + TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 2)); + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 2); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 1) == 23); + *dynarray_long_noscratch_at (&dyn, 0) = 24; + TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, count)); + TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count); + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 24); + for (int i = 1; i < count; ++i) + TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23); + dynarray_long_noscratch_free (&dyn); + } +} + +/* Test NUL-terminated string construction with the add function and + the simple finalize function. */ +static void +test_zstr (void) +{ + /* Totally empty string (no NUL termination). */ + { + struct zstr s; + zstr_init (&s); + char *result = zstr_finalize (&s, NULL); + TEST_VERIFY (result == NULL); + TEST_VERIFY (zstr_size (&s) == 0); + size_t length = 1; + result = zstr_finalize (&s, &length); + TEST_VERIFY (result == NULL); + TEST_VERIFY (length == 0); + TEST_VERIFY (zstr_size (&s) == 0); + } + + /* Empty string. */ + { + struct zstr s; + zstr_init (&s); + zstr_add (&s, '\0'); + char *result = zstr_finalize (&s, NULL); + TEST_VERIFY_EXIT (result != NULL); + TEST_VERIFY (*result == '\0'); + TEST_VERIFY (zstr_size (&s) == 0); + free (result); + + zstr_add (&s, '\0'); + size_t length = 1; + result = zstr_finalize (&s, &length); + TEST_VERIFY_EXIT (result != NULL); + TEST_VERIFY (*result == '\0'); + TEST_VERIFY (length == 1); + TEST_VERIFY (zstr_size (&s) == 0); + free (result); + } + + /* A few characters. */ + { + struct zstr s; + zstr_init (&s); + zstr_add (&s, 'A'); + zstr_add (&s, 'b'); + zstr_add (&s, 'c'); + zstr_add (&s, '\0'); + char *result = zstr_finalize (&s, NULL); + TEST_VERIFY_EXIT (result != NULL); + TEST_VERIFY (strcmp (result, "Abc") == 0); + TEST_VERIFY (zstr_size (&s) == 0); + free (result); + + zstr_add (&s, 'X'); + zstr_add (&s, 'y'); + zstr_add (&s, 'z'); + zstr_add (&s, '\0'); + size_t length = 1; + result = zstr_finalize (&s, &length); + TEST_VERIFY_EXIT (result != NULL); + TEST_VERIFY (strcmp (result, "Xyz") == 0); + TEST_VERIFY (length == 4); + TEST_VERIFY (zstr_size (&s) == 0); + free (result); + } +} + +static int +do_test (void) +{ + mtrace (); + test_int (); + test_str (); + test_long_init (); + test_zstr (); + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-interpose-aux-nothread.c b/REORG.TODO/malloc/tst-interpose-aux-nothread.c new file mode 100644 index 0000000000..804bac3708 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-aux-nothread.c @@ -0,0 +1,20 @@ +/* Interposed malloc, version without threading support. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define INTERPOSE_THREADS 0 +#include "tst-interpose-aux.c" diff --git a/REORG.TODO/malloc/tst-interpose-aux-thread.c b/REORG.TODO/malloc/tst-interpose-aux-thread.c new file mode 100644 index 0000000000..107ff2b6a4 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-aux-thread.c @@ -0,0 +1,20 @@ +/* Interposed malloc, version with threading support. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define INTERPOSE_THREADS 1 +#include "tst-interpose-aux.c" diff --git a/REORG.TODO/malloc/tst-interpose-aux.c b/REORG.TODO/malloc/tst-interpose-aux.c new file mode 100644 index 0000000000..68282b41d5 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-aux.c @@ -0,0 +1,271 @@ +/* Minimal malloc implementation for interposition tests. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include "tst-interpose-aux.h" + +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/uio.h> +#include <unistd.h> + +#if INTERPOSE_THREADS +#include <pthread.h> +#endif + +/* Print the error message and terminate the process with status 1. */ +__attribute__ ((noreturn)) +__attribute__ ((format (printf, 1, 2))) +static void * +fail (const char *format, ...) +{ + /* This assumes that vsnprintf will not call malloc. It does not do + so for the format strings we use. */ + char message[4096]; + va_list ap; + va_start (ap, format); + vsnprintf (message, sizeof (message), format, ap); + va_end (ap); + + enum { count = 3 }; + struct iovec iov[count]; + + iov[0].iov_base = (char *) "error: "; + iov[1].iov_base = (char *) message; + iov[2].iov_base = (char *) "\n"; + + for (int i = 0; i < count; ++i) + iov[i].iov_len = strlen (iov[i].iov_base); + + int unused __attribute__ ((unused)); + unused = writev (STDOUT_FILENO, iov, count); + _exit (1); +} + +#if INTERPOSE_THREADS +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static void +lock (void) +{ +#if INTERPOSE_THREADS + int ret = pthread_mutex_lock (&mutex); + if (ret != 0) + { + errno = ret; + fail ("pthread_mutex_lock: %m"); + } +#endif +} + +static void +unlock (void) +{ +#if INTERPOSE_THREADS + int ret = pthread_mutex_unlock (&mutex); + if (ret != 0) + { + errno = ret; + fail ("pthread_mutex_unlock: %m"); + } +#endif +} + +struct __attribute__ ((aligned (__alignof__ (max_align_t)))) allocation_header +{ + size_t allocation_index; + size_t allocation_size; +}; + +/* Array of known allocations, to track invalid frees. */ +enum { max_allocations = 65536 }; +static struct allocation_header *allocations[max_allocations]; +static size_t allocation_index; +static size_t deallocation_count; + +/* Sanity check for successful malloc interposition. */ +__attribute__ ((destructor)) +static void +check_for_allocations (void) +{ + if (allocation_index == 0) + { + /* Make sure that malloc is called at least once from libc. */ + void *volatile ptr = strdup ("ptr"); + /* Compiler barrier. The strdup function calls malloc, which + updates allocation_index, but strdup is marked __THROW, so + the compiler could optimize away the reload. */ + __asm__ volatile ("" ::: "memory"); + free (ptr); + /* If the allocation count is still zero, it means we did not + interpose malloc successfully. */ + if (allocation_index == 0) + fail ("malloc does not seem to have been interposed"); + } +} + +static struct allocation_header *get_header (const char *op, void *ptr) +{ + struct allocation_header *header = ((struct allocation_header *) ptr) - 1; + if (header->allocation_index >= allocation_index) + fail ("%s: %p: invalid allocation index: %zu (not less than %zu)", + op, ptr, header->allocation_index, allocation_index); + if (allocations[header->allocation_index] != header) + fail ("%s: %p: allocation pointer does not point to header, but %p", + op, ptr, allocations[header->allocation_index]); + return header; +} + +/* Internal helper functions. Those must be called while the lock is + acquired. */ + +static void * +malloc_internal (size_t size) +{ + if (allocation_index == max_allocations) + { + errno = ENOMEM; + return NULL; + } + size_t allocation_size = size + sizeof (struct allocation_header); + if (allocation_size < size) + { + errno = ENOMEM; + return NULL; + } + + size_t index = allocation_index++; + void *result = mmap (NULL, allocation_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (result == MAP_FAILED) + return NULL; + allocations[index] = result; + *allocations[index] = (struct allocation_header) + { + .allocation_index = index, + .allocation_size = allocation_size + }; + return allocations[index] + 1; +} + +static void +free_internal (const char *op, struct allocation_header *header) +{ + size_t index = header->allocation_index; + int result = mprotect (header, header->allocation_size, PROT_NONE); + if (result != 0) + fail ("%s: mprotect (%p, %zu): %m", op, header, header->allocation_size); + /* Catch double-free issues. */ + allocations[index] = NULL; + ++deallocation_count; +} + +static void * +realloc_internal (void *ptr, size_t new_size) +{ + struct allocation_header *header = get_header ("realloc", ptr); + size_t old_size = header->allocation_size - sizeof (struct allocation_header); + if (old_size >= new_size) + return ptr; + + void *newptr = malloc_internal (new_size); + if (newptr == NULL) + return NULL; + memcpy (newptr, ptr, old_size); + free_internal ("realloc", header); + return newptr; +} + +/* Public interfaces. These functions must perform locking. */ + +size_t +malloc_allocation_count (void) +{ + lock (); + size_t count = allocation_index; + unlock (); + return count; +} + +size_t +malloc_deallocation_count (void) +{ + lock (); + size_t count = deallocation_count; + unlock (); + return count; +} +void * +malloc (size_t size) +{ + lock (); + void *result = malloc_internal (size); + unlock (); + return result; +} + +void +free (void *ptr) +{ + if (ptr == NULL) + return; + lock (); + struct allocation_header *header = get_header ("free", ptr); + free_internal ("free", header); + unlock (); +} + +void * +calloc (size_t a, size_t b) +{ + if (b > 0 && a > SIZE_MAX / b) + { + errno = ENOMEM; + return NULL; + } + lock (); + /* malloc_internal uses mmap, so the memory is zeroed. */ + void *result = malloc_internal (a * b); + unlock (); + return result; +} + +void * +realloc (void *ptr, size_t n) +{ + if (n ==0) + { + free (ptr); + return NULL; + } + else if (ptr == NULL) + return malloc (n); + else + { + lock (); + void *result = realloc_internal (ptr, n); + unlock (); + return result; + } +} diff --git a/REORG.TODO/malloc/tst-interpose-aux.h b/REORG.TODO/malloc/tst-interpose-aux.h new file mode 100644 index 0000000000..1388a73b06 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-aux.h @@ -0,0 +1,30 @@ +/* Statistics interface for the minimal malloc implementation. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifndef TST_INTERPOSE_AUX_H +#define TST_INTERPOSE_AUX_H + +#include <stddef.h> + +/* Return the number of allocations performed. */ +size_t malloc_allocation_count (void); + +/* Return the number of deallocations performed. */ +size_t malloc_deallocation_count (void); + +#endif /* TST_INTERPOSE_AUX_H */ diff --git a/REORG.TODO/malloc/tst-interpose-nothread.c b/REORG.TODO/malloc/tst-interpose-nothread.c new file mode 100644 index 0000000000..6beea3698a --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-nothread.c @@ -0,0 +1,20 @@ +/* Malloc interposition test, dynamically-linked version without threads. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define INTERPOSE_THREADS 0 +#include "tst-interpose-skeleton.c" diff --git a/REORG.TODO/malloc/tst-interpose-skeleton.c b/REORG.TODO/malloc/tst-interpose-skeleton.c new file mode 100644 index 0000000000..82a9078158 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-skeleton.c @@ -0,0 +1,204 @@ +/* Test driver for malloc interposition tests. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if INTERPOSE_THREADS +#include <pthread.h> +#endif + +static int do_test (void); +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +/* Fills BUFFER with a test string. */ +static void +line_string (int number, char *buffer, size_t length) +{ + for (size_t i = 0; i < length - 2; ++i) + buffer[i] = 'A' + ((number + i) % 26); + buffer[length - 2] = '\n'; + buffer[length - 1] = '\0'; +} + +/* Perform the tests. */ +static void * +run_tests (void *closure) +{ + char *temp_file_path; + int fd = create_temp_file ("tst-malloc-interpose", &temp_file_path); + if (fd < 0) + _exit (1); + + /* Line lengths excluding the line terminator. */ + static const int line_lengths[] = { 0, 45, 80, 2, 8201, 0, 17, -1 }; + + /* Fill the test file with data. */ + { + FILE *fp = fdopen (fd, "w"); + for (int lineno = 0; line_lengths[lineno] >= 0; ++lineno) + { + char buffer[line_lengths[lineno] + 2]; + line_string (lineno, buffer, sizeof (buffer)); + fprintf (fp, "%s", buffer); + } + + if (ferror (fp)) + { + printf ("error: fprintf: %m\n"); + _exit (1); + } + if (fclose (fp) != 0) + { + printf ("error: fclose: %m\n"); + _exit (1); + } + } + + /* Read the test file. This tests libc-internal allocation with + realloc. */ + { + FILE *fp = fopen (temp_file_path, "r"); + + char *actual = NULL; + size_t actual_size = 0; + for (int lineno = 0; ; ++lineno) + { + errno = 0; + ssize_t result = getline (&actual, &actual_size, fp); + if (result == 0) + { + printf ("error: invalid return value 0 from getline\n"); + _exit (1); + } + if (result < 0 && errno != 0) + { + printf ("error: getline: %m\n"); + _exit (1); + } + if (result < 0 && line_lengths[lineno] >= 0) + { + printf ("error: unexpected end of file after line %d\n", lineno); + _exit (1); + } + if (result > 0 && line_lengths[lineno] < 0) + { + printf ("error: no end of file after line %d\n", lineno); + _exit (1); + } + if (result == -1 && line_lengths[lineno] == -1) + /* End of file reached as expected. */ + break; + + if (result != line_lengths[lineno] + 1) + { + printf ("error: line length mismatch: expected %d, got %zd\n", + line_lengths[lineno], result); + _exit (1); + } + + char expected[line_lengths[lineno] + 2]; + line_string (lineno, expected, sizeof (expected)); + if (strcmp (actual, expected) != 0) + { + printf ("error: line mismatch\n"); + printf ("error: expected: [[%s]]\n", expected); + printf ("error: actual: [[%s]]\n", actual); + _exit (1); + } + } + + if (fclose (fp) != 0) + { + printf ("error: fclose (after reading): %m\n"); + _exit (1); + } + } + + free (temp_file_path); + + /* Make sure that fork is working. */ + pid_t pid = fork (); + if (pid == -1) + { + printf ("error: fork: %m\n"); + _exit (1); + } + enum { exit_code = 55 }; + if (pid == 0) + _exit (exit_code); + int status; + int ret = waitpid (pid, &status, 0); + if (ret < 0) + { + printf ("error: waitpid: %m\n"); + _exit (1); + } + if (!WIFEXITED (status) || WEXITSTATUS (status) != exit_code) + { + printf ("error: unexpected exit status from child process: %d\n", + status); + _exit (1); + } + + return NULL; +} + +/* This is used to detect if malloc has not been successfully + interposed. The interposed malloc does not use brk/sbrk. */ +static void *initial_brk; +__attribute__ ((constructor)) +static void +set_initial_brk (void) +{ + initial_brk = sbrk (0); +} + +/* Terminate the process if the break value has been changed. */ +__attribute__ ((destructor)) +static void +check_brk (void) +{ + void *current = sbrk (0); + if (current != initial_brk) + { + printf ("error: brk changed from %p to %p; no interposition?\n", + initial_brk, current); + _exit (1); + } +} + +static int +do_test (void) +{ + check_brk (); + +#if INTERPOSE_THREADS + pthread_t thr = xpthread_create (NULL, run_tests, NULL); + xpthread_join (thr); +#else + run_tests (NULL); +#endif + + check_brk (); + + return 0; +} diff --git a/REORG.TODO/malloc/tst-interpose-static-nothread.c b/REORG.TODO/malloc/tst-interpose-static-nothread.c new file mode 100644 index 0000000000..ab3d464859 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-static-nothread.c @@ -0,0 +1,19 @@ +/* Malloc interposition test, static version without threads. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include "tst-interpose-nothread.c" diff --git a/REORG.TODO/malloc/tst-interpose-static-thread.c b/REORG.TODO/malloc/tst-interpose-static-thread.c new file mode 100644 index 0000000000..80ddcd4fce --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-static-thread.c @@ -0,0 +1,19 @@ +/* Malloc interposition test, static version with threads. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include "tst-interpose-nothread.c" diff --git a/REORG.TODO/malloc/tst-interpose-thread.c b/REORG.TODO/malloc/tst-interpose-thread.c new file mode 100644 index 0000000000..d09c959c90 --- /dev/null +++ b/REORG.TODO/malloc/tst-interpose-thread.c @@ -0,0 +1,20 @@ +/* Malloc interposition test, dynamically-linked version with threads. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define INTERPOSE_THREADS 1 +#include "tst-interpose-skeleton.c" diff --git a/REORG.TODO/malloc/tst-malloc-backtrace.c b/REORG.TODO/malloc/tst-malloc-backtrace.c new file mode 100644 index 0000000000..012ff81caa --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-backtrace.c @@ -0,0 +1,53 @@ +/* Verify that backtrace does not deadlock on itself on memory corruption. + Copyright (C) 2015-2017 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/>. */ + +#include <signal.h> +#include <stdlib.h> + +#include <support/support.h> + +#define SIZE 4096 + +/* Wrap free with a function to prevent gcc from optimizing it out. */ +static void +__attribute__((noinline)) +call_free (void *ptr) +{ + free (ptr); + *(size_t *)(ptr - sizeof (size_t)) = 1; +} + +int +do_test (void) +{ + void *ptr1 = malloc (SIZE); + void *ptr2 = malloc (SIZE); + + /* Avoid unwanted output to TTY after an expected memory corruption. */ + ignore_stderr(); + + call_free (ptr1); + ptr1 = malloc (SIZE); + + /* Not reached. The return statement is to put ptr2 into use so that gcc + doesn't optimize out that malloc call. */ + return (ptr1 == ptr2); +} + +#define EXPECTED_SIGNAL SIGABRT +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-malloc-fork-deadlock.c b/REORG.TODO/malloc/tst-malloc-fork-deadlock.c new file mode 100644 index 0000000000..30d3df3b08 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-fork-deadlock.c @@ -0,0 +1,206 @@ +/* Test concurrent fork, getline, and fflush (NULL). + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> +#include <malloc.h> +#include <time.h> +#include <string.h> +#include <signal.h> + +#include <support/xthread.h> +#include <support/temp_file.h> +#include <support/test-driver.h> + +enum { + /* Number of threads which call fork. */ + fork_thread_count = 4, + /* Number of threads which call getline (and, indirectly, + malloc). */ + read_thread_count = 8, +}; + +static bool termination_requested; + +static void * +fork_thread_function (void *closure) +{ + while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) + { + pid_t pid = fork (); + if (pid < 0) + { + printf ("error: fork: %m\n"); + abort (); + } + else if (pid == 0) + _exit (17); + + int status; + if (waitpid (pid, &status, 0) < 0) + { + printf ("error: waitpid: %m\n"); + abort (); + } + if (!WIFEXITED (status) || WEXITSTATUS (status) != 17) + { + printf ("error: waitpid returned invalid status: %d\n", status); + abort (); + } + } + return NULL; +} + +static char *file_to_read; + +static void * +read_thread_function (void *closure) +{ + FILE *f = fopen (file_to_read, "r"); + if (f == NULL) + { + printf ("error: fopen (%s): %m\n", file_to_read); + abort (); + } + + while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) + { + rewind (f); + char *line = NULL; + size_t line_allocated = 0; + ssize_t ret = getline (&line, &line_allocated, f); + if (ret < 0) + { + printf ("error: getline: %m\n"); + abort (); + } + free (line); + } + fclose (f); + + return NULL; +} + +static void * +flushall_thread_function (void *closure) +{ + while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) + if (fflush (NULL) != 0) + { + printf ("error: fflush (NULL): %m\n"); + abort (); + } + return NULL; +} + +static void +create_threads (pthread_t *threads, size_t count, void *(*func) (void *)) +{ + for (size_t i = 0; i < count; ++i) + threads[i] = xpthread_create (NULL, func, NULL); +} + +static void +join_threads (pthread_t *threads, size_t count) +{ + for (size_t i = 0; i < count; ++i) + xpthread_join (threads[i]); +} + +/* Create a file which consists of a single long line, and assigns + file_to_read. The hope is that this triggers an allocation in + getline which needs a lock. */ +static void +create_file_with_large_line (void) +{ + int fd = create_temp_file ("bug19431-large-line", &file_to_read); + if (fd < 0) + { + printf ("error: create_temp_file: %m\n"); + abort (); + } + FILE *f = fdopen (fd, "w+"); + if (f == NULL) + { + printf ("error: fdopen: %m\n"); + abort (); + } + for (int i = 0; i < 50000; ++i) + fputc ('x', f); + fputc ('\n', f); + if (ferror (f)) + { + printf ("error: fputc: %m\n"); + abort (); + } + if (fclose (f) != 0) + { + printf ("error: fclose: %m\n"); + abort (); + } +} + +static int +do_test (void) +{ + /* Make sure that we do not exceed the arena limit with the number + of threads we configured. */ + if (mallopt (M_ARENA_MAX, 400) == 0) + { + printf ("error: mallopt (M_ARENA_MAX) failed\n"); + return 1; + } + + /* Leave some room for shutting down all threads gracefully. */ + int timeout = 3; + if (timeout > DEFAULT_TIMEOUT) + timeout = DEFAULT_TIMEOUT - 1; + + create_file_with_large_line (); + + pthread_t fork_threads[fork_thread_count]; + create_threads (fork_threads, fork_thread_count, fork_thread_function); + pthread_t read_threads[read_thread_count]; + create_threads (read_threads, read_thread_count, read_thread_function); + pthread_t flushall_threads[1]; + create_threads (flushall_threads, 1, flushall_thread_function); + + struct timespec ts = {timeout, 0}; + if (nanosleep (&ts, NULL)) + { + printf ("error: error: nanosleep: %m\n"); + abort (); + } + + __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED); + + join_threads (flushall_threads, 1); + join_threads (read_threads, read_thread_count); + join_threads (fork_threads, fork_thread_count); + + free (file_to_read); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-malloc-thread-exit.c b/REORG.TODO/malloc/tst-malloc-thread-exit.c new file mode 100644 index 0000000000..8a0909b2f8 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-thread-exit.c @@ -0,0 +1,137 @@ +/* Test malloc with concurrent thread termination. + Copyright (C) 2015-2017 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 thread spawns a number of outer threads, equal to the arena + limit. The outer threads run a loop which start and join two + different kinds of threads: the first kind allocates (attaching an + arena to the thread; malloc_first_thread) and waits, the second + kind waits and allocates (wait_first_threads). Both kinds of + threads exit immediately after waiting. The hope is that this will + exhibit races in thread termination and arena management, + particularly related to the arena free list. */ + +#include <errno.h> +#include <malloc.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <support/support.h> +#include <support/xthread.h> +#include <support/test-driver.h> + +static bool termination_requested; +static int inner_thread_count = 4; +static size_t malloc_size = 32; + +static void +__attribute__ ((noinline, noclone)) +unoptimized_free (void *ptr) +{ + free (ptr); +} + +static void * +malloc_first_thread (void * closure) +{ + pthread_barrier_t *barrier = closure; + void *ptr = xmalloc (malloc_size); + xpthread_barrier_wait (barrier); + unoptimized_free (ptr); + return NULL; +} + +static void * +wait_first_thread (void * closure) +{ + pthread_barrier_t *barrier = closure; + xpthread_barrier_wait (barrier); + void *ptr = xmalloc (malloc_size); + unoptimized_free (ptr); + return NULL; +} + +static void * +outer_thread (void *closure) +{ + pthread_t *threads = xcalloc (sizeof (*threads), inner_thread_count); + while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) + { + pthread_barrier_t barrier; + xpthread_barrier_init (&barrier, NULL, inner_thread_count + 1); + for (int i = 0; i < inner_thread_count; ++i) + { + void *(*func) (void *); + if ((i % 2) == 0) + func = malloc_first_thread; + else + func = wait_first_thread; + threads[i] = xpthread_create (NULL, func, &barrier); + } + xpthread_barrier_wait (&barrier); + for (int i = 0; i < inner_thread_count; ++i) + xpthread_join (threads[i]); + xpthread_barrier_destroy (&barrier); + } + + free (threads); + + return NULL; +} + +static int +do_test (void) +{ + /* The number of threads should be smaller than the number of + arenas, so that there will be some free arenas to add to the + arena free list. */ + enum { outer_thread_count = 2 }; + if (mallopt (M_ARENA_MAX, 8) == 0) + { + printf ("error: mallopt (M_ARENA_MAX) failed\n"); + return 1; + } + + /* Leave some room for shutting down all threads gracefully. */ + int timeout = 3; + if (timeout > DEFAULT_TIMEOUT) + timeout = DEFAULT_TIMEOUT - 1; + + pthread_t *threads = xcalloc (sizeof (*threads), outer_thread_count); + for (long i = 0; i < outer_thread_count; ++i) + threads[i] = xpthread_create (NULL, outer_thread, NULL); + + struct timespec ts = {timeout, 0}; + if (nanosleep (&ts, NULL)) + { + printf ("error: error: nanosleep: %m\n"); + abort (); + } + + __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED); + + for (long i = 0; i < outer_thread_count; ++i) + xpthread_join (threads[i]); + free (threads); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-malloc-thread-fail.c b/REORG.TODO/malloc/tst-malloc-thread-fail.c new file mode 100644 index 0000000000..2745a33856 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-thread-fail.c @@ -0,0 +1,442 @@ +/* Test allocation function behavior on allocation failure. + Copyright (C) 2015-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +/* This test case attempts to trigger various unusual conditions + related to allocation failures, notably switching to a different + arena, and falling back to mmap (via sysmalloc). */ + +#include <errno.h> +#include <malloc.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <unistd.h> + +/* Wrapper for calloc with an optimization barrier. */ +static void * +__attribute__ ((noinline, noclone)) +allocate_zeroed (size_t a, size_t b) +{ + return calloc (a, b); +} + +/* System page size, as determined by sysconf (_SC_PAGE_SIZE). */ +static unsigned long page_size; + +/* Test parameters. */ +static size_t allocation_size; +static size_t alignment; +static enum { + with_malloc, + with_realloc, + with_aligned_alloc, + with_memalign, + with_posix_memalign, + with_valloc, + with_pvalloc, + with_calloc, + last_allocation_function = with_calloc +} allocation_function; + +/* True if an allocation function uses the alignment test + parameter. */ +const static bool alignment_sensitive[last_allocation_function + 1] = + { + [with_aligned_alloc] = true, + [with_memalign] = true, + [with_posix_memalign] = true, + }; + +/* Combined pointer/expected alignment result of an allocation + function. */ +struct allocate_result { + void *pointer; + size_t alignment; +}; + +/* Call the allocation function specified by allocation_function, with + allocation_size and alignment (if applicable) as arguments. No + alignment check. */ +static struct allocate_result +allocate_1 (void) +{ + switch (allocation_function) + { + case with_malloc: + return (struct allocate_result) + {malloc (allocation_size), _Alignof (max_align_t)}; + case with_realloc: + { + void *p = realloc (NULL, 16); + void *q; + if (p == NULL) + q = NULL; + else + { + q = realloc (p, allocation_size); + if (q == NULL) + free (p); + } + return (struct allocate_result) {q, _Alignof (max_align_t)}; + } + case with_aligned_alloc: + { + void *p = aligned_alloc (alignment, allocation_size); + return (struct allocate_result) {p, alignment}; + } + case with_memalign: + { + void *p = memalign (alignment, allocation_size); + return (struct allocate_result) {p, alignment}; + } + case with_posix_memalign: + { + void *p; + if (posix_memalign (&p, alignment, allocation_size)) + { + if (errno == ENOMEM) + p = NULL; + else + { + printf ("error: posix_memalign (p, %zu, %zu): %m\n", + alignment, allocation_size); + abort (); + } + } + return (struct allocate_result) {p, alignment}; + } + case with_valloc: + { + void *p = valloc (allocation_size); + return (struct allocate_result) {p, page_size}; + } + case with_pvalloc: + { + void *p = pvalloc (allocation_size); + return (struct allocate_result) {p, page_size}; + } + case with_calloc: + { + char *p = allocate_zeroed (1, allocation_size); + /* Check for non-zero bytes. */ + if (p != NULL) + for (size_t i = 0; i < allocation_size; ++i) + if (p[i] != 0) + { + printf ("error: non-zero byte at offset %zu\n", i); + abort (); + } + return (struct allocate_result) {p, _Alignof (max_align_t)}; + } + } + abort (); +} + +/* Call allocate_1 and perform the alignment check on the result. */ +static void * +allocate (void) +{ + struct allocate_result r = allocate_1 (); + if ((((uintptr_t) r.pointer) & (r.alignment - 1)) != 0) + { + printf ("error: allocation function %d, size %zu not aligned to %zu\n", + (int) allocation_function, allocation_size, r.alignment); + abort (); + } + return r.pointer; +} + +/* Barriers to synchronize thread creation and termination. */ +static pthread_barrier_t start_barrier; +static pthread_barrier_t end_barrier; + +/* Thread function which performs the allocation test. Called by + pthread_create and from the main thread. */ +static void * +allocate_thread (void *closure) +{ + /* Wait for the creation of all threads. */ + { + int ret = pthread_barrier_wait (&start_barrier); + if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) + { + errno = ret; + printf ("error: pthread_barrier_wait: %m\n"); + abort (); + } + } + + /* Allocate until we run out of memory, creating a single-linked + list. */ + struct list { + struct list *next; + }; + struct list *head = NULL; + while (true) + { + struct list *e = allocate (); + if (e == NULL) + break; + + e->next = head; + head = e; + } + + /* Wait for the allocation of all available memory. */ + { + int ret = pthread_barrier_wait (&end_barrier); + if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) + { + errno = ret; + printf ("error: pthread_barrier_wait: %m\n"); + abort (); + } + } + + /* Free the allocated memory. */ + while (head != NULL) + { + struct list *next = head->next; + free (head); + head = next; + } + + return NULL; +} + +/* Number of threads (plus the main thread. */ +enum { thread_count = 8 }; + +/* Thread attribute to request creation of threads with a non-default + stack size which is rather small. This avoids interfering with the + configured address space limit. */ +static pthread_attr_t small_stack; + +/* Runs one test in multiple threads, all in a subprocess so that + subsequent tests do not interfere with each other. */ +static void +run_one (void) +{ + /* Isolate the tests in a subprocess, so that we can start over + from scratch. */ + pid_t pid = fork (); + if (pid == 0) + { + /* In the child process. Create the allocation threads. */ + pthread_t threads[thread_count]; + + for (unsigned i = 0; i < thread_count; ++i) + { + int ret = pthread_create (threads + i, &small_stack, allocate_thread, NULL); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_create: %m\n"); + abort (); + } + } + + /* Also run the test on the main thread. */ + allocate_thread (NULL); + + for (unsigned i = 0; i < thread_count; ++i) + { + int ret = pthread_join (threads[i], NULL); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_join: %m\n"); + abort (); + } + } + _exit (0); + } + else if (pid < 0) + { + printf ("error: fork: %m\n"); + abort (); + } + + /* In the parent process. Wait for the child process to exit. */ + int status; + if (waitpid (pid, &status, 0) < 0) + { + printf ("error: waitpid: %m\n"); + abort (); + } + if (status != 0) + { + printf ("error: exit status %d from child process\n", status); + exit (1); + } +} + +/* Run all applicable allocation functions for the current test + parameters. */ +static void +run_allocation_functions (void) +{ + for (int af = 0; af <= last_allocation_function; ++af) + { + /* Run alignment-sensitive functions for non-default + alignments. */ + if (alignment_sensitive[af] != (alignment != 0)) + continue; + allocation_function = af; + run_one (); + } +} + +int +do_test (void) +{ + /* Limit the number of malloc arenas. We use a very low number so + that despute the address space limit configured below, all + requested arenas a can be created. */ + if (mallopt (M_ARENA_MAX, 2) == 0) + { + printf ("error: mallopt (M_ARENA_MAX) failed\n"); + return 1; + } + + /* Determine the page size. */ + { + long ret = sysconf (_SC_PAGE_SIZE); + if (ret < 0) + { + printf ("error: sysconf (_SC_PAGE_SIZE): %m\n"); + return 1; + } + page_size = ret; + } + + /* Limit the size of the process, so that memory allocation in + allocate_thread will eventually fail, without impacting the + entire system. */ + { + struct rlimit limit; + if (getrlimit (RLIMIT_AS, &limit) != 0) + { + printf ("getrlimit (RLIMIT_AS) failed: %m\n"); + return 1; + } + long target = 200 * 1024 * 1024; + if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target) + { + limit.rlim_cur = target; + if (setrlimit (RLIMIT_AS, &limit) != 0) + { + printf ("setrlimit (RLIMIT_AS) failed: %m\n"); + return 1; + } + } + } + + /* Initialize thread attribute with a reduced stack size. */ + { + int ret = pthread_attr_init (&small_stack); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_attr_init: %m\n"); + abort (); + } + unsigned long stack_size = ((256 * 1024) / page_size) * page_size; + if (stack_size < 4 * page_size) + stack_size = 8 * page_size; + ret = pthread_attr_setstacksize (&small_stack, stack_size); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_attr_setstacksize: %m\n"); + abort (); + } + } + + /* Initialize the barriers. We run thread_count threads, plus 1 for + the main thread. */ + { + int ret = pthread_barrier_init (&start_barrier, NULL, thread_count + 1); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_barrier_init: %m\n"); + abort (); + } + + ret = pthread_barrier_init (&end_barrier, NULL, thread_count + 1); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_barrier_init: %m\n"); + abort (); + } + } + + allocation_size = 144; + run_allocation_functions (); + allocation_size = page_size; + run_allocation_functions (); + + alignment = 128; + allocation_size = 512; + run_allocation_functions (); + + allocation_size = page_size; + run_allocation_functions (); + + allocation_size = 17 * page_size; + run_allocation_functions (); + + /* Deallocation the barriers and the thread attribute. */ + { + int ret = pthread_barrier_destroy (&end_barrier); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_barrier_destroy: %m\n"); + return 1; + } + ret = pthread_barrier_destroy (&start_barrier); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_barrier_destroy: %m\n"); + return 1; + } + ret = pthread_attr_destroy (&small_stack); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_attr_destroy: %m\n"); + return 1; + } + } + + return 0; +} + +/* The repeated allocations take some time on slow machines. */ +#define TIMEOUT 100 + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-malloc-usable-static-tunables.c b/REORG.TODO/malloc/tst-malloc-usable-static-tunables.c new file mode 100644 index 0000000000..8907db01a5 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-usable-static-tunables.c @@ -0,0 +1 @@ +#include <malloc/tst-malloc-usable.c> diff --git a/REORG.TODO/malloc/tst-malloc-usable-static.c b/REORG.TODO/malloc/tst-malloc-usable-static.c new file mode 100644 index 0000000000..8907db01a5 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-usable-static.c @@ -0,0 +1 @@ +#include <malloc/tst-malloc-usable.c> diff --git a/REORG.TODO/malloc/tst-malloc-usable-tunables.c b/REORG.TODO/malloc/tst-malloc-usable-tunables.c new file mode 100644 index 0000000000..8907db01a5 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-usable-tunables.c @@ -0,0 +1 @@ +#include <malloc/tst-malloc-usable.c> diff --git a/REORG.TODO/malloc/tst-malloc-usable.c b/REORG.TODO/malloc/tst-malloc-usable.c new file mode 100644 index 0000000000..6a64d204c7 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc-usable.c @@ -0,0 +1,49 @@ +/* Ensure that malloc_usable_size returns the request size with + MALLOC_CHECK_ exported to a positive value. + + Copyright (C) 2012-2017 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/>. */ + +#include <malloc.h> +#include <string.h> +#include <stdio.h> + +static int +do_test (void) +{ + size_t usable_size; + void *p = malloc (7); + if (!p) + { + printf ("memory allocation failed\n"); + return 1; + } + + usable_size = malloc_usable_size (p); + if (usable_size != 7) + { + printf ("malloc_usable_size: expected 7 but got %zu\n", usable_size); + return 1; + } + + memset (p, 0, usable_size); + free (p); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-malloc.c b/REORG.TODO/malloc/tst-malloc.c new file mode 100644 index 0000000000..dbc8d4ab56 --- /dev/null +++ b/REORG.TODO/malloc/tst-malloc.c @@ -0,0 +1,95 @@ +/* Copyright (C) 1999-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@arthur.rhein-neckar.de>, 1999. + + 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/>. */ + +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <libc-diag.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p, *q; + int save; + + errno = 0; + + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + p = malloc (-1); + DIAG_POP_NEEDS_COMMENT; + save = errno; + + if (p != NULL) + merror ("malloc (-1) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("errno is not set correctly"); + + p = malloc (10); + if (p == NULL) + merror ("malloc (10) failed."); + + /* realloc (p, 0) == free (p). */ + p = realloc (p, 0); + if (p != NULL) + merror ("realloc (p, 0) failed."); + + p = malloc (0); + if (p == NULL) + merror ("malloc (0) failed."); + + p = realloc (p, 0); + if (p != NULL) + merror ("realloc (p, 0) failed."); + + p = malloc (513 * 1024); + if (p == NULL) + merror ("malloc (513K) failed."); + + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + q = malloc (-512 * 1024); + DIAG_POP_NEEDS_COMMENT; + if (q != NULL) + merror ("malloc (-512K) succeeded."); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-mallocfork.c b/REORG.TODO/malloc/tst-mallocfork.c new file mode 100644 index 0000000000..f90ce94887 --- /dev/null +++ b/REORG.TODO/malloc/tst-mallocfork.c @@ -0,0 +1,51 @@ +/* Derived from the test case in + http://sourceware.org/bugzilla/show_bug.cgi?id=838. */ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +static void +sig_handler (int signum) +{ + pid_t child = fork (); + if (child == 0) + exit (0); + TEMP_FAILURE_RETRY (waitpid (child, NULL, 0)); +} + +static int +do_test (void) +{ + pid_t parent = getpid (); + + struct sigaction action = { .sa_handler = sig_handler }; + sigemptyset (&action.sa_mask); + + malloc (sizeof (int)); + + if (sigaction (SIGALRM, &action, NULL) != 0) + { + puts ("sigaction failed"); + return 1; + } + + /* Create a child that sends the signal to be caught. */ + pid_t child = fork (); + if (child == 0) + { + if (kill (parent, SIGALRM) == -1) + perror ("kill"); + exit (0); + } + + TEMP_FAILURE_RETRY (waitpid (child, NULL, 0)); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-mallocfork2.c b/REORG.TODO/malloc/tst-mallocfork2.c new file mode 100644 index 0000000000..bb6a3319d8 --- /dev/null +++ b/REORG.TODO/malloc/tst-mallocfork2.c @@ -0,0 +1,211 @@ +/* Test case for async-signal-safe fork (with respect to malloc). + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +/* This test will fail if the process is multi-threaded because we + only have an async-signal-safe fork in the single-threaded case + (where we skip acquiring the malloc heap locks). + + This test only checks async-signal-safety with regards to malloc; + other, more rarely-used glibc subsystems could have locks which + still make fork unsafe, even in single-threaded processes. */ + +#include <errno.h> +#include <sched.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> + +/* How many malloc objects to keep arond. */ +enum { malloc_objects = 1009 }; + +/* The maximum size of an object. */ +enum { malloc_maximum_size = 70000 }; + +/* How many signals need to be delivered before the test exits. */ +enum { signal_count = 1000 }; + +static int do_test (void); +#define TIMEOUT 100 +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +/* Process ID of the subprocess which sends SIGUSR1 signals. */ +static pid_t sigusr1_sender_pid; + +/* Set to 1 if SIGUSR1 is received. Used to detect a signal during + malloc/free. */ +static volatile sig_atomic_t sigusr1_received; + +/* Periodically set to 1, to indicate that the process is making + progress. Checked by liveness_signal_handler. */ +static volatile sig_atomic_t progress_indicator = 1; + +static void +sigusr1_handler (int signo) +{ + /* Let the main program make progress, by temporarily suspending + signals from the subprocess. */ + if (sigusr1_received) + return; + /* sigusr1_sender_pid might not be initialized in the parent when + the first SIGUSR1 signal arrives. */ + if (sigusr1_sender_pid > 0 && kill (sigusr1_sender_pid, SIGSTOP) != 0) + { + write_message ("error: kill (SIGSTOP)\n"); + abort (); + } + sigusr1_received = 1; + + /* Perform a fork with a trivial subprocess. */ + pid_t pid = fork (); + if (pid == -1) + { + write_message ("error: fork\n"); + abort (); + } + if (pid == 0) + _exit (0); + int status; + int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (ret < 0) + { + write_message ("error: waitpid\n"); + abort (); + } + if (status != 0) + { + write_message ("error: unexpected exit status from subprocess\n"); + abort (); + } +} + +static void +liveness_signal_handler (int signo) +{ + if (progress_indicator) + progress_indicator = 0; + else + write_message ("warning: process seems to be stuck\n"); +} + +static void +__attribute__ ((noreturn)) +signal_sender (int signo, bool sleep) +{ + pid_t target = getppid (); + while (true) + { + if (kill (target, signo) != 0) + { + dprintf (STDOUT_FILENO, "error: kill: %m\n"); + abort (); + } + if (sleep) + usleep (1 * 1000 * 1000); + else + /* Reduce the rate at which we send signals. */ + sched_yield (); + } +} + +static int +do_test (void) +{ + struct sigaction action = + { + .sa_handler = sigusr1_handler, + }; + sigemptyset (&action.sa_mask); + + if (sigaction (SIGUSR1, &action, NULL) != 0) + { + printf ("error: sigaction: %m"); + return 1; + } + + action.sa_handler = liveness_signal_handler; + if (sigaction (SIGUSR2, &action, NULL) != 0) + { + printf ("error: sigaction: %m"); + return 1; + } + + pid_t sigusr2_sender_pid = fork (); + if (sigusr2_sender_pid == 0) + signal_sender (SIGUSR2, true); + sigusr1_sender_pid = fork (); + if (sigusr1_sender_pid == 0) + signal_sender (SIGUSR1, false); + + void *objects[malloc_objects] = {}; + unsigned signals = 0; + unsigned seed = 1; + time_t last_report = 0; + while (signals < signal_count) + { + progress_indicator = 1; + int slot = rand_r (&seed) % malloc_objects; + size_t size = rand_r (&seed) % malloc_maximum_size; + if (kill (sigusr1_sender_pid, SIGCONT) != 0) + { + printf ("error: kill (SIGCONT): %m\n"); + signal (SIGUSR1, SIG_IGN); + kill (sigusr1_sender_pid, SIGKILL); + kill (sigusr2_sender_pid, SIGKILL); + return 1; + } + sigusr1_received = false; + free (objects[slot]); + objects[slot] = malloc (size); + if (sigusr1_received) + { + ++signals; + time_t current = time (0); + if (current != last_report) + { + printf ("info: SIGUSR1 signal count: %u\n", signals); + last_report = current; + } + } + if (objects[slot] == NULL) + { + printf ("error: malloc: %m\n"); + signal (SIGUSR1, SIG_IGN); + kill (sigusr1_sender_pid, SIGKILL); + kill (sigusr2_sender_pid, SIGKILL); + return 1; + } + } + + /* Clean up allocations. */ + for (int slot = 0; slot < malloc_objects; ++slot) + free (objects[slot]); + + /* Terminate the signal-sending subprocess. The SIGUSR1 handler + should no longer run because it uses sigusr1_sender_pid. */ + signal (SIGUSR1, SIG_IGN); + kill (sigusr1_sender_pid, SIGKILL); + kill (sigusr2_sender_pid, SIGKILL); + + return 0; +} diff --git a/REORG.TODO/malloc/tst-mallocstate.c b/REORG.TODO/malloc/tst-mallocstate.c new file mode 100644 index 0000000000..5cb39c0f3d --- /dev/null +++ b/REORG.TODO/malloc/tst-mallocstate.c @@ -0,0 +1,505 @@ +/* Emulate Emacs heap dumping to test malloc_set_state. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Wolfram Gloger <wg@malloc.de>, 2001. + + 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/>. */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <libc-symbols.h> +#include <shlib-compat.h> + +#include "malloc.h" + +/* Make the compatibility symbols availabile to this test case. */ +void *malloc_get_state (void); +compat_symbol_reference (libc, malloc_get_state, malloc_get_state, GLIBC_2_0); +int malloc_set_state (void *); +compat_symbol_reference (libc, malloc_set_state, malloc_set_state, GLIBC_2_0); + +static int do_test (void); +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +/* Maximum object size in the fake heap. */ +enum { max_size = 64 }; + +/* Allocation actions. These are randomized actions executed on the + dumped heap (see allocation_tasks below). They are interspersed + with operations on the new heap (see heap_activity). */ +enum allocation_action + { + action_free, /* Dumped and freed. */ + action_realloc, /* Dumped and realloc'ed. */ + action_realloc_same, /* Dumped and realloc'ed, same size. */ + action_realloc_smaller, /* Dumped and realloc'ed, shrinked. */ + action_count + }; + +/* Dumped heap. Initialize it, so that the object is placed into the + .data section, for increased realism. The size is an upper bound; + we use about half of the space. */ +static size_t dumped_heap[action_count * max_size * max_size + / sizeof (size_t)] = {1}; + +/* Next free space in the dumped heap. Also top of the heap at the + end of the initialization procedure. */ +static size_t *next_heap_chunk; + +/* Copied from malloc.c and hooks.c. The version is deliberately + lower than the final version of malloc_set_state. */ +#define NBINS 128 +#define MALLOC_STATE_MAGIC 0x444c4541l +#define MALLOC_STATE_VERSION (0 * 0x100l + 4l) +static struct +{ + long magic; + long version; + void *av[NBINS * 2 + 2]; + char *sbrk_base; + int sbrked_mem_bytes; + unsigned long trim_threshold; + unsigned long top_pad; + unsigned int n_mmaps_max; + unsigned long mmap_threshold; + int check_action; + unsigned long max_sbrked_mem; + unsigned long max_total_mem; + unsigned int n_mmaps; + unsigned int max_n_mmaps; + unsigned long mmapped_mem; + unsigned long max_mmapped_mem; + int using_malloc_checking; + unsigned long max_fast; + unsigned long arena_test; + unsigned long arena_max; + unsigned long narenas; +} save_state = + { + .magic = MALLOC_STATE_MAGIC, + .version = MALLOC_STATE_VERSION, + }; + +/* Allocate a blob in the fake heap. */ +static void * +dumped_heap_alloc (size_t length) +{ + /* malloc needs three state bits in the size field, so the minimum + alignment is 8 even on 32-bit architectures. malloc_set_state + should be compatible with such heaps even if it currently + provides more alignment to applications. */ + enum + { + heap_alignment = 8, + heap_alignment_mask = heap_alignment - 1 + }; + _Static_assert (sizeof (size_t) <= heap_alignment, + "size_t compatible with heap alignment"); + + /* Need at least this many bytes for metadata and application + data. */ + size_t chunk_size = sizeof (size_t) + length; + /* Round up the allocation size to the heap alignment. */ + chunk_size += heap_alignment_mask; + chunk_size &= ~heap_alignment_mask; + if ((chunk_size & 3) != 0) + { + /* The lower three bits in the chunk size have to be 0. */ + write_message ("error: dumped_heap_alloc computed invalid chunk size\n"); + _exit (1); + } + if (next_heap_chunk == NULL) + /* Initialize the top of the heap. Add one word of zero padding, + to match existing practice. */ + { + dumped_heap[0] = 0; + next_heap_chunk = dumped_heap + 1; + } + else + /* The previous chunk is allocated. */ + chunk_size |= 1; + *next_heap_chunk = chunk_size; + + /* User data starts after the chunk header. */ + void *result = next_heap_chunk + 1; + next_heap_chunk += chunk_size / sizeof (size_t); + + /* Mark the previous chunk as used. */ + *next_heap_chunk = 1; + return result; +} + +/* Global seed variable for the random number generator. */ +static unsigned long long global_seed; + +/* Simple random number generator. The numbers are in the range from + 0 to UINT_MAX (inclusive). */ +static unsigned int +rand_next (unsigned long long *seed) +{ + /* Linear congruential generated as used for MMIX. */ + *seed = *seed * 6364136223846793005ULL + 1442695040888963407ULL; + return *seed >> 32; +} + +/* Fill LENGTH bytes at BUFFER with random contents, as determined by + SEED. */ +static void +randomize_buffer (unsigned char *buffer, size_t length, + unsigned long long seed) +{ + for (size_t i = 0; i < length; ++i) + buffer[i] = rand_next (&seed); +} + +/* Dumps the buffer to standard output, in hexadecimal. */ +static void +dump_hex (unsigned char *buffer, size_t length) +{ + for (int i = 0; i < length; ++i) + printf (" %02X", buffer[i]); +} + +/* Set to true if an error is encountered. */ +static bool errors = false; + +/* Keep track of object allocations. */ +struct allocation +{ + unsigned char *data; + unsigned int size; + unsigned int seed; +}; + +/* Check that the allocation task allocation has the expected + contents. */ +static void +check_allocation (const struct allocation *alloc, int index) +{ + size_t size = alloc->size; + if (alloc->data == NULL) + { + printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n", + size, index, alloc->seed); + errors = true; + return; + } + + unsigned char expected[4096]; + if (size > sizeof (expected)) + { + printf ("error: invalid allocation size %zu at %d, seed %u\n", + size, index, alloc->seed); + errors = true; + return; + } + randomize_buffer (expected, size, alloc->seed); + if (memcmp (alloc->data, expected, size) != 0) + { + printf ("error: allocation %d data mismatch, size %zu, seed %u\n", + index, size, alloc->seed); + printf (" expected:"); + dump_hex (expected, size); + putc ('\n', stdout); + printf (" actual:"); + dump_hex (alloc->data, size); + putc ('\n', stdout); + errors = true; + } +} + +/* A heap allocation combined with pending actions on it. */ +struct allocation_task +{ + struct allocation allocation; + enum allocation_action action; +}; + +/* Allocation tasks. Initialized by init_allocation_tasks and used by + perform_allocations. */ +enum { allocation_task_count = action_count * max_size }; +static struct allocation_task allocation_tasks[allocation_task_count]; + +/* Fisher-Yates shuffle of allocation_tasks. */ +static void +shuffle_allocation_tasks (void) +{ + for (int i = 0; i < allocation_task_count - 1; ++i) + { + /* Pick pair in the tail of the array. */ + int j = i + (rand_next (&global_seed) + % ((unsigned) (allocation_task_count - i))); + if (j < 0 || j >= allocation_task_count) + { + write_message ("error: test bug in shuffle\n"); + _exit (1); + } + /* Exchange. */ + struct allocation_task tmp = allocation_tasks[i]; + allocation_tasks[i] = allocation_tasks[j]; + allocation_tasks[j] = tmp; + } +} + +/* Set up the allocation tasks and the dumped heap. */ +static void +initial_allocations (void) +{ + /* Initialize in a position-dependent way. */ + for (int i = 0; i < allocation_task_count; ++i) + allocation_tasks[i] = (struct allocation_task) + { + .allocation = + { + .size = 1 + (i / action_count), + .seed = i, + }, + .action = i % action_count + }; + + /* Execute the tasks in a random order. */ + shuffle_allocation_tasks (); + + /* Initialize the contents of the dumped heap. */ + for (int i = 0; i < allocation_task_count; ++i) + { + struct allocation_task *task = allocation_tasks + i; + task->allocation.data = dumped_heap_alloc (task->allocation.size); + randomize_buffer (task->allocation.data, task->allocation.size, + task->allocation.seed); + } + + for (int i = 0; i < allocation_task_count; ++i) + check_allocation (&allocation_tasks[i].allocation, i); +} + +/* Indicates whether init_heap has run. This variable needs to be + volatile because malloc is declared __THROW, which implies it is a + leaf function, but we expect it to run our hooks. */ +static volatile bool heap_initialized; + +/* Executed by glibc malloc, through __malloc_initialize_hook + below. */ +static void +init_heap (void) +{ + write_message ("info: performing heap initialization\n"); + heap_initialized = true; + + /* Populate the dumped heap. */ + initial_allocations (); + + /* Complete initialization of the saved heap data structure. */ + save_state.sbrk_base = (void *) dumped_heap; + save_state.sbrked_mem_bytes = sizeof (dumped_heap); + /* Top pointer. Adjust so that it points to the start of struct + malloc_chunk. */ + save_state.av[2] = (void *) (next_heap_chunk - 1); + + /* Integrate the dumped heap into the process heap. */ + if (malloc_set_state (&save_state) != 0) + { + write_message ("error: malloc_set_state failed\n"); + _exit (1); + } +} + +/* Interpose the initialization callback. */ +void (*volatile __malloc_initialize_hook) (void) = init_heap; + +/* Simulate occasional unrelated heap activity in the non-dumped + heap. */ +enum { heap_activity_allocations_count = 32 }; +static struct allocation heap_activity_allocations + [heap_activity_allocations_count] = {}; +static int heap_activity_seed_counter = 1000 * 1000; + +static void +heap_activity (void) +{ + /* Only do this from time to time. */ + if ((rand_next (&global_seed) % 4) == 0) + { + int slot = rand_next (&global_seed) % heap_activity_allocations_count; + struct allocation *alloc = heap_activity_allocations + slot; + if (alloc->data == NULL) + { + alloc->size = rand_next (&global_seed) % (4096U + 1); + alloc->data = xmalloc (alloc->size); + alloc->seed = heap_activity_seed_counter++; + randomize_buffer (alloc->data, alloc->size, alloc->seed); + check_allocation (alloc, 1000 + slot); + } + else + { + check_allocation (alloc, 1000 + slot); + free (alloc->data); + alloc->data = NULL; + } + } +} + +static void +heap_activity_deallocate (void) +{ + for (int i = 0; i < heap_activity_allocations_count; ++i) + free (heap_activity_allocations[i].data); +} + +/* Perform a full heap check across the dumped heap allocation tasks, + and the simulated heap activity directly above. */ +static void +full_heap_check (void) +{ + /* Dumped heap. */ + for (int i = 0; i < allocation_task_count; ++i) + if (allocation_tasks[i].allocation.data != NULL) + check_allocation (&allocation_tasks[i].allocation, i); + + /* Heap activity allocations. */ + for (int i = 0; i < heap_activity_allocations_count; ++i) + if (heap_activity_allocations[i].data != NULL) + check_allocation (heap_activity_allocations + i, i); +} + +/* Used as an optimization barrier to force a heap allocation. */ +__attribute__ ((noinline, noclone)) +static void +my_free (void *ptr) +{ + free (ptr); +} + +static int +do_test (void) +{ + my_free (malloc (1)); + if (!heap_initialized) + { + printf ("error: heap was not initialized by malloc\n"); + return 1; + } + + /* The first pass performs the randomly generated allocation + tasks. */ + write_message ("info: first pass through allocation tasks\n"); + full_heap_check (); + + /* Execute the post-undump tasks in a random order. */ + shuffle_allocation_tasks (); + + for (int i = 0; i < allocation_task_count; ++i) + { + heap_activity (); + struct allocation_task *task = allocation_tasks + i; + switch (task->action) + { + case action_free: + check_allocation (&task->allocation, i); + free (task->allocation.data); + task->allocation.data = NULL; + break; + + case action_realloc: + check_allocation (&task->allocation, i); + task->allocation.data = xrealloc + (task->allocation.data, task->allocation.size + max_size); + check_allocation (&task->allocation, i); + break; + + case action_realloc_same: + check_allocation (&task->allocation, i); + task->allocation.data = xrealloc + (task->allocation.data, task->allocation.size); + check_allocation (&task->allocation, i); + break; + + case action_realloc_smaller: + check_allocation (&task->allocation, i); + size_t new_size = task->allocation.size - 1; + task->allocation.data = xrealloc (task->allocation.data, new_size); + if (new_size == 0) + { + if (task->allocation.data != NULL) + { + printf ("error: realloc with size zero did not deallocate\n"); + errors = true; + } + /* No further action on this task. */ + task->action = action_free; + } + else + { + task->allocation.size = new_size; + check_allocation (&task->allocation, i); + } + break; + + case action_count: + abort (); + } + full_heap_check (); + } + + /* The second pass frees the objects which were allocated during the + first pass. */ + write_message ("info: second pass through allocation tasks\n"); + + shuffle_allocation_tasks (); + for (int i = 0; i < allocation_task_count; ++i) + { + heap_activity (); + struct allocation_task *task = allocation_tasks + i; + switch (task->action) + { + case action_free: + /* Already freed, nothing to do. */ + break; + + case action_realloc: + case action_realloc_same: + case action_realloc_smaller: + check_allocation (&task->allocation, i); + free (task->allocation.data); + task->allocation.data = NULL; + break; + + case action_count: + abort (); + } + full_heap_check (); + } + + heap_activity_deallocate (); + + /* Check that the malloc_get_state stub behaves in the intended + way. */ + errno = 0; + if (malloc_get_state () != NULL) + { + printf ("error: malloc_get_state succeeded\n"); + errors = true; + } + if (errno != ENOSYS) + { + printf ("error: malloc_get_state: %m\n"); + errors = true; + } + + return errors; +} diff --git a/REORG.TODO/malloc/tst-mallopt.c b/REORG.TODO/malloc/tst-mallopt.c new file mode 100644 index 0000000000..37f6c4cc04 --- /dev/null +++ b/REORG.TODO/malloc/tst-mallopt.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2014-2017 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/>. */ + +#include <malloc.h> +#include <stdio.h> +#include <string.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + int ret; + + ret = mallopt(M_CHECK_ACTION, 1); + + if (ret != 1) + merror ("mallopt (M_CHECK_ACTION, 1) failed."); + + ret = mallopt(M_MMAP_MAX, 64*1024); + + if (ret != 1) + merror ("mallopt (M_MMAP_MAX, 64*1024) failed."); + + ret = mallopt(M_MMAP_THRESHOLD, 64*1024); + + if (ret != 1) + merror ("mallopt (M_MMAP_THRESHOLD, 64*1024) failed."); + + ret = mallopt(M_MXFAST, 0); + + if (ret != 1) + merror ("mallopt (M_MXFAST, 0) failed."); + + ret = mallopt(M_PERTURB, 0xa5); + + if (ret != 1) + merror ("mallopt (M_PERTURB, 0xa5) failed."); + + ret = mallopt(M_TOP_PAD, 64*1024); + + if (ret != 1) + merror ("mallopt (M_TOP_PAD, 64*1024) failed."); + + ret = mallopt(M_TRIM_THRESHOLD, -1); + + if (ret != 1) + merror ("mallopt (M_TRIM_THRESHOLD, -1) failed."); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-mcheck.c b/REORG.TODO/malloc/tst-mcheck.c new file mode 100644 index 0000000000..5a66bab331 --- /dev/null +++ b/REORG.TODO/malloc/tst-mcheck.c @@ -0,0 +1,115 @@ +/* Copyright (C) 2005-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2005. + + 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/>. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <libc-diag.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p, *q; + + errno = 0; + + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + p = malloc (-1); + DIAG_POP_NEEDS_COMMENT; + + if (p != NULL) + merror ("malloc (-1) succeeded."); + else if (errno != ENOMEM) + merror ("errno is not set correctly."); + + p = malloc (10); + if (p == NULL) + merror ("malloc (10) failed."); + + p = realloc (p, 0); + if (p != NULL) + merror ("realloc (p, 0) failed."); + + p = malloc (0); + if (p == NULL) + merror ("malloc (0) failed."); + + p = realloc (p, 0); + if (p != NULL) + merror ("realloc (p, 0) failed."); + + q = malloc (256); + if (q == NULL) + merror ("malloc (256) failed."); + + p = malloc (512); + if (p == NULL) + merror ("malloc (512) failed."); + + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + if (realloc (p, -256) != NULL) + merror ("realloc (p, -256) succeeded."); + else if (errno != ENOMEM) + merror ("errno is not set correctly."); + DIAG_POP_NEEDS_COMMENT; + + free (p); + + p = malloc (512); + if (p == NULL) + merror ("malloc (512) failed."); + + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + if (realloc (p, -1) != NULL) + merror ("realloc (p, -1) succeeded."); + else if (errno != ENOMEM) + merror ("errno is not set correctly."); + DIAG_POP_NEEDS_COMMENT; + + free (p); + free (q); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-memalign.c b/REORG.TODO/malloc/tst-memalign.c new file mode 100644 index 0000000000..b403464e27 --- /dev/null +++ b/REORG.TODO/malloc/tst-memalign.c @@ -0,0 +1,114 @@ +/* Test for memalign. + Copyright (C) 2013-2017 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/>. */ + +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p; + unsigned long pagesize = getpagesize (); + unsigned long ptrval; + int save; + + errno = 0; + + /* An attempt to allocate a huge value should return NULL and set + errno to ENOMEM. */ + p = memalign (sizeof (void *), -1); + + save = errno; + + if (p != NULL) + merror ("memalign (sizeof (void *), -1) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("memalign (sizeof (void *), -1) errno is not set correctly"); + + free (p); + + errno = 0; + + /* Test to expose integer overflow in malloc internals from BZ #15857. */ + p = memalign (pagesize, -pagesize); + + save = errno; + + if (p != NULL) + merror ("memalign (pagesize, -pagesize) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("memalign (pagesize, -pagesize) errno is not set correctly"); + + free (p); + + errno = 0; + + /* Test to expose integer overflow in malloc internals from BZ #16038. */ + p = memalign (-1, pagesize); + + save = errno; + + if (p != NULL) + merror ("memalign (-1, pagesize) succeeded."); + + if (p == NULL && save != EINVAL) + merror ("memalign (-1, pagesize) errno is not set correctly"); + + free (p); + + /* A zero-sized allocation should succeed with glibc, returning a + non-NULL value. */ + p = memalign (sizeof (void *), 0); + + if (p == NULL) + merror ("memalign (sizeof (void *), 0) failed."); + + free (p); + + /* Check the alignment of the returned pointer is correct. */ + p = memalign (0x100, 10); + + if (p == NULL) + merror ("memalign (0x100, 10) failed."); + + ptrval = (unsigned long) p; + + if ((ptrval & 0xff) != 0) + merror ("pointer is not aligned to 0x100"); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-mtrace.c b/REORG.TODO/malloc/tst-mtrace.c new file mode 100644 index 0000000000..2726c68b76 --- /dev/null +++ b/REORG.TODO/malloc/tst-mtrace.c @@ -0,0 +1,105 @@ +/* Test program for mtrace. + Copyright (C) 2000-2017 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/>. */ + +#include <mcheck.h> +#include <paths.h> +#include <search.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +static void print (const void *node, VISIT value, int level); + +/* Used for several purposes. */ +static FILE *fp; + + +static int +do_test (void) +{ + void *root = NULL; + size_t linelen = 0; + char *line = NULL; + + /* Enable memory usage tracing. */ + mtrace (); + + /* Perform some operations which definitely will allocate some + memory. */ + fp = fopen (__FILE__, "r"); + if (fp == NULL) + /* Shouldn't happen since this program is executed in the source + directory. */ + abort (); + + while (!feof (fp)) + { + char **p; + char *copy; + ssize_t n = getline (&line, &linelen, fp); + + if (n < 0) + break; + + if (n == 0) + continue; + + copy = strdup (line); + if (copy == NULL) + abort (); + + p = (char **) tsearch (copy, &root, + (int (*)(const void *, const void *))strcmp); + if (*p != copy) + /* This line wasn't added. */ + free (copy); + } + + fclose (fp); + + fp = fopen (_PATH_DEVNULL, "w"); + if (fp != NULL) + { + /* Write something through stdout. */ + twalk (root, print); + + fclose (fp); + } + + /* Free everything. */ + tdestroy (root, free); + + /* Also the line buffer. */ + free (line); + + /* That's it. */ + return 0; +} + + +static void +print (const void *node, VISIT value, int level) +{ + static int cnt; + if (value == postorder || value == leaf) + fprintf (fp, "%3d: %s", ++cnt, *(const char **) node); +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-mtrace.sh b/REORG.TODO/malloc/tst-mtrace.sh new file mode 100755 index 0000000000..6ab799f9d0 --- /dev/null +++ b/REORG.TODO/malloc/tst-mtrace.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Testing the mtrace function. +# Copyright (C) 2000-2017 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/>. + +set -e + +common_objpfx=$1; shift +test_program_prefix_before_env=$1; shift +run_program_env=$1; shift +test_program_prefix_after_env=$1; shift + +status=0 +trap "rm -f ${common_objpfx}malloc/tst-mtrace.leak; exit 1" 1 2 15 + +${test_program_prefix_before_env} \ +${run_program_env} \ +MALLOC_TRACE=${common_objpfx}malloc/tst-mtrace.leak \ +${test_program_prefix_after_env} \ + ${common_objpfx}malloc/tst-mtrace || status=1 + +if test $status -eq 0 && test -f ${common_objpfx}malloc/mtrace; then + ${common_objpfx}malloc/mtrace ${common_objpfx}malloc/tst-mtrace.leak \ + > ${common_objpfx}malloc/tst-mtrace.out|| status=1 +fi + +rm -f ${common_objpfx}malloc/tst-mtrace.leak + +exit $status diff --git a/REORG.TODO/malloc/tst-obstack.c b/REORG.TODO/malloc/tst-obstack.c new file mode 100644 index 0000000000..ee1385d0f7 --- /dev/null +++ b/REORG.TODO/malloc/tst-obstack.c @@ -0,0 +1,67 @@ +/* Test case by Alexandre Duret-Lutz <duret_g@epita.fr>. */ +#include <obstack.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#define obstack_chunk_alloc verbose_malloc +#define obstack_chunk_free verbose_free +#define ALIGN_BOUNDARY 64 +#define ALIGN_MASK (ALIGN_BOUNDARY - 1) +#define OBJECT_SIZE 1000 + +static void * +verbose_malloc (size_t size) +{ + void *buf = malloc (size); + printf ("malloc (%zu) => %p\n", size, buf); + return buf; +} + +static void +verbose_free (void *buf) +{ + free (buf); + printf ("free (%p)\n", buf); +} + +static int +do_test (void) +{ + int result = 0; + int align = 2; + + while (align <= 64) + { + struct obstack obs; + int i; + int align_mask = align - 1; + + printf ("\n Alignment mask: %d\n", align_mask); + + obstack_init (&obs); + obstack_alignment_mask (&obs) = align_mask; + /* finish an empty object to take alignment into account */ + obstack_finish (&obs); + + /* let's allocate some objects and print their addresses */ + for (i = 15; i > 0; --i) + { + void *obj = obstack_alloc (&obs, OBJECT_SIZE); + + printf ("obstack_alloc (%u) => %p \t%s\n", OBJECT_SIZE, obj, + ((uintptr_t) obj & align_mask) ? "(not aligned)" : ""); + result |= ((uintptr_t) obj & align_mask) != 0; + } + + /* clean up */ + obstack_free (&obs, 0); + + align <<= 1; + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-posix_memalign.c b/REORG.TODO/malloc/tst-posix_memalign.c new file mode 100644 index 0000000000..31573b699e --- /dev/null +++ b/REORG.TODO/malloc/tst-posix_memalign.c @@ -0,0 +1,118 @@ +/* Test for posix_memalign. + Copyright (C) 2013-2017 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/>. */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p; + int ret; + unsigned long pagesize = getpagesize (); + unsigned long ptrval; + + p = NULL; + + /* An attempt to allocate a huge value should return ENOMEM and + p should remain NULL. */ + ret = posix_memalign (&p, sizeof (void *), -1); + + if (ret != ENOMEM) + merror ("posix_memalign (&p, sizeof (void *), -1) succeeded."); + + if (ret == ENOMEM && p != NULL) + merror ("returned an error but pointer was modified"); + + free (p); + + p = NULL; + + /* Test to expose integer overflow in malloc internals from BZ #15857. */ + ret = posix_memalign (&p, pagesize, -pagesize); + + if (ret != ENOMEM) + merror ("posix_memalign (&p, pagesize, -pagesize) succeeded."); + + free (p); + + p = NULL; + + /* Test to expose integer overflow in malloc internals from BZ #16038. */ + ret = posix_memalign (&p, -1, pagesize); + + if (ret != EINVAL) + merror ("posix_memalign (&p, -1, pagesize) succeeded."); + + free (p); + + p = NULL; + + /* A zero-sized allocation should succeed with glibc, returning zero + and setting p to a non-NULL value. */ + ret = posix_memalign (&p, sizeof (void *), 0); + + if (ret != 0 || p == NULL) + merror ("posix_memalign (&p, sizeof (void *), 0) failed."); + + free (p); + + ret = posix_memalign (&p, 0x300, 10); + + if (ret != EINVAL) + merror ("posix_memalign (&p, 0x300, 10) succeeded."); + + ret = posix_memalign (&p, 0, 10); + + if (ret != EINVAL) + merror ("posix_memalign (&p, 0, 10) succeeded."); + + p = NULL; + + ret = posix_memalign (&p, 0x100, 10); + + if (ret != 0) + merror ("posix_memalign (&p, 0x100, 10) failed."); + + if (ret == 0 && p == NULL) + merror ("returned success but pointer is NULL"); + + ptrval = (unsigned long) p; + + if (ret == 0 && (ptrval & 0xff) != 0) + merror ("pointer is not aligned to 0x100"); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-pvalloc.c b/REORG.TODO/malloc/tst-pvalloc.c new file mode 100644 index 0000000000..00f1952c36 --- /dev/null +++ b/REORG.TODO/malloc/tst-pvalloc.c @@ -0,0 +1,99 @@ +/* Test for pvalloc. + Copyright (C) 2013-2017 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/>. */ + +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p; + unsigned long pagesize = getpagesize (); + unsigned long ptrval; + int save; + + errno = 0; + + /* An attempt to allocate a huge value should return NULL and set + errno to ENOMEM. */ + p = pvalloc (-1); + + save = errno; + + if (p != NULL) + merror ("pvalloc (-1) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("pvalloc (-1) errno is not set correctly"); + + free (p); + + errno = 0; + + /* Test to expose integer overflow in malloc internals from BZ #15855. */ + p = pvalloc (-pagesize); + + save = errno; + + if (p != NULL) + merror ("pvalloc (-pagesize) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("pvalloc (-pagesize) errno is not set correctly"); + + free (p); + + /* A zero-sized allocation should succeed with glibc, returning a + non-NULL value. */ + p = pvalloc (0); + + if (p == NULL) + merror ("pvalloc (0) failed."); + + free (p); + + /* Check the alignment of the returned pointer is correct. */ + p = pvalloc (32); + + if (p == NULL) + merror ("pvalloc (32) failed."); + + ptrval = (unsigned long) p; + + if ((ptrval & (pagesize - 1)) != 0) + merror ("returned pointer is not page aligned."); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-realloc.c b/REORG.TODO/malloc/tst-realloc.c new file mode 100644 index 0000000000..31a58bd026 --- /dev/null +++ b/REORG.TODO/malloc/tst-realloc.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2013-2017 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/>. */ + +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <libc-diag.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p; + unsigned char *c; + int save, i, ok; + + errno = 0; + + /* realloc (NULL, ...) behaves similarly to malloc (C89). */ + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + p = realloc (NULL, -1); + DIAG_POP_NEEDS_COMMENT; + save = errno; + + if (p != NULL) + merror ("realloc (NULL, -1) succeeded."); + + /* errno should be set to ENOMEM on failure (POSIX). */ + if (p == NULL && save != ENOMEM) + merror ("errno is not set correctly"); + + errno = 0; + + /* realloc (NULL, ...) behaves similarly to malloc (C89). */ + p = realloc (NULL, 10); + save = errno; + + if (p == NULL) + merror ("realloc (NULL, 10) failed."); + + /* errno should be clear on success (POSIX). */ + if (p != NULL && save != 0) + merror ("errno is set but should not be"); + + free (p); + + p = calloc (20, 1); + if (p == NULL) + merror ("calloc (20, 1) failed."); + + /* Check increasing size preserves contents (C89). */ + p = realloc (p, 200); + if (p == NULL) + merror ("realloc (p, 200) failed."); + + c = p; + ok = 1; + + for (i = 0; i < 20; i++) + { + if (c[i] != 0) + ok = 0; + } + + if (ok == 0) + merror ("first 20 bytes were not cleared"); + + free (p); + + p = realloc (NULL, 100); + if (p == NULL) + merror ("realloc (NULL, 100) failed."); + + memset (p, 0xff, 100); + + /* Check decreasing size preserves contents (C89). */ + p = realloc (p, 16); + if (p == NULL) + merror ("realloc (p, 16) failed."); + + c = p; + ok = 1; + + for (i = 0; i < 16; i++) + { + if (c[i] != 0xff) + ok = 0; + } + + if (ok == 0) + merror ("first 16 bytes were not correct"); + + /* Check failed realloc leaves original untouched (C89). */ + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif + c = realloc (p, -1); + DIAG_POP_NEEDS_COMMENT; + if (c != NULL) + merror ("realloc (p, -1) succeeded."); + + c = p; + ok = 1; + + for (i = 0; i < 16; i++) + { + if (c[i] != 0xff) + ok = 0; + } + + if (ok == 0) + merror ("first 16 bytes were not correct after failed realloc"); + + /* realloc (p, 0) frees p (C89) and returns NULL (glibc). */ + p = realloc (p, 0); + if (p != NULL) + merror ("realloc (p, 0) returned non-NULL."); + + /* realloc (NULL, 0) acts like malloc (0) (glibc). */ + p = realloc (NULL, 0); + if (p == NULL) + merror ("realloc (NULL, 0) returned NULL."); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-reallocarray.c b/REORG.TODO/malloc/tst-reallocarray.c new file mode 100644 index 0000000000..f1cbf7fe0a --- /dev/null +++ b/REORG.TODO/malloc/tst-reallocarray.c @@ -0,0 +1,118 @@ +/* Test for reallocarray. + Copyright (C) 2017 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/>. */ + +#include <errno.h> +#include <malloc.h> +#include <string.h> +#include <support/check.h> + +static int +do_test (void) +{ + void *ptr = NULL; + void *ptr2 = NULL; + unsigned char *c; + size_t i; + int ok; + const size_t max = ~(size_t)0; + size_t a, b; + + /* Test overflow detection. */ + errno = 0; + ptr = reallocarray (NULL, max, 2); + TEST_VERIFY (!ptr); + TEST_VERIFY (errno == ENOMEM); + + errno = 0; + ptr = reallocarray (NULL, 2, max); + TEST_VERIFY (!ptr); + TEST_VERIFY (errno == ENOMEM); + + a = 65537; + b = max/65537 + 1; + errno = 0; + ptr = reallocarray (NULL, a, b); + TEST_VERIFY (!ptr); + TEST_VERIFY (errno == ENOMEM); + + errno = 0; + ptr = reallocarray (NULL, b, a); + TEST_VERIFY (!ptr); + TEST_VERIFY (errno == ENOMEM); + + /* Test realloc-like behavior. */ + /* Allocate memory like malloc. */ + ptr = reallocarray (NULL, 10, 2); + TEST_VERIFY_EXIT (ptr); + TEST_VERIFY_EXIT (malloc_usable_size (ptr) >= 10*2); + + memset (ptr, 0xAF, 10*2); + + /* Enlarge buffer. */ + ptr2 = reallocarray (ptr, 20, 2); + TEST_VERIFY (ptr2); + if (ptr2) + ptr = ptr2; + TEST_VERIFY (malloc_usable_size (ptr) >= 20*2); + + c = ptr; + ok = 1; + for (i = 0; i < 10*2; ++i) + { + if (c[i] != 0xAF) + ok = 0; + } + TEST_VERIFY (ok); + + /* Decrease buffer size. */ + ptr2 = reallocarray (ptr, 5, 3); + TEST_VERIFY (ptr2); + if (ptr2) + ptr = ptr2; + TEST_VERIFY_EXIT (malloc_usable_size (ptr) >= 5*3); + + c = ptr; + ok = 1; + for (i = 0; i < 5*3; ++i) + { + if (c[i] != 0xAF) + ok = 0; + } + TEST_VERIFY (ok); + + /* Overflow should leave buffer untouched. */ + errno = 0; + ptr2 = reallocarray (ptr, 2, ~(size_t)0); + TEST_VERIFY (!ptr2); + TEST_VERIFY (errno == ENOMEM); + + c = ptr; + ok = 1; + for (i = 0; i < 5*3; ++i) + { + if (c[i] != 0xAF) + ok = 0; + } + TEST_VERIFY (ok); + + free (ptr); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/malloc/tst-scratch_buffer.c b/REORG.TODO/malloc/tst-scratch_buffer.c new file mode 100644 index 0000000000..5c9f3442ae --- /dev/null +++ b/REORG.TODO/malloc/tst-scratch_buffer.c @@ -0,0 +1,155 @@ +/* + Copyright (C) 2015-2017 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/>. */ + +#include <scratch_buffer.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +static bool +unchanged_array_size (struct scratch_buffer *buf, size_t a, size_t b) +{ + size_t old_length = buf->length; + if (!scratch_buffer_set_array_size (buf, a, b)) + { + printf ("scratch_buffer_set_array_size failed: %zu %zu\n", + a, b); + return false; + } + if (old_length != buf->length) + { + printf ("scratch_buffer_set_array_size did not preserve size: %zu %zu\n", + a, b); + return false; + } + return true; +} + +static bool +array_size_must_fail (size_t a, size_t b) +{ + for (int pass = 0; pass < 2; ++pass) + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + if (pass > 0) + if (!scratch_buffer_grow (&buf)) + { + printf ("scratch_buffer_grow in array_size_must_fail failed\n"); + return false; + } + if (scratch_buffer_set_array_size (&buf, a, b)) + { + printf ("scratch_buffer_set_array_size passed: %d %zu %zu\n", + pass, a, b); + return false; + } + if (buf.data != buf.__space) + { + printf ("scratch_buffer_set_array_size did not free: %d %zu %zu\n", + pass, a, b); + return false; + } + } + return true; +} + +static int +do_test (void) +{ + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, ' ', buf.length); + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, ' ', buf.length); + size_t old_length = buf.length; + scratch_buffer_grow (&buf); + if (buf.length <= old_length) + { + printf ("scratch_buffer_grow did not enlarge buffer\n"); + return 1; + } + memset (buf.data, ' ', buf.length); + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, '@', buf.length); + strcpy (buf.data, "prefix"); + size_t old_length = buf.length; + scratch_buffer_grow_preserve (&buf); + if (buf.length <= old_length) + { + printf ("scratch_buffer_grow_preserve did not enlarge buffer\n"); + return 1; + } + if (strcmp (buf.data, "prefix") != 0) + { + printf ("scratch_buffer_grow_preserve did not copy buffer\n"); + return 1; + } + for (unsigned i = 7; i < old_length; ++i) + if (((char *)buf.data)[i] != '@') + { + printf ("scratch_buffer_grow_preserve did not copy buffer (%u)\n", + i); + return 1; + } + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + for (int pass = 0; pass < 4; ++pass) + { + if (!(unchanged_array_size (&buf, 0, 0) + && unchanged_array_size (&buf, 1, 0) + && unchanged_array_size (&buf, 0, 1) + && unchanged_array_size (&buf, -1, 0) + && unchanged_array_size (&buf, 0, -1) + && unchanged_array_size (&buf, 1ULL << 16, 0) + && unchanged_array_size (&buf, 0, 1ULL << 16) + && unchanged_array_size (&buf, (size_t) (1ULL << 32), 0) + && unchanged_array_size (&buf, 0, (size_t) (1ULL << 32)))) + return 1; + if (!scratch_buffer_grow (&buf)) + { + printf ("scratch_buffer_grow_failed (pass %d)\n", pass); + } + } + scratch_buffer_free (&buf); + } + { + if (!(array_size_must_fail (-1, 1) + && array_size_must_fail (-1, -1) + && array_size_must_fail (1, -1) + && array_size_must_fail (((size_t)-1) / 4, 4) + && array_size_must_fail (4, ((size_t)-1) / 4))) + return 1; + } + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-trim1.c b/REORG.TODO/malloc/tst-trim1.c new file mode 100644 index 0000000000..310707e0e1 --- /dev/null +++ b/REORG.TODO/malloc/tst-trim1.c @@ -0,0 +1,56 @@ +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define N 10000 + +static void *arr[N]; + +static int +do_test (void) +{ + for (int i = 0; i < N; ++i) + { + size_t size = random () % 16384; + + if ((arr[i] = malloc (size)) == NULL) + { + nomem: + puts ("not enough memory"); + return 0; + } + + memset (arr[i], size, size); + } + + void *p = malloc (256); + if (p == NULL) + goto nomem; + memset (p, 1, 256); + + puts ("=================================================================="); + + for (int i = 0; i < N; ++i) + if (i % 13 != 0) + free (arr[i]); + + puts ("=================================================================="); + + malloc_trim (0); + + puts ("=================================================================="); + + p = malloc (30000); + if (p == NULL) + goto nomem; + + memset (p, 2, 30000); + + malloc_trim (0); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/malloc/tst-valloc.c b/REORG.TODO/malloc/tst-valloc.c new file mode 100644 index 0000000000..57161f6b7d --- /dev/null +++ b/REORG.TODO/malloc/tst-valloc.c @@ -0,0 +1,99 @@ +/* Test for valloc. + Copyright (C) 2013-2017 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/>. */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +static int errors = 0; + +static void +merror (const char *msg) +{ + ++errors; + printf ("Error: %s\n", msg); +} + +static int +do_test (void) +{ + void *p; + unsigned long pagesize = getpagesize (); + unsigned long ptrval; + int save; + + errno = 0; + + /* An attempt to allocate a huge value should return NULL and set + errno to ENOMEM. */ + p = valloc (-1); + + save = errno; + + if (p != NULL) + merror ("valloc (-1) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("valloc (-1) errno is not set correctly"); + + free (p); + + errno = 0; + + /* Test to expose integer overflow in malloc internals from BZ #15856. */ + p = valloc (-pagesize); + + save = errno; + + if (p != NULL) + merror ("valloc (-pagesize) succeeded."); + + if (p == NULL && save != ENOMEM) + merror ("valloc (-pagesize) errno is not set correctly"); + + free (p); + + /* A zero-sized allocation should succeed with glibc, returning a + non-NULL value. */ + p = valloc (0); + + if (p == NULL) + merror ("valloc (0) failed."); + + free (p); + + /* Check the alignment of the returned pointer is correct. */ + p = valloc (32); + + if (p == NULL) + merror ("valloc (32) failed."); + + ptrval = (unsigned long) p; + + if ((ptrval & (pagesize - 1)) != 0) + merror ("returned pointer is not page aligned."); + + free (p); + + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |