From 4d952dfe7756644a4e7f92081906fd7d4194209c Mon Sep 17 00:00:00 2001 From: Miles Bader Date: Thu, 21 Nov 2002 06:06:22 +0000 Subject: Improve malloc debugging support. --- extra/Configs/Config.in | 11 +++++ libc/stdlib/malloc/Makefile | 14 ++++++- libc/stdlib/malloc/free.c | 25 ++++++++---- libc/stdlib/malloc/heap_debug.c | 25 +++++++----- libc/stdlib/malloc/malloc.c | 31 ++++++++------ libc/stdlib/malloc/malloc.h | 45 ++++++++++++++++----- libc/stdlib/malloc/malloc_debug.c | 85 +++++++++++++++++++++++++++++++++++++++ libc/stdlib/malloc/realloc.c | 7 ++-- 8 files changed, 197 insertions(+), 46 deletions(-) create mode 100644 libc/stdlib/malloc/malloc_debug.c diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in index 3b5fcf1f5..089c1b40b 100644 --- a/extra/Configs/Config.in +++ b/extra/Configs/Config.in @@ -367,4 +367,15 @@ config SUPPORT_LD_DEBUG_EARLY to debug the uClibc shared library loader early initialization, answer Y. Mere mortals answer N. +config UCLIBC_MALLOC_DEBUGGING + bool "Build malloc with debugging support" + depends MALLOC + default n + help + Answer Y here to compile extra debugging support code into malloc. + Malloc debugging output may then be enabled at runtime using + the MALLOC_DEBUG environment variable. Because this increases + the size of malloc appreciably (due to strings etc), you + should say N unless you need to debug a malloc problem. + endmenu diff --git a/libc/stdlib/malloc/Makefile b/libc/stdlib/malloc/Makefile index 97527621b..d722d1dc3 100644 --- a/libc/stdlib/malloc/Makefile +++ b/libc/stdlib/malloc/Makefile @@ -27,6 +27,16 @@ include $(TOPDIR)Rules.mak # calloc.c can be found at uClibc/libc/stdlib/calloc.c CSRC = malloc.c free.c realloc.c memalign.c \ heap_alloc.c heap_alloc_at.c heap_free.c + +# Turn on malloc debugging if requested +ifeq ($(UCLIBC_MALLOC_DEBUGGING),y) +CSRC += malloc_debug.c heap_debug.c +CFLAGS += -DMALLOC_DEBUGGING -DHEAP_DEBUGGING +ifeq ($(UCLIBC_UCLINUX_BROKEN_MUNMAP),y) +CFLAGS += -DMALLOC_MMB_DEBUGGING +endif +endif + COBJS=$(patsubst %.c,%.o, $(CSRC)) OBJS=$(COBJS) @@ -37,10 +47,10 @@ $(LIBC): ar-target ar-target: $(OBJS) $(AR) $(ARFLAGS) $(LIBC) $(OBJS) -$(COBJS): %.o : %.c +# Depend on uClinux_config.h to cache changes in __UCLIBC_MALLOC_DEBUGGING__ +$(COBJS): %.o : %.c ../../../include/bits/uClibc_config.h $(CC) $(CFLAGS) -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o clean: rm -f *.[oa] *~ core - diff --git a/libc/stdlib/malloc/free.c b/libc/stdlib/malloc/free.c index a00f996d9..3b2ec651e 100644 --- a/libc/stdlib/malloc/free.c +++ b/libc/stdlib/malloc/free.c @@ -31,7 +31,7 @@ free_to_heap (void *mem, struct heap *heap) /* Normal free. */ - MALLOC_DEBUG ("free: 0x%lx (base = 0x%lx, total_size = %d)\n", + MALLOC_DEBUG (1, "free: 0x%lx (base = 0x%lx, total_size = %d)", (long)mem, (long)MALLOC_BASE (mem), MALLOC_SIZE (mem)); size = MALLOC_SIZE (mem); @@ -76,7 +76,7 @@ free_to_heap (void *mem, struct heap *heap) reasonably cheap. */ if ((void *)end != sbrk (0)) { - MALLOC_DEBUG (" not unmapping: 0x%lx - 0x%lx (%ld bytes)\n", + MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)", start, end, end - start); __malloc_unlock_sbrk (); __heap_unlock (heap); @@ -84,7 +84,7 @@ free_to_heap (void *mem, struct heap *heap) } #endif - MALLOC_DEBUG (" unmapping: 0x%lx - 0x%lx (%ld bytes)\n", + MALLOC_DEBUG (0, "unmapping: 0x%lx - 0x%lx (%ld bytes)", start, end, end - start); /* Remove FA from the heap. */ @@ -119,7 +119,8 @@ free_to_heap (void *mem, struct heap *heap) exactly as we got them from mmap, so scan through our list of mmapped blocks, and return them in order. */ - MALLOC_MMB_DEBUG (" walking mmb list for region 0x%x[%d]...\n", start, end - start); + MALLOC_MMB_DEBUG (1, "walking mmb list for region 0x%x[%d]...", + start, end - start); prev_mmb = 0; mmb = __malloc_mmapped_blocks; @@ -127,7 +128,7 @@ free_to_heap (void *mem, struct heap *heap) && ((mmb_end = (mmb_start = (unsigned long)mmb->mem) + mmb->size) <= end)) { - MALLOC_MMB_DEBUG (" considering mmb at 0x%x: 0x%x[%d]\n", + MALLOC_MMB_DEBUG (1, "considering mmb at 0x%x: 0x%x[%d]", (unsigned)mmb, mmb_start, mmb_end - mmb_start); if (mmb_start >= start @@ -143,18 +144,20 @@ free_to_heap (void *mem, struct heap *heap) this block, so give up. */ break; - MALLOC_MMB_DEBUG (" unmapping mmb at 0x%x: 0x%x[%d]\n", + MALLOC_MMB_DEBUG (1, "unmapping mmb at 0x%x: 0x%x[%d]", (unsigned)mmb, mmb_start, mmb_end - mmb_start); if (mmb_start != start) /* We're going to unmap a part of the heap that begins after start, so put the intervening region back into the heap. */ { - MALLOC_MMB_DEBUG (" putting intervening region back into heap: 0x%x[%d]\n", + MALLOC_MMB_DEBUG (0, "putting intervening region back into heap: 0x%x[%d]", start, mmb_start - start); __heap_free (heap, (void *)start, mmb_start - start); } + MALLOC_MMB_DEBUG_INDENT (-1); + /* Unlink MMB from the list. */ if (prev_mmb) prev_mmb->next = next_mmb; @@ -194,17 +197,21 @@ free_to_heap (void *mem, struct heap *heap) prev_mmb = mmb; mmb = mmb->next; } + + MALLOC_MMB_DEBUG_INDENT (-1); } if (start != end) /* Hmm, well there's something we couldn't unmap, so put it back into the heap. */ { - MALLOC_MMB_DEBUG (" putting tail region back into heap: 0x%x[%d]\n", + MALLOC_MMB_DEBUG (0, "putting tail region back into heap: 0x%x[%d]", start, end - start); __heap_free (heap, (void *)start, end - start); } + MALLOC_MMB_DEBUG_INDENT (-1); + # else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ /* MEM/LEN may not be page-aligned, so we have to page-align them, @@ -241,6 +248,8 @@ free_to_heap (void *mem, struct heap *heap) #endif /* MALLOC_USE_SBRK */ } + + MALLOC_DEBUG_INDENT (-1); } void diff --git a/libc/stdlib/malloc/heap_debug.c b/libc/stdlib/malloc/heap_debug.c index 85ed15609..c1a127317 100644 --- a/libc/stdlib/malloc/heap_debug.c +++ b/libc/stdlib/malloc/heap_debug.c @@ -16,6 +16,7 @@ #include #include +#include "malloc.h" #include "heap.h" @@ -29,14 +30,14 @@ __heap_dump_freelist (struct heap *heap) { struct heap_free_area *fa; for (fa = heap->free_areas; fa; fa = fa->next) - fprintf (stderr, - " 0x%lx: 0x%lx - 0x%lx (%d)\tP=0x%lx, N=0x%lx\n", - (long)fa, - (long)HEAP_FREE_AREA_START (fa), - (long)HEAP_FREE_AREA_END (fa), - fa->size, - (long)fa->prev, - (long)fa->next); + __malloc_debug_printf (0, + "0x%lx: 0x%lx - 0x%lx (%d)\tP=0x%lx, N=0x%lx", + (long)fa, + (long)HEAP_FREE_AREA_START (fa), + (long)HEAP_FREE_AREA_END (fa), + fa->size, + (long)fa->prev, + (long)fa->next); } /* Output a text representation of HEAP to stderr, labelling it with STR. */ @@ -51,8 +52,9 @@ __heap_dump (struct heap *heap, const char *str) recursed = 1; - fprintf (stderr, " %s: heap @0x%lx:\n", str, (long)heap); + __malloc_debug_printf (1, "%s: heap @0x%lx:", str, (long)heap); __heap_dump_freelist (heap); + __malloc_debug_indent (-1); recursed = 0; } @@ -76,7 +78,10 @@ __heap_check_failure (struct heap *heap, struct heap_free_area *fa, vfprintf (stderr, fmt, val); va_end (val); - fprintf (stderr, "\nheap dump:\n"); + putc ('\n', stderr); + + __malloc_debug_set_indent (0); + __malloc_debug_printf (1, "heap dump:"); __heap_dump_freelist (heap); exit (22); diff --git a/libc/stdlib/malloc/malloc.c b/libc/stdlib/malloc/malloc.c index b23d566b1..4466367ed 100644 --- a/libc/stdlib/malloc/malloc.c +++ b/libc/stdlib/malloc/malloc.c @@ -29,10 +29,6 @@ struct heap __malloc_heap = HEAP_INIT_WITH_FA (initial_fa); malloc_mutex_t __malloc_sbrk_lock; #endif /* MALLOC_USE_LOCKING && MALLOC_USE_SBRK */ -#ifdef MALLOC_DEBUGGING -int __malloc_debug = 0; -#endif - #ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ /* A list of all malloc_mmb structures describing blocsk that @@ -44,10 +40,6 @@ struct malloc_mmb *__malloc_mmapped_blocks = 0; annoying ways. */ HEAP_DECLARE_STATIC_FREE_AREA (initial_mmb_fa, 48); /* enough for 3 mmbs */ struct heap __malloc_mmb_heap = HEAP_INIT_WITH_FA (initial_mmb_fa); - -# ifdef MALLOC_MMB_DEBUGGING -int __malloc_mmb_debug = 0; -# endif #endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ @@ -56,7 +48,7 @@ malloc_from_heap (size_t size, struct heap *heap) { void *mem; - MALLOC_DEBUG ("malloc: %d bytes\n", size); + MALLOC_DEBUG (1, "malloc: %d bytes", size); /* Include extra space to record the size of the allocated block. */ size += MALLOC_HEADER_SIZE; @@ -121,7 +113,7 @@ malloc_from_heap (size_t size, struct heap *heap) struct malloc_mmb *mmb, *prev_mmb, *new_mmb; #endif - MALLOC_DEBUG (" adding memory: 0x%lx - 0x%lx (%d bytes)\n", + MALLOC_DEBUG (1, "adding system memroy to heap: 0x%lx - 0x%lx (%d bytes)", (long)block, (long)block + block_size, block_size); /* Get back the heap lock. */ @@ -130,6 +122,8 @@ malloc_from_heap (size_t size, struct heap *heap) /* Put BLOCK into the heap. */ __heap_free (heap, block, block_size); + MALLOC_DEBUG_INDENT (-1); + /* Try again to allocate. */ mem = __heap_alloc (heap, &size); @@ -155,7 +149,7 @@ malloc_from_heap (size_t size, struct heap *heap) else __malloc_mmapped_blocks = new_mmb; - MALLOC_MMB_DEBUG (" new mmb at 0x%x: 0x%x[%d]\n", + MALLOC_MMB_DEBUG (0, "new mmb at 0x%x: 0x%x[%d]", (unsigned)new_mmb, (unsigned)new_mmb->mem, block_size); #endif /* !MALLOC_USE_SBRK && __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ @@ -167,9 +161,11 @@ malloc_from_heap (size_t size, struct heap *heap) { mem = MALLOC_SETUP (mem, size); - MALLOC_DEBUG (" malloc: returning 0x%lx (base:0x%lx, total_size:%d)\n", + MALLOC_DEBUG (-1, "malloc: returning 0x%lx (base:0x%lx, total_size:%ld)", (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); } + else + MALLOC_DEBUG (-1, "malloc: returning 0"); return mem; } @@ -177,5 +173,16 @@ malloc_from_heap (size_t size, struct heap *heap) void * malloc (size_t size) { +#ifdef MALLOC_DEBUGGING + static int debugging_initialized = 0; + if (! debugging_initialized) + { + debugging_initialized = 1; + __malloc_debug_init (); + } + if (__malloc_check) + __heap_check (&__malloc_heap, "malloc"); +#endif + return malloc_from_heap (size, &__malloc_heap); } diff --git a/libc/stdlib/malloc/malloc.h b/libc/stdlib/malloc/malloc.h index db0fb9bd7..7fe8b6016 100644 --- a/libc/stdlib/malloc/malloc.h +++ b/libc/stdlib/malloc/malloc.h @@ -78,12 +78,18 @@ extern struct heap __malloc_mmb_heap; about mmap block allocation/freeing by the `uclinux broken munmap' code to stderr, when the variable __malloc_mmb_debug is set to true. */ #ifdef MALLOC_MMB_DEBUGGING -#include +# include extern int __malloc_mmb_debug; -#define MALLOC_MMB_DEBUG(fmt, args...) \ - (__malloc_mmb_debug ? fprintf (stderr, fmt , ##args) : 0) +# define MALLOC_MMB_DEBUG(indent, fmt, args...) \ + (__malloc_mmb_debug ? __malloc_debug_printf (indent, fmt , ##args) : 0) +# define MALLOC_MMB_DEBUG_INDENT(indent) \ + (__malloc_mmb_debug ? __malloc_debug_indent (indent) : 0) +# ifndef MALLOC_DEBUGGING +# define MALLOC_DEBUGGING +# endif #else /* !MALLOC_MMB_DEBUGGING */ -#define MALLOC_MMB_DEBUG(fmt, args...) (void)0 +# define MALLOC_MMB_DEBUG(fmt, args...) (void)0 +# define MALLOC_MMB_DEBUG_INDENT(indent) (void)0 #endif /* MALLOC_MMB_DEBUGGING */ #endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ @@ -158,13 +164,30 @@ extern malloc_mutex_t __malloc_sbrk_lock; /* Define MALLOC_DEBUGGING to cause malloc to emit debugging info to stderr when the variable __malloc_debug is set to true. */ #ifdef MALLOC_DEBUGGING -#include -extern int __malloc_debug; -#define MALLOC_DEBUG(fmt, args...) \ - (__malloc_debug ? fprintf (stderr, fmt , ##args) : 0) -#else -#define MALLOC_DEBUG(fmt, args...) (void)0 -#endif + +extern int __malloc_debug, __malloc_check; + +# define MALLOC_DEBUG(indent, fmt, args...) \ + (__malloc_debug ? __malloc_debug_printf (indent, fmt , ##args) : 0) +# define MALLOC_DEBUG_INDENT(indent) \ + (__malloc_debug ? __malloc_debug_indent (indent) : 0) + +extern int __malloc_debug_cur_indent; + +/* Print FMT and args indented at the current debug print level, followed + by a newline, and change the level by INDENT. */ +extern void __malloc_debug_printf (int indent, const char *fmt, ...); + +/* Change the current debug print level by INDENT. */ +#define __malloc_debug_indent(indent) (__malloc_debug_cur_indent += indent) + +/* Set the current debug print level to LEVEL. */ +#define __malloc_debug_set_indent(level) (__malloc_debug_cur_indent = level) + +#else /* !MALLOC_DEBUGGING */ +# define MALLOC_DEBUG(fmt, args...) (void)0 +# define MALLOC_DEBUG_INDENT(indent) (void)0 +#endif /* MALLOC_DEBUGGING */ /* Return SZ rounded down to POWER_OF_2_SIZE (which must be power of 2). */ diff --git a/libc/stdlib/malloc/malloc_debug.c b/libc/stdlib/malloc/malloc_debug.c new file mode 100644 index 000000000..20a37f2f0 --- /dev/null +++ b/libc/stdlib/malloc/malloc_debug.c @@ -0,0 +1,85 @@ +/* + * libc/stdlib/malloc/malloc_debug.c -- malloc debugging support + * + * Copyright (C) 2002 NEC Corporation + * Copyright (C) 2002 Miles Bader + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING.LIB in the main + * directory of this archive for more details. + * + * Written by Miles Bader + */ + +#include +#include +#include +#include + +#include "malloc.h" +#include "heap.h" + +#ifdef MALLOC_DEBUGGING +int __malloc_debug = 0, __malloc_check = 0; +#endif + +#ifdef MALLOC_MMB_DEBUGGING +int __malloc_mmb_debug = 0; +#endif + +/* Debugging output is indented this may levels. */ +int __malloc_debug_cur_indent = 0; + + +/* Print FMT and args indented at the current debug print level, followed + by a newline, and change the level by INDENT. */ +void +__malloc_debug_printf (int indent, const char *fmt, ...) +{ + int i; + va_list val; + + for (i = 0; i < __malloc_debug_cur_indent; i++) + fputs (" ", stderr); + + va_start (val, fmt); + vfprintf (stderr, fmt, val); + va_end (val); + + putc ('\n', stderr); + + __malloc_debug_indent (indent); +} + +void +__malloc_debug_init (void) +{ + char *ev = getenv ("MALLOC_DEBUG"); + if (ev) + { + int val = atoi (ev); + if (val & 1) + __malloc_check = 1; + +#ifdef MALLOC_DEBUGGING + if (val & 2) + __malloc_debug = 1; +#endif +#ifdef MALLOC_MMB_DEBUGGING + if (val & 4) + __malloc_mmb_debug = 1; +#endif + +#ifdef HEAP_DEBUGGING + if (val & 8) + __heap_debug = 1; +#endif + + if (val) + __malloc_debug_printf + (0, "malloc_debug: initialized to %d (check = %d, dump = %d, dump_mmb = %d, dump_heap = %d)", + val, + !!(val & 1), !!(val & 2), + !!(val & 4), !!(val & 8)); + } +} diff --git a/libc/stdlib/malloc/realloc.c b/libc/stdlib/malloc/realloc.c index 9bfe95198..d4e0d9cb4 100644 --- a/libc/stdlib/malloc/realloc.c +++ b/libc/stdlib/malloc/realloc.c @@ -43,7 +43,7 @@ realloc (void *mem, size_t new_size) allocation unit (SIZE is already guaranteed to be so).*/ new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE); - MALLOC_DEBUG ("realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)\n", + MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)", (long)mem, new_size, (long)base_mem, size); if (new_size > size) @@ -81,9 +81,10 @@ realloc (void *mem, size_t new_size) } if (mem) - MALLOC_DEBUG (" realloc: returning 0x%lx" - " (base:0x%lx, total_size:%d)\n", + MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)", (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); + else + MALLOC_DEBUG (-1, "realloc: returning 0"); return mem; } -- cgit v1.2.3