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