www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/06/02/08:04:14

Date: Sat, 02 Jun 2001 15:01:54 +0300
From: "Eli Zaretskii" <eliz AT is DOT elta DOT co DOT il>
Sender: halo1 AT zahav DOT net DOT il
To: djgpp-workers AT delorie DOT com
Message-Id: <9003-Sat02Jun2001150153+0300-eliz@is.elta.co.il>
X-Mailer: Emacs 20.6 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.9
Subject: malloc debugging facilities
Reply-To: djgpp-workers AT delorie DOT com

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 -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019