Home | History | Annotate | Line # | Download | only in libbsdmalloc
malloc.c revision 1.1
      1 /*	$NetBSD: malloc.c,v 1.1 2003/04/21 22:21:06 elric Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1983, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #if defined(LIBC_SCCS) && !defined(lint)
     38 #if 0
     39 static char sccsid[] = "@(#)malloc.c	8.1 (Berkeley) 6/4/93";
     40 #else
     41 __RCSID("$NetBSD: malloc.c,v 1.1 2003/04/21 22:21:06 elric Exp $");
     42 #endif
     43 #endif /* LIBC_SCCS and not lint */
     44 
     45 /*
     46  * malloc.c (Caltech) 2/21/82
     47  * Chris Kingsley, kingsley@cit-20.
     48  *
     49  * This is a very fast storage allocator.  It allocates blocks of a small
     50  * number of different sizes, and keeps free lists of each size.  Blocks that
     51  * don't exactly fit are passed up to the next larger size.  In this
     52  * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
     53  * This is designed for use in a virtual memory environment.
     54  */
     55 
     56 #include <sys/types.h>
     57 #if defined(DEBUG) || defined(RCHECK)
     58 #include <sys/uio.h>
     59 #endif
     60 #if defined(RCHECK) || defined(MSTATS)
     61 #include <stdio.h>
     62 #endif
     63 #include <stdlib.h>
     64 #include <string.h>
     65 #include <unistd.h>
     66 #include "reentrant.h"
     67 
     68 
     69 /*
     70  * The overhead on a block is at least 4 bytes.  When free, this space
     71  * contains a pointer to the next free block, and the bottom two bits must
     72  * be zero.  When in use, the first byte is set to MAGIC, and the second
     73  * byte is the size index.  The remaining bytes are for alignment.
     74  * If range checking is enabled then a second word holds the size of the
     75  * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
     76  * The order of elements is critical: ov_magic must overlay the low order
     77  * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
     78  */
     79 union	overhead {
     80 	union	overhead *ov_next;	/* when free */
     81 	struct {
     82 		u_char	ovu_magic;	/* magic number */
     83 		u_char	ovu_index;	/* bucket # */
     84 #ifdef RCHECK
     85 		u_short	ovu_rmagic;	/* range magic number */
     86 		u_long	ovu_size;	/* actual block size */
     87 #endif
     88 	} ovu;
     89 #define	ov_magic	ovu.ovu_magic
     90 #define	ov_index	ovu.ovu_index
     91 #define	ov_rmagic	ovu.ovu_rmagic
     92 #define	ov_size		ovu.ovu_size
     93 };
     94 
     95 #define	MAGIC		0xef		/* magic # on accounting info */
     96 #ifdef RCHECK
     97 #define RMAGIC		0x5555		/* magic # on range info */
     98 #endif
     99 
    100 #ifdef RCHECK
    101 #define	RSLOP		sizeof (u_short)
    102 #else
    103 #define	RSLOP		0
    104 #endif
    105 
    106 /*
    107  * nextf[i] is the pointer to the next free block of size 2^(i+3).  The
    108  * smallest allocatable block is 8 bytes.  The overhead information
    109  * precedes the data area returned to the user.
    110  */
    111 #define	NBUCKETS 30
    112 static	union overhead *nextf[NBUCKETS];
    113 
    114 static	long pagesz;			/* page size */
    115 static	int pagebucket;			/* page size bucket */
    116 
    117 #ifdef MSTATS
    118 /*
    119  * nmalloc[i] is the difference between the number of mallocs and frees
    120  * for a given block size.
    121  */
    122 static	u_int nmalloc[NBUCKETS];
    123 #endif
    124 
    125 #ifdef _REENT
    126 static	mutex_t malloc_mutex = MUTEX_INITIALIZER;
    127 #endif
    128 
    129 static void morecore __P((int));
    130 static int findbucket __P((union overhead *, int));
    131 #ifdef MSTATS
    132 void mstats __P((const char *));
    133 #endif
    134 
    135 #if defined(DEBUG) || defined(RCHECK)
    136 #define	ASSERT(p)   if (!(p)) botch(__STRING(p))
    137 
    138 static void botch __P((const char *));
    139 
    140 /*
    141  * NOTE: since this may be called while malloc_mutex is locked, stdio must not
    142  *       be used in this function.
    143  */
    144 static void
    145 botch(s)
    146 	const char *s;
    147 {
    148 	struct iovec iov[3];
    149 
    150 	iov[0].iov_base	= "\nassertion botched: ";
    151 	iov[0].iov_len	= 20;
    152 	iov[1].iov_base	= (void *)s;
    153 	iov[1].iov_len	= strlen(s);
    154 	iov[2].iov_base	= "\n";
    155 	iov[2].iov_len	= 1;
    156 
    157 	/*
    158 	 * This place deserves a word of warning: a cancellation point will
    159 	 * occur when executing writev(), and we might be still owning
    160 	 * malloc_mutex.  At this point we need to disable cancellation
    161 	 * until `after' abort() because i) establishing a cancellation handler
    162 	 * might, depending on the implementation, result in another malloc()
    163 	 * to be executed, and ii) it is really not desirable to let execution
    164 	 * continue.  `Fix me.'
    165 	 *
    166 	 * Note that holding mutex_lock during abort() is safe.
    167 	 */
    168 
    169 	(void)writev(STDERR_FILENO, iov, 3);
    170 	abort();
    171 }
    172 #else
    173 #define	ASSERT(p)
    174 #endif
    175 
    176 void *
    177 malloc(nbytes)
    178 	size_t nbytes;
    179 {
    180   	union overhead *op;
    181 	int bucket;
    182   	long n;
    183 	unsigned amt;
    184 
    185 	mutex_lock(&malloc_mutex);
    186 
    187 	/*
    188 	 * First time malloc is called, setup page size and
    189 	 * align break pointer so all data will be page aligned.
    190 	 */
    191 	if (pagesz == 0) {
    192 		pagesz = n = getpagesize();
    193 		ASSERT(pagesz > 0);
    194 		op = (union overhead *)(void *)sbrk(0);
    195   		n = n - sizeof (*op) - ((long)op & (n - 1));
    196 		if (n < 0)
    197 			n += pagesz;
    198 		if (n) {
    199 			if (sbrk((int)n) == (void *)-1) {
    200 				mutex_unlock(&malloc_mutex);
    201 				return (NULL);
    202 			}
    203 		}
    204 		bucket = 0;
    205 		amt = 8;
    206 		while (pagesz > amt) {
    207 			amt <<= 1;
    208 			bucket++;
    209 		}
    210 		pagebucket = bucket;
    211 	}
    212 	/*
    213 	 * Convert amount of memory requested into closest block size
    214 	 * stored in hash buckets which satisfies request.
    215 	 * Account for space used per block for accounting.
    216 	 */
    217 	if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
    218 #ifndef RCHECK
    219 		amt = 8;	/* size of first bucket */
    220 		bucket = 0;
    221 #else
    222 		amt = 16;	/* size of first bucket */
    223 		bucket = 1;
    224 #endif
    225 		n = -((long)sizeof (*op) + RSLOP);
    226 	} else {
    227 		amt = (unsigned)pagesz;
    228 		bucket = pagebucket;
    229 	}
    230 	while (nbytes > amt + n) {
    231 		amt <<= 1;
    232 		if (amt == 0)
    233 			return (NULL);
    234 		bucket++;
    235 	}
    236 	/*
    237 	 * If nothing in hash bucket right now,
    238 	 * request more memory from the system.
    239 	 */
    240   	if ((op = nextf[bucket]) == NULL) {
    241   		morecore(bucket);
    242   		if ((op = nextf[bucket]) == NULL) {
    243 			mutex_unlock(&malloc_mutex);
    244   			return (NULL);
    245 		}
    246 	}
    247 	/* remove from linked list */
    248   	nextf[bucket] = op->ov_next;
    249 	op->ov_magic = MAGIC;
    250 	op->ov_index = bucket;
    251 #ifdef MSTATS
    252   	nmalloc[bucket]++;
    253 #endif
    254 	mutex_unlock(&malloc_mutex);
    255 #ifdef RCHECK
    256 	/*
    257 	 * Record allocated size of block and
    258 	 * bound space with magic numbers.
    259 	 */
    260 	op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
    261 	op->ov_rmagic = RMAGIC;
    262   	*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
    263 #endif
    264   	return ((void *)(op + 1));
    265 }
    266 
    267 /*
    268  * Allocate more memory to the indicated bucket.
    269  */
    270 static void
    271 morecore(bucket)
    272 	int bucket;
    273 {
    274   	union overhead *op;
    275 	long sz;		/* size of desired block */
    276   	long amt;			/* amount to allocate */
    277   	long nblks;			/* how many blocks we get */
    278 
    279 	/*
    280 	 * sbrk_size <= 0 only for big, FLUFFY, requests (about
    281 	 * 2^30 bytes on a VAX, I think) or for a negative arg.
    282 	 */
    283 	sz = 1 << (bucket + 3);
    284 #ifdef DEBUG
    285 	ASSERT(sz > 0);
    286 #else
    287 	if (sz <= 0)
    288 		return;
    289 #endif
    290 	if (sz < pagesz) {
    291 		amt = pagesz;
    292   		nblks = amt / sz;
    293 	} else {
    294 		amt = sz + pagesz;
    295 		nblks = 1;
    296 	}
    297 	op = (union overhead *)(void *)sbrk((int)amt);
    298 	/* no more room! */
    299   	if ((long)op == -1)
    300   		return;
    301 	/*
    302 	 * Add new memory allocated to that on
    303 	 * free list for this hash bucket.
    304 	 */
    305   	nextf[bucket] = op;
    306   	while (--nblks > 0) {
    307 		op->ov_next =
    308 		    (union overhead *)(void *)((caddr_t)(void *)op+(size_t)sz);
    309 		op = op->ov_next;
    310   	}
    311 }
    312 
    313 void
    314 free(cp)
    315 	void *cp;
    316 {
    317 	long size;
    318 	union overhead *op;
    319 
    320   	if (cp == NULL)
    321   		return;
    322 	op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead));
    323 #ifdef DEBUG
    324   	ASSERT(op->ov_magic == MAGIC);		/* make sure it was in use */
    325 #else
    326 	if (op->ov_magic != MAGIC)
    327 		return;				/* sanity */
    328 #endif
    329 #ifdef RCHECK
    330   	ASSERT(op->ov_rmagic == RMAGIC);
    331 	ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
    332 #endif
    333   	size = op->ov_index;
    334   	ASSERT(size < NBUCKETS);
    335 	mutex_lock(&malloc_mutex);
    336 	op->ov_next = nextf[(unsigned int)size];/* also clobbers ov_magic */
    337   	nextf[(unsigned int)size] = op;
    338 #ifdef MSTATS
    339   	nmalloc[(size_t)size]--;
    340 #endif
    341 	mutex_unlock(&malloc_mutex);
    342 }
    343 
    344 /*
    345  * When a program attempts "storage compaction" as mentioned in the
    346  * old malloc man page, it realloc's an already freed block.  Usually
    347  * this is the last block it freed; occasionally it might be farther
    348  * back.  We have to search all the free lists for the block in order
    349  * to determine its bucket: 1st we make one pass thru the lists
    350  * checking only the first block in each; if that fails we search
    351  * ``__realloc_srchlen'' blocks in each list for a match (the variable
    352  * is extern so the caller can modify it).  If that fails we just copy
    353  * however many bytes was given to realloc() and hope it's not huge.
    354  */
    355 int __realloc_srchlen = 4;	/* 4 should be plenty, -1 =>'s whole list */
    356 
    357 void *
    358 realloc(cp, nbytes)
    359 	void *cp;
    360 	size_t nbytes;
    361 {
    362   	u_long onb;
    363 	long i;
    364 	union overhead *op;
    365 	char *res;
    366 	int was_alloced = 0;
    367 
    368   	if (cp == NULL)
    369   		return (malloc(nbytes));
    370 	if (nbytes == 0) {
    371 		free (cp);
    372 		return (NULL);
    373 	}
    374 	op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead));
    375 	mutex_lock(&malloc_mutex);
    376 	if (op->ov_magic == MAGIC) {
    377 		was_alloced++;
    378 		i = op->ov_index;
    379 	} else {
    380 		/*
    381 		 * Already free, doing "compaction".
    382 		 *
    383 		 * Search for the old block of memory on the
    384 		 * free list.  First, check the most common
    385 		 * case (last element free'd), then (this failing)
    386 		 * the last ``__realloc_srchlen'' items free'd.
    387 		 * If all lookups fail, then assume the size of
    388 		 * the memory block being realloc'd is the
    389 		 * largest possible (so that all "nbytes" of new
    390 		 * memory are copied into).  Note that this could cause
    391 		 * a memory fault if the old area was tiny, and the moon
    392 		 * is gibbous.  However, that is very unlikely.
    393 		 */
    394 		if ((i = findbucket(op, 1)) < 0 &&
    395 		    (i = findbucket(op, __realloc_srchlen)) < 0)
    396 			i = NBUCKETS;
    397 	}
    398 	onb = (u_long)1 << (u_long)(i + 3);
    399 	if (onb < pagesz)
    400 		onb -= sizeof (*op) + RSLOP;
    401 	else
    402 		onb += pagesz - sizeof (*op) - RSLOP;
    403 	/* avoid the copy if same size block */
    404 	if (was_alloced) {
    405 		if (i) {
    406 			i = (long)1 << (long)(i + 2);
    407 			if (i < pagesz)
    408 				i -= sizeof (*op) + RSLOP;
    409 			else
    410 				i += pagesz - sizeof (*op) - RSLOP;
    411 		}
    412 		if (nbytes <= onb && nbytes > i) {
    413 #ifdef RCHECK
    414 			op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
    415 			*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
    416 #endif
    417 			mutex_unlock(&malloc_mutex);
    418 			return (cp);
    419 
    420 		}
    421 #ifndef _REENT
    422 		else
    423 			free(cp);
    424 #endif
    425 	}
    426 	mutex_unlock(&malloc_mutex);
    427 	if ((res = malloc(nbytes)) == NULL) {
    428 #ifdef _REENT
    429 		free(cp);
    430 #endif
    431 		return (NULL);
    432 	}
    433 #ifndef _REENT
    434 	if (cp != res)		/* common optimization if "compacting" */
    435 		(void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb));
    436 #else
    437 	(void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb));
    438 	free(cp);
    439 #endif
    440   	return (res);
    441 }
    442 
    443 /*
    444  * Search ``srchlen'' elements of each free list for a block whose
    445  * header starts at ``freep''.  If srchlen is -1 search the whole list.
    446  * Return bucket number, or -1 if not found.
    447  */
    448 static int
    449 findbucket(freep, srchlen)
    450 	union overhead *freep;
    451 	int srchlen;
    452 {
    453 	union overhead *p;
    454 	int i, j;
    455 
    456 	for (i = 0; i < NBUCKETS; i++) {
    457 		j = 0;
    458 		for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
    459 			if (p == freep)
    460 				return (i);
    461 			j++;
    462 		}
    463 	}
    464 	return (-1);
    465 }
    466 
    467 #ifdef MSTATS
    468 /*
    469  * mstats - print out statistics about malloc
    470  *
    471  * Prints two lines of numbers, one showing the length of the free list
    472  * for each size category, the second showing the number of mallocs -
    473  * frees for each size category.
    474  */
    475 void
    476 mstats(s)
    477 	char *s;
    478 {
    479   	int i, j;
    480   	union overhead *p;
    481   	int totfree = 0,
    482   	totused = 0;
    483 
    484   	fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
    485   	for (i = 0; i < NBUCKETS; i++) {
    486   		for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
    487   			;
    488   		fprintf(stderr, " %d", j);
    489   		totfree += j * (1 << (i + 3));
    490   	}
    491   	fprintf(stderr, "\nused:\t");
    492   	for (i = 0; i < NBUCKETS; i++) {
    493   		fprintf(stderr, " %d", nmalloc[i]);
    494   		totused += nmalloc[i] * (1 << (i + 3));
    495   	}
    496   	fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
    497 	    totused, totfree);
    498 }
    499 #endif
    500