Mail Archives: djgpp-workers/2001/06/02/08:04:14
The changes below add some simple diagnostics and debugging facilities
to the memory-allocation routines. They also implement functions for
reporting the amount of heap that is free and in use, which is
something quite a few users asked about over the years.
The API is modeled after similar functionality on Unix and GNU/Linux
systems; adding Borland-compatible coreleft, heapwalk, heapcheck,
etc. is left as an exercise.
Comments?
Index: include/stdlib.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/stdlib.h,v
retrieving revision 1.8
diff -u -r1.8 stdlib.h
--- include/stdlib.h 2001/01/20 22:04:14 1.8
+++ include/stdlib.h 2001/06/02 11:36:55
@@ -1,3 +1,4 @@
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
@@ -143,6 +144,31 @@
#define __system_emulate_chdir 0x4000 /* handle `cd' internally */
extern int __system_flags;
+
+extern void (*__libc_malloc_hook)(size_t, void *);
+extern void (*__libc_malloc_fail_hook)(size_t);
+extern void (*__libc_free_hook)(void *);
+extern void (*__libc_free_null_hook)(void);
+extern void (*__libc_realloc_hook)(void *, size_t);
+
+struct mallinfo {
+ int arena;
+ int ordblks;
+ int smblks;
+ int hblks;
+ int hblkhd;
+ int usmblks;
+ int fsmblks;
+ int uordblks;
+ int fordblks;
+ int keepcost;
+};
+
+struct mallinfo mallinfo(void);
+
+int malloc_verify(void);
+int malloc_debug(int);
+void mallocmap(void);
#endif /* !_POSIX_SOURCE */
#endif /* !__STRICT_ANSI__ */
Index: include/libc/malloc.h
===================================================================
RCS file: /cvs/djgpp/djgpp/include/libc/malloc.h,v
retrieving revision 1.1
diff -u -r1.1 malloc.h
--- include/libc/malloc.h 2001/01/20 04:08:38 1.1
+++ include/libc/malloc.h 2001/06/02 11:37:04
@@ -24,7 +24,11 @@
#define AFTER(bp) ((BLOCK *)((char *)bp + ((bp->size & ~1) + 8)))
#define DATA(bp) ((char *)&(bp->next))
+#ifndef NUMSMALL
+#define NUMSMALL 0
+#endif
#define ALIGN 8
+#define SMALL (NUMSMALL*ALIGN)
#endif /* !_POSIX_SOURCE */
#endif /* !__STRICT_ANSI__ */
Index: src/docs/kb/wc204.txi
===================================================================
RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v
retrieving revision 1.71
diff -u -p -r1.71 wc204.txi
--- src/docs/kb/wc204.txi 2001/06/01 18:04:29 1.71
+++ src/docs/kb/wc204.txi 2001/06/02 11:38:26
@@ -433,3 +433,17 @@ Two new functions, @code{monstartup} and
control when profiling starts and ends, and what range of addresses is
recorded in the profiling data.
+@findex mallinfo AT r{, added}
+@findex malloc_verify AT r{, added}
+@findex malloc_debug AT r{, added}
+@findex mallocmap AT r{, added}
+@findex malloc AT r{, debug facilities}
+There are new @code{malloc} debug facilities. While not as powerful and
+versatile as existing external packages, such as @acronym{YAMD}, these
+facilities do allow to detect a wide variety of heap corruption
+problems, and to report important heap usage statistics. The new
+functions are @code{mallinfo}, @code{malloc_debug},
+@code{malloc_verify}, and @code{mallocmap}; these names are compatible
+with many Unix and GNU/Linux systems. In addition, special hooks, such
+as @code{__libc_malloc_hook} and @code{__libc_free_hook}, are provided
+for building custom @code{malloc} debugging packages.
Index: src/libc/ansi/stdlib/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/makefile,v
retrieving revision 1.3
diff -u -p -r1.3 makefile
--- src/libc/ansi/stdlib/makefile 1998/01/02 01:06:08 1.3
+++ src/libc/ansi/stdlib/makefile 2001/06/02 11:38:28
@@ -20,6 +20,7 @@ SRC += ldiv.c
SRC += llabs.c
SRC += lldiv.c
SRC += malloc.c
+SRC += malldbg.c
SRC += qsort.c
SRC += rand.c
SRC += strtod.c
Index: src/libc/ansi/stdlib/malloc.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/malloc.c,v
retrieving revision 1.9
diff -u -p -r1.9 malloc.c
--- src/libc/ansi/stdlib/malloc.c 2001/05/31 18:21:04 1.9
+++ src/libc/ansi/stdlib/malloc.c 2001/06/02 11:38:30
@@ -9,20 +9,17 @@
#include <string.h>
#include <unistd.h>
#include <libc/malloc.h>
+#include "xmalloc.h"
-#define NUMSMALL 0
-#define SMALL (NUMSMALL*ALIGN)
-
static BLOCK *slop = 0;
static BLOCK *freelist[30];
#if NUMSMALL
static BLOCK *smallblocks[NUMSMALL];
#endif
-
-#define MIN_SAVE_EXTRA 64
-#define BIG_BLOCK 4096
-#define DEBUG 0
+static unsigned long malloc_bytes_in_use;
+static unsigned long malloc_chunks_in_use;
+static unsigned long malloc_sbrked;
#if DEBUG
static void
@@ -49,27 +46,6 @@ consistency()
#define CHECK(p)
#endif
-static inline int
-size2bucket(size_t size)
-{
- int rv=0;
- size>>=2;
- while (size)
- {
- rv++;
- size>>=1;
- }
- return rv;
-}
-
-static inline int
-b2bucket(BLOCK *b)
-{
- if (b->bucket == -1)
- b->bucket = size2bucket(b->size);
- return b->bucket;
-}
-
static inline BLOCK *
split_block(BLOCK *b, size_t size)
{
@@ -87,8 +63,65 @@ split_block(BLOCK *b, size_t size)
CHECK(rv);
return rv;
}
+
+/* These hooks can be used to gain access to the innards of memory
+ allocation routines without bloating each program's size (since
+ malloc is called from the startup code) or adverse effects on
+ run-time performance of programs which don't call these hooks or
+ malloc debug routines (which call them internally). */
+BLOCK **
+__malloc_get_freelist(void)
+{
+ return freelist;
+}
+
+BLOCK *
+__malloc_get_slop(void)
+{
+ return slop;
+}
+
+#if NUMSMALL
+BLOCK **
+__malloc_get_smallblocks(void)
+{
+ return smallblocks;
+}
+#endif
+
+unsigned long
+__malloc_get_bytes_in_use(void)
+{
+ return malloc_bytes_in_use;
+}
+
+unsigned long
+__malloc_get_chunks_in_use(void)
+{
+ return malloc_chunks_in_use;
+}
+
+unsigned long
+__malloc_get_sbrked(void)
+{
+ return malloc_sbrked;
+}
+
+void (*__libc_malloc_hook)(size_t, void *);
+void (*__libc_malloc_fail_hook)(size_t);
+void (*__libc_free_hook)(void *);
+void (*__libc_free_null_hook)(void);
+void (*__libc_realloc_hook)(void *, size_t);
-#define RET(rv) CHECK(rv); ENDSZ(rv) |= 1; rv->size |= 1; return DATA(rv)
+#define RET(rv) \
+ do { \
+ CHECK(rv); \
+ ENDSZ(rv) |= 1; \
+ malloc_bytes_in_use += rv->size; \
+ malloc_chunks_in_use++; \
+ rv->size |= 1; \
+ return DATA(rv); \
+ } while (0)
void *
malloc(size_t size)
@@ -110,6 +143,10 @@ malloc(size_t size)
if (rv)
{
smallblocks[size/ALIGN] = rv->next;
+ if (__libc_malloc_hook)
+ __libc_malloc_hook(size, rv);
+ malloc_bytes_in_use += rv->size;
+ malloc_chunks_in_use++;
return DATA(rv);
}
}
@@ -130,6 +167,8 @@ malloc(size_t size)
}
else
slop = 0;
+ if (__libc_malloc_hook)
+ __libc_malloc_hook(size, rv);
RET(rv);
}
@@ -140,6 +179,8 @@ malloc(size_t size)
if (rv->size >= size && rv->size < size+size/4)
{
*prev = rv->next;
+ if (__libc_malloc_hook)
+ __libc_malloc_hook(size, rv);
RET(rv);
}
}
@@ -177,6 +218,8 @@ malloc(size_t size)
printf(" slop size %u/%08x\n", slop->size, slop);
#endif
}
+ if (__libc_malloc_hook)
+ __libc_malloc_hook(size, rv);
RET(rv);
}
b++;
@@ -185,7 +228,12 @@ malloc(size_t size)
chunk_size = size+16; /* two ends plus two placeholders */
rv = (BLOCK *)sbrk(chunk_size);
if (rv == (BLOCK *)(-1))
+ {
+ if (__libc_malloc_fail_hook)
+ __libc_malloc_fail_hook(size);
return 0;
+ }
+ malloc_sbrked += chunk_size;
#if DEBUG
printf("sbrk(%d) -> %08x, expected %08x\n", chunk_size, rv, expected_sbrk);
#endif
@@ -214,6 +262,8 @@ malloc(size_t size)
AFTER(rv)->size = 1;
CHECK(rv);
+ if (__libc_malloc_hook)
+ __libc_malloc_hook(size, rv);
RET(rv);
}
@@ -289,19 +339,29 @@ free(void *ptr)
{
BLOCK *block;
if (ptr == 0)
+ {
+ if (__libc_free_null_hook)
+ __libc_free_null_hook();
return;
+ }
block = (BLOCK *)((char *)ptr-4);
+ if (__libc_free_hook)
+ __libc_free_hook(block);
#if NUMSMALL
if (block->size < SMALL)
{
block->next = smallblocks[block->size/ALIGN];
smallblocks[block->size/ALIGN] = block;
+ malloc_bytes_in_use -= block->size;
+ malloc_chunks_in_use--;
return;
}
#endif
block->size &= ~1;
+ malloc_bytes_in_use -= block->size;
+ malloc_chunks_in_use--;
ENDSZ(block) &= ~1;
block->bucket = -1;
#if DEBUG
@@ -376,6 +436,7 @@ realloc_inplace(BLOCK *cur, size_t old_s
ENDSZ(after2) = after2->size;
cur->size += alloc_delta;
ENDSZ(cur) = cur->size;
+ malloc_bytes_in_use += alloc_delta;
if (is_slop_ptr)
slop = after2;
else
@@ -386,6 +447,7 @@ realloc_inplace(BLOCK *cur, size_t old_s
/* Merge the entire free block with the block being expanded. */
cur->size += after_sz + 8;
ENDSZ(cur) = cur->size;
+ malloc_bytes_in_use += after_sz + 8;
if (is_slop_ptr)
slop = 0;
}
@@ -403,6 +465,8 @@ realloc(void *ptr, size_t size)
return malloc(size);
b = (BLOCK *)((char *)ptr-4);
+ if (__libc_realloc_hook)
+ __libc_realloc_hook(b, size);
copysize = b->size & ~1;
if (size <= copysize)
{
--- /dev/null Sat Jun 2 14:54:59 2001
+++ src/libc/ansi/stdlib/malldbg.c Sat Jun 2 14:20:18 2001
@@ -0,0 +1,468 @@
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libc/malloc.h>
+
+#include "xmalloc.h"
+
+struct mallinfo
+mallinfo(void)
+{
+ struct mallinfo info;
+ int b;
+ BLOCK *block, **free_list, *slop;
+ unsigned long free_size = 0;
+#if NUMSMALL
+ BLOCK **small_blocks;
+#endif
+
+ info.arena = __malloc_get_sbrked();
+
+ info.ordblks = __malloc_get_chunks_in_use();
+ /* To the chunks in use add the number of blocks on the free list
+ and the slop, if non-NULL. */
+ free_list = __malloc_get_freelist();
+ for (b = 0; b < 30; b++)
+ for (block = free_list[b]; block; block = block->next)
+ {
+ info.ordblks++;
+ free_size += block->size;
+ }
+ slop = __malloc_get_slop();
+ if (slop)
+ {
+ info.ordblks++;
+ free_size += slop->size;
+ }
+
+ info.smblks = info.hblks = info.hblkhd = info.usmblks = info.fsmblks = 0;
+#if NUMSMALL
+ small_blocks = __malloc_get_smallblocks();
+ for (b = 0; b < NUMSMALL; b++)
+ for (block = small_blocks[b]; block; block = block->next)
+ {
+ info.smblks++;
+ info.fsmblks += block->size;
+ }
+#endif
+
+ info.uordblks = __malloc_get_bytes_in_use();
+ info.fordblks = free_size;
+ info.keepcost = 0; /* we don't support mallopt(M_KEEP, ...) */
+
+ return info;
+}
+
+static int malloc_debug_level;
+
+static BLOCK **recorded_blocks;
+static int n_recorded_blocks;
+
+static inline int
+check_block(BLOCK *b)
+{
+ size_t size, endsz;
+ size_t *endsz_ptr;
+ extern unsigned __djgpp_selector_limit;
+
+ if (!b)
+ {
+ if (malloc_debug_level)
+ fprintf(stderr, "Corrupted heap block: addr=0x00000000\n");
+ if (malloc_debug_level > 2)
+ abort();
+ return 0;
+ }
+
+ if ((((unsigned)b + 4) & 3) != 0)
+ {
+ if (malloc_debug_level)
+ fprintf(stderr, "Heap block 0x%08x: address incorrectly aligned\n",
+ (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ return 0;
+ }
+
+ size = b->size;
+ endsz_ptr = (size_t *)((char *)b + (b->size & ~1) + 4);
+ if ((unsigned)endsz_ptr < 0x1000
+ || (unsigned)endsz_ptr >= __djgpp_selector_limit)
+ {
+ if (malloc_debug_level)
+ {
+ fprintf(stderr,
+ "Bad size info in heap block: addr=0x%08x size=0x%08x\n",
+ (unsigned)b, (unsigned)endsz_ptr);
+ }
+ if (malloc_debug_level > 2)
+ abort();
+ return 0;
+ }
+ else
+ endsz = *endsz_ptr;
+ if (size != endsz)
+ {
+ if (malloc_debug_level)
+ {
+ fprintf(stderr,
+ "Corrupted heap block: addr=0x%08x size=0x%08x ENDSZ=0x%08x\n",
+ (unsigned)b, (unsigned)size, (unsigned)endsz);
+ }
+ if (malloc_debug_level > 2)
+ abort();
+ return 0;
+ }
+ return 1;
+}
+
+int
+malloc_verify(void)
+{
+ BLOCK *b, **free_list;
+#if NUMSMALL
+ BLOCK **small_blks;
+#endif
+ int i, result = 1;
+ extern unsigned __djgpp_selector_limit;
+ BLOCK *slop = __malloc_get_slop();
+
+ if (slop
+ && ((unsigned)slop < 0x1000
+ || (unsigned)slop >= __djgpp_selector_limit
+ || (((unsigned)slop + 4) & 3) != 0))
+ {
+ result = 0;
+ if (malloc_debug_level)
+ {
+ fprintf(stderr, "Bad slop 0x%08x\n", (unsigned)slop);
+ if (malloc_debug_level > 2)
+ abort();
+ }
+ }
+ else if (slop)
+ result &= check_block(slop);
+
+ free_list = __malloc_get_freelist();
+ if (free_list)
+ {
+ for (i = 0; i < 30; i++)
+ for (b = free_list[i]; b; b = b->next)
+ {
+ if ((unsigned)b < 0x1000 || (unsigned)b >= __djgpp_selector_limit
+ || (((unsigned)b + 4) & 3) != 0)
+ {
+ result = 0;
+ if (malloc_debug_level)
+ {
+ if ((((unsigned)b + 4) & 3) != 0)
+ fprintf(stderr,
+ "Free block 0x%08x: address incorrectly aligned\n",
+ (unsigned)b);
+ else
+ fprintf(stderr,
+ "Free block 0x%08x: address out of valid range\n",
+ (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ else
+ break; /* we cannot use b->next, so go to next bucket */
+ }
+ }
+ else
+ result &= check_block(b);
+ }
+ }
+
+#if NUMSMALL
+ small_blks = __malloc_get_smallblocks();
+ if (small_blks)
+ {
+ for (i = 0; i < NUMSMALL; i++)
+ for (b = small_blks[i]; b; b = b->next)
+ {
+ if ((unsigned)b < 0x1000 || (unsigned)b >= __djgpp_selector_limit
+ || (((unsigned)b + 4) & 3) != 0)
+ {
+ result = 0;
+ if (malloc_debug_level)
+ {
+ if ((((unsigned)b + 4) & 3) != 0)
+ fprintf(stderr,
+ "Free small block 0x%08x: address incorrectly aligned\n",
+ (unsigned)b);
+ else
+ fprintf(stderr,
+ "Free small block 0x%08x: address out of valid range\n",
+ (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ else
+ break; /* we cannot use b->next, so go to next bucket */
+ }
+ }
+ else
+ result &= check_block(b);
+ }
+ }
+#endif
+
+ if (recorded_blocks)
+ for (i = 0; i < n_recorded_blocks; i++)
+ if (recorded_blocks[i])
+ result &= check_block(recorded_blocks[i]);
+
+ return result;
+}
+
+static int next_vacant;
+
+static void
+note_malloc(size_t size, void *ptr)
+{
+ int i;
+ BLOCK *b = ptr;
+ extern unsigned __djgpp_selector_limit;
+
+ /* Record the allocated block. */
+ if (recorded_blocks)
+ {
+ /* See if we can find a vacant cell using the hint from the last
+ invocation. */
+ if (!recorded_blocks[next_vacant])
+ {
+ recorded_blocks[next_vacant++] = b;
+ if (next_vacant >= n_recorded_blocks)
+ next_vacant = 0;
+ i = 0;
+ }
+ else
+ {
+ for (i = 0; i < n_recorded_blocks; i++)
+ if (!recorded_blocks[i])
+ {
+ recorded_blocks[i] = b;
+ next_vacant = i + 1;
+ if (next_vacant >= n_recorded_blocks)
+ next_vacant = 0;
+ break;
+ }
+ }
+
+ if (i == n_recorded_blocks && malloc_debug_level > 2)
+ fprintf(stderr, "No space to record block 0x%08x of size 0x%08x\n",
+ (unsigned)b, (unsigned)size);
+ }
+
+ if (malloc_debug_level > 1)
+ {
+ if ((unsigned)b < 0x1000 || (unsigned)b >= __djgpp_selector_limit)
+ {
+ fprintf(stderr, "Block address 0x%08x out of range\n",
+ (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ }
+ malloc_verify();
+ }
+}
+
+static void
+note_free(void *ptr)
+{
+ BLOCK *b = ptr;
+ int i;
+
+ /* Remove this block from the list of recorded blocks. */
+ if (recorded_blocks)
+ {
+ /* Check the cached vacant position, in case they are freeing
+ memory in the same or reverse order as they allocated it. */
+ if (next_vacant < n_recorded_blocks - 1
+ && recorded_blocks[next_vacant + 1] == b)
+ i = next_vacant + 1;
+ else if (next_vacant > 0 && recorded_blocks[next_vacant - 1] == b)
+ i = next_vacant - 1;
+ else if (recorded_blocks[next_vacant] == b)
+ i = next_vacant;
+ else
+ i = 0;
+ for ( ; i < n_recorded_blocks; i++)
+ if (recorded_blocks[i] == b)
+ {
+ recorded_blocks[i] = NULL;
+ next_vacant = i;
+ break;
+ }
+
+ if (i == n_recorded_blocks && malloc_debug_level > 1)
+ {
+ fprintf(stderr,
+ "Block 0x%08x freed, but is not known to be allocated\n",
+ (unsigned)ptr);
+ if (malloc_debug_level > 2)
+ abort();
+ }
+ }
+
+ if (malloc_debug_level > 1)
+ malloc_verify();
+}
+
+static void
+note_malloc_fail(size_t size)
+{
+ fprintf(stderr, "Allocation failed for size 0x%08x\n", (unsigned)size);
+}
+
+static void
+note_free_null(void)
+{
+ fprintf(stderr, "NULL pointer free'd\n");
+}
+
+static void
+note_realloc(void *ptr, size_t size)
+{
+ if (malloc_debug_level > 1)
+ {
+ BLOCK *b = ptr;
+ extern unsigned __djgpp_selector_limit;
+
+ if ((unsigned)b < 0x1000 || (unsigned)b >= __djgpp_selector_limit)
+ {
+ fprintf(stderr, "Block address 0x%08x out of range\n", (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ }
+ else if ((((unsigned)b + 4) & 3) != 0)
+ {
+ fprintf(stderr, "Block address 0x%08x incorrectly aligned\n",
+ (unsigned)b);
+ if (malloc_debug_level > 2)
+ abort();
+ }
+ }
+}
+
+int
+malloc_debug(int level)
+{
+ int old_level = malloc_debug_level;
+ static char debug_linebuf[160]; /* enough? */
+
+ malloc_debug_level = level;
+
+ if (malloc_debug_level)
+ {
+ /* Make sure we can use fprintf to stderr in the hooks without a
+ risk of reentering malloc. */
+ if (stderr->_base == NULL && (stderr->_flag & _IONBF) == 0)
+ if (setvbuf(stderr, debug_linebuf, _IOLBF, sizeof(debug_linebuf)))
+ abort();
+
+ __libc_malloc_hook = note_malloc;
+ __libc_free_hook = note_free;
+ __libc_realloc_hook = note_realloc;
+
+ if (!recorded_blocks)
+ {
+ char *mdinfo = getenv("MALLOC_DEBUG"), *endp;
+ if (mdinfo)
+ {
+ n_recorded_blocks = strtol(mdinfo, &endp, 0);
+ if (n_recorded_blocks < 0 || n_recorded_blocks > 16*1024*1024)
+ n_recorded_blocks = 0;
+ }
+ if (!n_recorded_blocks)
+ n_recorded_blocks = 100*1024;
+
+ recorded_blocks = (BLOCK **) sbrk(n_recorded_blocks * sizeof(BLOCK *));
+ if ((long)recorded_blocks == -1)
+ {
+ recorded_blocks = NULL;
+ n_recorded_blocks = 0;
+ }
+ else
+ memset(recorded_blocks, 0, n_recorded_blocks * sizeof(BLOCK *));
+ }
+
+ if (malloc_debug_level > 2)
+ {
+ __libc_malloc_fail_hook = note_malloc_fail;
+ if (malloc_debug_level > 3)
+ __libc_free_null_hook = note_free_null;
+ else
+ __libc_free_null_hook = NULL;
+ }
+ else
+ {
+ __libc_malloc_fail_hook = NULL;
+ __libc_free_null_hook = NULL;
+ }
+ }
+ else
+ {
+ __libc_malloc_hook = NULL;
+ __libc_free_hook = NULL;
+ __libc_realloc_hook = NULL;
+ __libc_malloc_fail_hook = NULL;
+ __libc_free_null_hook = NULL;
+ }
+ return old_level;
+}
+
+static inline void
+print_block(BLOCK *b, const char *remark)
+{
+ if (b)
+ {
+ size_t size = b->size;
+ size_t endsz = *(size_t *)((char *)b + (b->size & ~1) + 4);
+
+ printf(" Addr=0x%08x size=0x%08x ENDSZ=0x%08x %s\n",
+ (unsigned)b, (unsigned)size, (unsigned)endsz, remark);
+ }
+}
+
+void
+mallocmap(void)
+{
+ BLOCK *b, *slop, **free_list;
+#if NUMSMALL
+ BLOCK **small_blks;
+#endif
+ int i;
+
+ slop = __malloc_get_slop();
+ free_list = __malloc_get_freelist();
+
+ if (slop)
+ print_block(slop, "slop");
+
+ if (free_list)
+ {
+ for (i = 0; i < 30; i++)
+ for (b = free_list[i]; b; b = b->next)
+ print_block(b, "free");
+ }
+
+#if NUMSMALL
+ small_blks = __malloc_get_smallblocks();
+ if (small_blks)
+ {
+ for (i = 0; i < NUMSMALL; i++)
+ for (b = small_blks[i]; b; b = b->next)
+ print_block(b, "small");
+ }
+#endif
+
+ if (recorded_blocks)
+ {
+ for (i = 0; i < n_recorded_blocks; i++)
+ print_block(recorded_blocks[i], "in use");
+ }
+}
--- /dev/null Sat Jun 2 14:55:08 2001
+++ src/libc/ansi/stdlib/xmalloc.h Sat Jun 2 13:33:22 2001
@@ -0,0 +1,34 @@
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+
+#define MIN_SAVE_EXTRA 64
+#define BIG_BLOCK 4096
+
+#define DEBUG 0
+
+static inline int
+size2bucket(size_t size)
+{
+ int rv=0;
+ size>>=2;
+ while (size)
+ {
+ rv++;
+ size>>=1;
+ }
+ return rv;
+}
+
+static inline int
+b2bucket(BLOCK *b)
+{
+ if (b->bucket == -1)
+ b->bucket = size2bucket(b->size);
+ return b->bucket;
+}
+
+BLOCK ** __malloc_get_freelist(void);
+BLOCK * __malloc_get_slop(void);
+BLOCK ** __malloc_get_smallblocks(void);
+unsigned long __malloc_get_bytes_in_use(void);
+unsigned long __malloc_get_chunks_in_use(void);
+unsigned long __malloc_get_sbrked(void);
Index: src/libc/ansi/stdlib/malloc.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdlib/malloc.txh,v
retrieving revision 1.5
diff -u -p -r1.5 malloc.txh
--- src/libc/ansi/stdlib/malloc.txh 2001/01/24 14:01:13 1.5
+++ src/libc/ansi/stdlib/malloc.txh 2001/06/02 11:38:32
@@ -113,3 +113,356 @@ if (now+new > max)
@}
@end example
+@c ----------------------------------------------------------------------
+
+@node mallinfo, memory
+@subheading Syntax
+
+@example
+#include <stdlib.h>
+
+struct mallinfo mallinfo(void);
+@end example
+
+@subheading Description
+
+This function returns information about heap space usage. It is
+intended to be used for debugging dynamic memory allocation and tracking
+heap usage. The @code{struct mallinfo} structure is defined by
+@file{stdlib.h} as follows:
+
+@example
+ struct mallinfo @{
+ int arena;
+ int ordblks;
+ int smblks;
+ int hblks;
+ int hblkhd;
+ int usmblks;
+ int fsmblks;
+ int uordblks;
+ int fordblks;
+ int keepcost;
+ @};
+@end example
+
+@noindent
+whose members are:
+
+@table @code
+@item arena
+The total amount of space, in bytes, handed by @code{sbrk} to
+@code{malloc}. Note that this is not the same as @code{sbrk(0)}, since
+@code{sbrk} allocates memory in large chunks and then subdivides them
+and passes them to @code{malloc} as required. In particular, the result
+of @code{sbrk(0)} might be much larger than the @code{arena} member of
+@code{struct mallinfo} when the DPMI host allocates memory in
+non-contiguous regions (happens on MS-Windows).
+
+@item ordblks
+The number of ``ordinary blocks'': the total number of allocated and
+free blocks maintained by @code{malloc}.
+
+@item smblks
+The number of ``small blocks''. This is normally zero, unless
+@code{malloc} was compiled with the symbol @code{NUMSMALL} defined to a
+non-zero value. Doing so activates an optional algorithm which serves
+small allocations quickly from a special pool. If this option is
+activated, the @code{smblks} member returns the number of free small
+blocks (the allocated small blocks are included in the value of
+@code{ordblks}).
+
+@item hblks
+@itemx hblkhd
+Always zero, kept for compatibility with other systems.
+
+@item usmblks
+The space (in bytes) in ``small blocks'' that are in use. This is
+always zero in the DJGPP implementation.
+
+@item fsmblks
+The space in free ``small blocks''. Non-zero only of @code{malloc} was
+compiled with @code{NUMSMALL} defined to a non-zero value. In that
+case, gives the amount of space in bytes in free small blocks.
+
+@item uordblks
+The amount of space, in bytes, in the heap space currently used by the
+application. This does not include the small overhead (8 bytes per
+block) used by @code{malloc} to maintain its hidden information in each
+allocated block.
+
+@item fordblks
+The amount of free heap space maintained by @code{malloc} in its free
+list.
+
+@item keepcost
+Always zero, kept for compatibility.
+@end table
+
+@subheading Return Value
+
+The @code{mallinfo} structure filled with information.
+
+@subheading Portability
+
+@port-note posix This function is available on many Unix systems.
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+ struct mallinfo info = mallinfo();
+
+ printf("Memory in use: %d bytes\n",
+ info.usmblks + info.uordblks);
+ printf("Total heap size: %d bytes\n", info.arena);
+@end example
+
+@c ----------------------------------------------------------------------
+
+@node malloc_verify, memory
+@subheading Syntax
+
+@example
+#include <stdlib.h>
+
+int malloc_verify(void);
+@end example
+
+@subheading Description
+
+This function attempts to determine if the heap has been corrupted. It
+scans all the blocks allocated by @code{malloc} and handed to the
+application, and also all the free blocks maintained by @code{malloc}
+and @code{free} in the internal free list. Each block is checked for
+consistency of the hidden bookkeeping information recorded in it by
+@code{malloc} and @code{free}. The blocks on the free list are
+additionally validated by chasing all the @code{next} pointers in the
+linked list and checking them against limits for valid pointers (between
+0x1000 and the data segment limit), and the alignment. (Unaligned
+pointers are probably corrupted, since @code{malloc} always returns a
+properly aligned storage.)
+
+What happens when a bad block is found depends on the current
+@dfn{malloc diagnostics level}: for example, the block can be reported,
+or the program may be aborted. @xref{malloc_debug}, for the details.
+
+@subheading Return Value
+
+If the program isn't aborted during the function's run (this depends on
+the current diagnostics level), @code{malloc_verify} returns 1 if the
+heap passes all tests, or zero of some of the tests failed.
+
+@subheading Portability
+
+@port-note posix This function is available on many Unix systems.
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+ if (malloc_verify() == 0)
+ printf ("Heap corruption detected!\n");
+@end example
+
+@c ----------------------------------------------------------------------
+
+@node malloc_debug, memory
+@subheading Syntax
+
+@example
+#include <stdlib.h>
+
+int malloc_debug(int level);
+@end example
+
+@subheading Description
+
+This function sets the level of error diagnosis and reporting during
+subsequent calls to @code{malloc}, @code{free}, @code{realloc}, and all
+functions which call them internally. The argument @var{level} is
+interpreted as follows:
+
+@table @asis
+@item Level 0
+No checking; the memory allocation functions behave as they do if
+@code{malloc_debug} was never called. Memory in use by the application
+which was allocated while level 0 was in effect cannot be checked by
+@code{malloc_verify} unless it is @code{free}d first.
+
+@item Level 1
+Each one of the allocated blocks is recorded in a special structure,
+where @code{malloc_verify} can test them for corruption, even if these
+blocks were not yet @code{free}d. If errors are detected by
+@code{malloc_verify}, it prints diagnostic messages to the standard
+error stream, with address and size of the offending block and other
+pertinent information. This level slows down memory allocation to some
+extent due to additional overhead of calling special functions which
+record extra debugging info.
+
+@item Level 2
+Like level 1, but in addition the consistency of the entire heap is
+verified (by calling @code{malloc_verify}) on every call to the memory
+allocation functions. @emph{Warning: this may significantly slow down
+the application.}
+
+@item Level 3
+Like level 2, except that the program is aborted whenever a heap
+corruption is detected. In addition, failed allocations (i.e.@: when
+@code{malloc} returns @code{NULL} because it cannot satisfy a request)
+are reported to standard error. Also, if the storage where allocated
+blocks are recorded is exhausted, a message to that effect is printed.
+
+@item Level 4
+Like level 3, but calls to @code{free} with a @code{NULL} pointer as an
+argument are also reported.
+@end table
+
+When @code{malloc_debug} is first called with a positive argument, it
+allocates storage for recording blocks in use. To avoid reentrancy
+problems, this storage is allocated via a direct call to @code{sbrk},
+and its size is fixed. The size used to allocate this storage is by
+default 400KB, which is enough to record 100 thousand allocated blocks.
+You can tailor the size to your needs by setting the environment
+variable @code{MALLOC_DEBUG} to the maximum number of blocks you want to
+be able to track. (The value of @code{MALLOC_DEBUG} should only be as
+large as the maximum number of allocations which is expected to be in
+use at any given time, because when a buffer is freed, it is removed
+from this storage and its cell can be reused for another allocation.)
+Note that the larger this storage size, the more slow-down will your
+program experience when the diagnostic level is set to a non-zero value,
+since the debugging code needs to search the list of recorded blocks in
+use each time you call @code{malloc} or @code{free}.
+
+@subheading Return Value
+
+@code{malloc_debug} returns the previous error diagnostic level. The
+default level is 0.
+
+@subheading Portability
+
+@port-note posix This function is available on many Unix systems.
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+ malloc_debug(2);
+ ...
+ malloc_verify();
+@end example
+
+@c ----------------------------------------------------------------------
+
+@node mallocmap, memory
+@subheading Syntax
+
+@example
+#include <stdlib.h>
+
+void mallocmap(int level);
+@end example
+
+@subheading Description
+
+This function prints a map of the heap storage to standard output. For
+each block, its address and size are printed, as well as an indication
+whether it is free or in use. If the @dfn{slop} (a special free block
+cached for performance reasons) and the small blocks are available, they
+are printed as well (these two are variants of free blocks). Blocks in
+use will only be printed if the diagnostic level was set to a non-zero
+value by a call to @code{malloc_debug} (@pxref{malloc_debug}), since
+otherwise the allocated blocks are not recorded by @code{malloc}.
+
+@subheading Return Value
+
+None.
+
+@subheading Portability
+
+@port-note posix This function is available on many Unix systems.
+@portability !ansi, !posix
+
+@c ----------------------------------------------------------------------
+
+@node malloc hook functions, memory
+@subheading Syntax
+
+@example
+#include <stdlib.h>
+#include <libc/malloc.h>
+
+void (*__libc_malloc_hook)(size_t size, void *block);
+void (*__libc_malloc_fail_hook)(size_t size);
+void (*__libc_free_hook)(void *block);
+void (*__libc_free_null_hook)(void);
+void (*__libc_realloc_hook)(void *block, size_t size);
+@end example
+
+@subheading Description
+
+These hooks are provided for building custom @code{malloc} debugging
+packages. Such packages typically need to be notified when memory is
+allocated and freed by the application, in order to be able to find
+memory leaks, code that writes beyond the limits of allocated buffers or
+attempts to free buffers which were not allocated by @code{malloc}, etc.
+These hooks can be used to define callback functions which will be
+called by the library at strategic points. Each callback is only called
+if it is non- AT code{NULL}; by default, all of them are initialized to a
+@code{NULL} value.
+
+@table @code
+@item __libc_malloc_hook
+Called just before a chunk of memory is about to be returned to the
+application in response to an allocation request. @var{size} is the
+size requested by the application (@strong{not} the actual size of the
+allocated buffer, which may be larger). @var{block} is a pointer to the
+block that was allocated, which is 4 bytes before the pointer that
+@code{malloc} will return to the application; these 4 bytes are used to
+record the actual size of the buffer. An additional copy of the block's
+size is recorded immediately after the buffer's end. Thus,
+@w{@code{*(size_t *)((char *)block + 4 + (BLOCK *)block->size)}} gives
+the second copy of the block's size.
+
+@item __libc_malloc_fail_hook
+Called if @code{malloc} failed to find a free block large enough to
+satisfy a request, and also failed to obtain additional memory from
+@code{sbrk}. @var{size} is the requested allocation size.
+
+@item __libc_free_hook
+Called when a buffer is about to be freed. @var{block} is a pointer 4
+bytes before the address passed to @code{free} by the application,
+i.e.@: it is a pointer to the beginning of the size information
+maintained before the user buffer.
+
+@item __libc_free_null_hook
+Called whenever a @code{NULL} pointer is passed to @code{free}.
+@acronym{ANSI} C specifically rules that this is allowed and should have
+no effect, but you might want to catch such cases if your program needs
+to be portable to old compilers whose libraries don't like @code{NULL}
+pointers in @code{free}.
+
+@item __libc_realloc_hook
+Called at entry to @code{realloc}, before the actual reallocation.
+@var{block} is a pointer 4 bytes before the address passed to
+@code{free} by the application, i.e.@: it is a pointer to the beginning
+of the size information maintained before the user buffer. @var{size}
+is the new size requested by the application. (This hook is called
+@emph{in addition} to the other hooks which will be called by
+@code{free} and @code{malloc} if and when @code{realloc} calls them.)
+@end table
+
+The @code{BLOCK} data type is used by @code{malloc} and @code{free} to
+maintain the heap. The only member which is always guaranteed to be
+valid is @code{size} (the additional copy of the size, recorded beyond
+the buffer's end, is also guaranteed to be valid). The @code{next}
+member is valid in all blocks that are part of the free list. This
+means that @code{__libc_malloc_hook} can use the @code{next} member, but
+@code{__libc_free_hook} cannot.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+These hooks are specific to DJGPP.
+
Index: tests/libc/ansi/stdlib/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/stdlib/makefile,v
retrieving revision 1.2
diff -u -p -r1.2 makefile
--- tests/libc/ansi/stdlib/makefile 1998/01/01 21:42:08 1.2
+++ tests/libc/ansi/stdlib/makefile 2001/06/02 11:48:55
@@ -6,5 +6,6 @@ SRC += shell.c
SRC += strtod.c
SRC += system.c
SRC += system2.c
+SRC += tmalloc.c
include $(TOP)/../makefile.inc
--- /dev/null Sat Jun 2 14:55:25 2001
+++ tests/libc/ansi/stdlib/tmalloc.c Sat Jun 2 13:02:42 2001
@@ -0,0 +1,114 @@
+/* Testbed for the malloc-debug facilities. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+print_malloc_info (void)
+{
+ struct mallinfo info = mallinfo ();
+
+ printf (">>> mallinfo reports:\n"
+ " Arena: %d\n"
+ " Ordinary blocks: %d\n"
+ " Small blocks: %d\n"
+ " Space in small blocks in use: %d\n"
+ " Space in free small blocks: %d\n"
+ " Space in ordinary blocks in use: %d\n"
+ " Space in free ordinary blocks: %d\n",
+ info.arena, info.ordblks, info.smblks,
+ info.usmblks, info.fsmblks, info.uordblks, info.fordblks);
+}
+
+#define MAXTBL 1024
+struct alloc {
+ char *ptr;
+ size_t size;
+};
+
+int main (int argc, char *argv[])
+{
+ struct alloc table[MAXTBL];
+ size_t total = 0;
+ volatile int i, j;
+ char buf[80];
+ int corrupted_blocks;
+
+ printf ("\n *** Test of malloc debugging facilities ***\n\n");
+ sprintf (buf, "MALLOC_DEBUG=%d", MAXTBL * 2);
+ putenv (buf);
+ print_malloc_info ();
+ printf ("\n>>> mallocmap reports (expect to see only free blocks):\n");
+ mallocmap ();
+
+ /* First, allocate and then free some memory, to prime the malloc
+ internal data structures. */
+ malloc_debug (1);
+ for (i = 0; i < MAXTBL; i++)
+ {
+ size_t chunk_size = (rand() >> 18) + 10;
+
+ table[i].ptr = malloc (chunk_size);
+ table[i].size = chunk_size;
+ if (table[i].ptr == NULL)
+ {
+ printf ("!!!malloc failed after %d requests for chunk size %lu%s\n",
+ i, chunk_size < 1024 ? chunk_size : chunk_size / 1024,
+ chunk_size < 1024 ? "" : "KB");
+ break;
+ }
+ total += chunk_size;
+ }
+
+ printf ("\n=== Application requested %lu bytes in %d allocations\n",
+ total, i);
+ print_malloc_info ();
+ printf ("\n>>> mallocmap reports (expect to see only used blocks):\n");
+ mallocmap ();
+ printf ("\n=== Heap corruption test:\n");
+ if (malloc_verify () == 0)
+ printf ("!!! FAILED (??? better look for bugs...)\n");
+ else
+ printf (">>> PASSED (as expected)\n");
+ for (j = 0; j < i; j++)
+ free (table[j].ptr);
+ printf ("\n=== Freed everything.\n");
+ print_malloc_info ();
+
+ /* Now allocate memory again, and fill each chunk with random
+ characters, overrunning some of the buffers in the process. */
+ printf ("\n=== Corrupt memory test (expect early exit and corrupted block reports).\n");
+ malloc_debug (1);
+ for (i = 0, corrupted_blocks = 0; i < MAXTBL; i++)
+ {
+ size_t chunk_size = (rand() >> 18) + 10;
+ int fill_value = (rand () & 0x7f) + ' ';
+ size_t n_to_fill = chunk_size +
+ /* about 1% of corrupted blocks */
+ ((rand () > RAND_MAX - RAND_MAX/100) ? 50 : 0);
+
+ fflush (stdout);
+ table[i].ptr = malloc (chunk_size);
+ table[i].size = chunk_size;
+ if (table[i].ptr == NULL)
+ {
+ printf ("!!!malloc failed after %d requests for chunk size %lu%s\n",
+ i, chunk_size < 1024 ? chunk_size : chunk_size / 1024,
+ chunk_size < 1024 ? "" : "KB");
+ break;
+ }
+ if (n_to_fill > chunk_size)
+ corrupted_blocks++;
+ memset (table[i].ptr, fill_value, n_to_fill);
+ if (malloc_verify () == 0)
+ {
+ printf ("!!!Heap corruption detected after %d allocations\n"
+ " and %d corrupted blocks; exiting.\n",
+ i, corrupted_blocks);
+ return 1;
+ }
+ }
+
+ return 0;
+}
- Raw text -