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