bin/52907: [PATCH] more malloc options for debugging programs
Dan Nelson
dnelson at allantgroup.com
Thu Oct 16 07:50:31 PDT 2003
The following reply was made to PR bin/52907; it has been noted by GNATS.
From: Dan Nelson <dnelson at allantgroup.com>
To: freebsd-gnats-submit at FreeBSD.org
Cc:
Subject: Re: bin/52907: [PATCH] more malloc options for debugging programs
Date: Thu, 16 Oct 2003 09:47:08 -0500
An updated patch, to keep up with changes in -current:
Index: lib/libc/stdlib/malloc.3
===================================================================
RCS file: /home/ncvs/src/lib/libc/stdlib/malloc.3,v
retrieving revision 1.60
diff -p -u -r1.60 malloc.3
--- lib/libc/stdlib/malloc.3 24 Dec 2002 13:41:45 -0000 1.60
+++ lib/libc/stdlib/malloc.3 3 Jun 2003 15:10:13 -0000
@@ -177,19 +177,45 @@ flags being set) become fatal.
The process will call
.Xr abort 3
in these cases.
+.It C
+This option sets the
+.Dq J
+option, keeps a count of the total number of memory allocation calls,
+and when filling memory with junk, initializes the low-order bytes in each
+4-byte word with the running count.
+For example, the 255th malloc operation will be initialized with 0xd0d0d0ff,
+and the next free after that will have its memory set to 0xd1d10100.
+Note that there is some ambiguity in interpreting these values;
+0xd0d0d0ff could also have been the 53503rd or 13684991th operation.
+.It Cn
+Keep a count of the total number of memory allocation calls,
+and abort at the nth operation.
+n can be specified in decimal, or it can be hexidecimal (prefixed with 0x).
+If a hex value is used, ensure that no options that may be confused with
+hex digits follow this option.
+A lowercase
+.Dq c
+will clear both the
+.Dq C
+and
+.Dq Cn
+options.
+.Dq C0
+can be used to clear only this option, if needed.
.It J
Each byte of new memory allocated by
.Fn malloc ,
.Fn realloc
or
-.Fn reallocf
-as well as all memory returned by
+.Fn reallocf
+will be initialized to 0xd0.
+All memory returned by
.Fn free ,
.Fn realloc
or
.Fn reallocf
-will be initialized to 0xd0.
-This options also sets the
+will be initialized to 0xd1.
+This option also sets the
.Dq R
option.
This is intended for debugging and will impact performance negatively.
@@ -281,6 +307,12 @@ If the environment variable
.Ev MALLOC_OPTIONS
is set, the characters it contains will be interpreted as flags to the
allocation functions.
+.It Ev MALLOC_COUNT
+If the environment variable
+.Ev MALLOC_COUNT
+is set, it will be interpreted as a count value (see the
+.Dq Cn
+option). This value will override any counts specified in MALLOC_OPTIONS.
.El
.Sh RETURN VALUES
The
Index: lib/libc/stdlib/malloc.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/stdlib/malloc.c,v
retrieving revision 1.79
diff -p -u -r1.79 malloc.c
--- lib/libc/stdlib/malloc.c 27 Sep 2003 18:58:26 -0000 1.79
+++ lib/libc/stdlib/malloc.c 30 Sep 2003 18:45:11 -0000
@@ -26,7 +26,8 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/
* What to use for Junk. This is the byte value we use to fill with
* when the 'J' option is enabled.
*/
-#define SOME_JUNK 0xd0 /* as in "Duh" :-) */
+#define ALLOC_JUNK 0xd0 /* as in "Duh" :-) */
+#define FREE_JUNK 0xd1 /* as in "Die" :-) */
/*
* The basic parameters you can tweak.
@@ -248,6 +249,15 @@ static int malloc_zero;
/* junk fill ? */
static int malloc_junk = 1;
+/* junk fill with a counter ? */
+static int malloc_counter_junk = 0;
+
+/* keep track of the total number of malloc/realloc/frees done */
+static u_int32_t malloc_counter = 0;
+
+/* If this is nonzero, die when malloc_counter == malloc_counter_abort */
+static u_int32_t malloc_counter_abort = 0;
+
#ifdef HAS_UTRACE
/* utrace ? */
@@ -288,6 +298,7 @@ static int extend_pgdir(u_long index);
static void *imalloc(size_t size);
static void ifree(void *ptr);
static void *irealloc(void *ptr, size_t size);
+static void fillrange(void *ptr, size_t size, u_int32_t value, u_char pad);
static void
wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4)
@@ -445,6 +456,16 @@ malloc_init ()
case '<': malloc_cache >>= 1; break;
case 'a': malloc_abort = 0; break;
case 'A': malloc_abort = 1; break;
+ case 'c': malloc_counter_abort = 0; malloc_counter_junk = 0; break;
+ case 'C':
+ if (p[1] >= '0' && p[1] <= '9') {
+ errnosave = errno;
+ malloc_counter_abort = strtoul(p+1, &p, 0);
+ errno = errnosave;
+ p--; /* decrement to compensate for the for loop */
+ } else
+ malloc_counter_junk = 1;
+ break;
case 'h': malloc_hint = 0; break;
case 'H': malloc_hint = 1; break;
case 'r': malloc_realloc = 0; break;
@@ -471,6 +492,13 @@ malloc_init ()
}
}
+ p = getenv("MALLOC_COUNT");
+ if (p) {
+ errnosave = errno;
+ malloc_counter_abort = strtoul(p, NULL, 0);
+ errno = errnosave;
+ }
+
/*
* Sensitive processes, somewhat arbitrarily defined here as setuid,
* setgid, root and wheel cannot afford to have malloc mistakes.
@@ -481,6 +509,13 @@ malloc_init ()
UTRACE(0, 0, 0);
/*
+ * Filling junk with a running count implies filing with junk in the
+ * first place.
+ */
+ if (malloc_counter_junk)
+ malloc_junk=1;
+
+ /*
* We want junk in the entire allocation, and zero only in the part
* the user asked for.
*/
@@ -598,7 +633,7 @@ malloc_pages(size_t size)
page_dir[index+i] = MALLOC_FOLLOW;
if (malloc_junk)
- memset(p, SOME_JUNK, size << malloc_pageshift);
+ fillrange(p, size << malloc_pageshift, malloc_counter, ALLOC_JUNK);
}
if (delay_free) {
@@ -733,7 +768,7 @@ malloc_bytes(size_t size)
k <<= bp->shift;
if (malloc_junk)
- memset((u_char*)bp->page + k, SOME_JUNK, bp->size);
+ fillrange((u_char*)bp->page + k, bp->size, malloc_counter, ALLOC_JUNK);
return ((u_char *)bp->page + k);
}
@@ -906,7 +941,7 @@ free_pages(void *ptr, u_long index, stru
l = i << malloc_pageshift;
if (malloc_junk)
- memset(ptr, SOME_JUNK, l);
+ fillrange(ptr, l, malloc_counter, FREE_JUNK);
if (malloc_hint)
madvise(ptr, l, MADV_FREE);
@@ -1028,7 +1063,7 @@ free_bytes(void *ptr, u_long index, stru
}
if (malloc_junk)
- memset(ptr, SOME_JUNK, info->size);
+ fillrange(ptr, info->size, malloc_counter, FREE_JUNK);
info->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS);
info->free++;
@@ -1109,6 +1144,28 @@ ifree(void *ptr)
return;
}
+static void fillrange(void *ptr, size_t size, u_int32_t value, u_char pad)
+{
+ int i = 0;
+
+ if (malloc_counter_junk)
+ {
+ u_int32_t fillvalue;
+ if (value < 256) // fits in one byte: D0D0D012
+ fillvalue = pad<<24 | pad<<16 | pad<<8 | value;
+ else if (value < 65536) // fits in two bytes D0D01234
+ fillvalue = pad<<24 | pad<<16 | value;
+ else // D0123456
+ fillvalue = pad<<24 | (value & 0x00FFFFFF);
+
+ for( ; i < (size/4)*4 ; i += 4)
+ ((u_int32_t *)ptr)[i/4] = fillvalue;
+ }
+
+ for( ; i < size ; i++)
+ ((u_char *)ptr)[i] = pad;
+}
+
/*
* These are the public exported interface routines.
*/
@@ -1128,6 +1185,11 @@ malloc(size_t size)
errno = EDOOFUS;
return (NULL);
}
+ if (!malloc_started)
+ malloc_init();
+ malloc_counter++;
+ if (malloc_counter_abort && malloc_counter == malloc_counter_abort)
+ wrterror("Aborting as requested\n");
if (malloc_sysv && !size)
r = NULL;
else if (!size)
@@ -1156,6 +1218,9 @@ free(void *ptr)
errno = EDOOFUS;
return;
}
+ malloc_counter++;
+ if (malloc_counter_abort && malloc_counter == malloc_counter_abort)
+ wrterror("Aborting as requested\n");
if (ptr != ZEROSIZEPTR)
ifree(ptr);
UTRACE(ptr, 0, 0);
@@ -1179,6 +1244,15 @@ realloc(void *ptr, size_t size)
errno = EDOOFUS;
return (NULL);
}
+ if (ptr && !malloc_started) {
+ wrtwarning("malloc() has never been called\n");
+ ptr = 0;
+ }
+ if (!malloc_started)
+ malloc_init();
+ malloc_counter++;
+ if (malloc_counter_abort && malloc_counter == malloc_counter_abort)
+ wrterror("Aborting as requested\n");
if (ptr == ZEROSIZEPTR)
ptr = NULL;
if (malloc_sysv && !size) {
More information about the freebsd-bugs
mailing list