From owner-freebsd-bugs@FreeBSD.ORG Thu Oct 16 07:50:31 2003 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 562DE16A4B3 for ; Thu, 16 Oct 2003 07:50:31 -0700 (PDT) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 73B6744001 for ; Thu, 16 Oct 2003 07:50:28 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.9/8.12.9) with ESMTP id h9GEoRFY073302 for ; Thu, 16 Oct 2003 07:50:27 -0700 (PDT) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.9/8.12.9/Submit) id h9GEoRJb073301; Thu, 16 Oct 2003 07:50:27 -0700 (PDT) (envelope-from gnats) Date: Thu, 16 Oct 2003 07:50:27 -0700 (PDT) Message-Id: <200310161450.h9GEoRJb073301@freefall.freebsd.org> To: freebsd-bugs@FreeBSD.org From: Dan Nelson Subject: Re: bin/52907: [PATCH] more malloc options for debugging programs X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: Dan Nelson List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 16 Oct 2003 14:50:31 -0000 The following reply was made to PR bin/52907; it has been noted by GNATS. From: Dan Nelson To: freebsd-gnats-submit@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) {