Home | History | Annotate | Line # | Download | only in ksh
alloc.c revision 1.5
      1 /*	$NetBSD: alloc.c,v 1.5 2003/06/23 11:38:51 agc Exp $	*/
      2 
      3 /*
      4  * area-based allocation built on malloc/free
      5  */
      6 #include <sys/cdefs.h>
      7 
      8 #ifndef lint
      9 __RCSID("$NetBSD: alloc.c,v 1.5 2003/06/23 11:38:51 agc Exp $");
     10 #endif
     11 
     12 
     13 #include "sh.h"
     14 
     15 #ifdef TEST_ALLOC
     16 # define shellf	printf
     17 # ifndef DEBUG_ALLOC
     18 #  define DEBUG_ALLOC
     19 # endif /* DEBUG_ALLOC */
     20 #endif /* TEST_ALLOC */
     21 
     22 #ifdef MEM_DEBUG
     23 
     24 /*
     25  * Special versions of alloc routines if doing mem_debug
     26  */
     27 Area *
     28 _chmem_ainit(ap, file, line)
     29 	Area *ap;
     30 	const char *file;
     31 	int line;
     32 {
     33 	ap->freelist = (struct Block *) _chmem_newpool("ainit", (char *) 0, -1,
     34 						file, line);
     35 	if (!ap->freelist)
     36 	    aerror(ap, "ainit failed (ie, newpool)");
     37 	return ap;
     38 }
     39 
     40 /* free all object in Area */
     41 void
     42 _chmem_afreeall(ap, file, line)
     43 	Area *ap;
     44 	const char *file;
     45 	int line;
     46 {
     47 	_chmem_delpool((Chmem_poolp) ap->freelist, 0, file, line);
     48 	/* Kind of ugly, but it works */
     49 	_chmem_ainit(ap, file, line);
     50 }
     51 
     52 /* allocate object from Area */
     53 void *
     54 _chmem_alloc(size, ap, file, line)
     55 	size_t size;
     56 	Area *ap;
     57 	const char *file;
     58 	int line;
     59 {
     60 	return _chmem_mallocp((Chmem_poolp) ap->freelist, size, file, line);
     61 }
     62 
     63 /* change size of object -- like realloc */
     64 void *
     65 _chmem_aresize(ptr, size, ap, file, line)
     66 	void *ptr;
     67 	size_t size;
     68 	Area *ap;
     69 	const char *file;
     70 	int line;
     71 {
     72 	if (!ptr)
     73 		/* Done as realloc(0, size) is not portable */
     74 		return _chmem_mallocp((Chmem_poolp) ap->freelist, size,
     75 					file, line);
     76 	else
     77 		return _chmem_reallocp((Chmem_poolp) ap->freelist, ptr, size,
     78 					file, line);
     79 }
     80 
     81 void
     82 _chmem_afree(ptr, ap, file, line)
     83 	void *ptr;
     84 	Area *ap;
     85 	const char *file;
     86 	int line;
     87 {
     88 	return _chmem_freep((Chmem_poolp) ap->freelist, ptr, file, line);
     89 }
     90 
     91 #else /* MEM_DEBUG */
     92 
     93 # if DEBUG_ALLOC
     94 void acheck ARGS((Area *ap));
     95 #  define ACHECK(ap)	acheck(ap)
     96 # else /* DEBUG_ALLOC */
     97 #  define ACHECK(ap)
     98 # endif /* DEBUG_ALLOC */
     99 
    100 #define	ICELLS	200		/* number of Cells in small Block */
    101 
    102 typedef union Cell Cell;
    103 typedef struct Block Block;
    104 
    105 /*
    106  * The Cells in a Block are organized as a set of objects.
    107  * Each object (pointed to by dp) begins with the block it is in
    108  * (dp-2)->block, then has a size in (dp-1)->size, which is
    109  * followed with "size" data Cells.  Free objects are
    110  * linked together via dp->next.
    111  */
    112 
    113 #define NOBJECT_FIELDS	2	/* the block and size `fields' */
    114 
    115 union Cell {
    116 	size_t	size;
    117 	Cell   *next;
    118 	Block  *block;
    119 	struct {int _;} junk;	/* alignment */
    120 	double djunk;		/* alignment */
    121 };
    122 
    123 struct Block {
    124 	Block  *next;		/* list of Blocks in Area */
    125 	Block  *prev;		/* previous block in list */
    126 	Cell   *freelist;	/* object free list */
    127 	Cell   *last;		/* &b.cell[size] */
    128 	Cell	cell [1];	/* [size] Cells for allocation */
    129 };
    130 
    131 static Block aempty = {&aempty, &aempty, aempty.cell, aempty.cell};
    132 
    133 static void ablockfree ARGS((Block *bp, Area *ap));
    134 static void *asplit ARGS((Area *ap, Block *bp, Cell *fp, Cell *fpp, int cells));
    135 
    136 /* create empty Area */
    137 Area *
    138 ainit(ap)
    139 	register Area *ap;
    140 {
    141 	ap->freelist = &aempty;
    142 	ACHECK(ap);
    143 	return ap;
    144 }
    145 
    146 /* free all object in Area */
    147 void
    148 afreeall(ap)
    149 	register Area *ap;
    150 {
    151 	register Block *bp;
    152 	register Block *tmp;
    153 
    154 	ACHECK(ap);
    155 	bp = ap->freelist;
    156 	if (bp != NULL && bp != &aempty) {
    157 		do {
    158 			tmp = bp;
    159 			bp = bp->next;
    160 			free((void*)tmp);
    161 		} while (bp != ap->freelist);
    162 		ap->freelist = &aempty;
    163 	}
    164 	ACHECK(ap);
    165 }
    166 
    167 /* allocate object from Area */
    168 void *
    169 alloc(size, ap)
    170 	size_t size;
    171 	register Area *ap;
    172 {
    173 	int cells, acells;
    174 	Block *bp = 0;
    175 	Cell *fp = 0, *fpp = 0;
    176 
    177 	ACHECK(ap);
    178 	if (size <= 0)
    179 		aerror(ap, "allocate bad size");
    180 	cells = (unsigned)(size + sizeof(Cell) - 1) / sizeof(Cell);
    181 
    182 	/* allocate at least this many cells */
    183 	acells = cells + NOBJECT_FIELDS;
    184 
    185 	/*
    186 	 * Only attempt to track small objects - let malloc deal
    187 	 * with larger objects. (this way we don't have to deal with
    188 	 * coalescing memory, or with releasing it to the system)
    189 	 */
    190 	if (cells <= ICELLS) {
    191 		/* find free Cell large enough */
    192 		for (bp = ap->freelist; ; bp = bp->next) {
    193 			for (fpp = NULL, fp = bp->freelist;
    194 			     fp != bp->last; fpp = fp, fp = fp->next)
    195 			{
    196 				if ((fp-1)->size >= cells)
    197 					goto Found;
    198 			}
    199 			/* wrapped around Block list, create new Block */
    200 			if (bp->next == ap->freelist) {
    201 				bp = 0;
    202 				break;
    203 			}
    204 		}
    205 		/* Not much free space left?  Allocate a big object this time */
    206 		acells += ICELLS;
    207 	}
    208 	if (bp == 0) {
    209 		bp = (Block*) malloc(offsetof(Block, cell[acells]));
    210 		if (bp == NULL)
    211 			aerror(ap, "cannot allocate");
    212 		if (ap->freelist == &aempty) {
    213 			ap->freelist = bp->next = bp->prev = bp;
    214 		} else {
    215 			bp->next = ap->freelist->next;
    216 			ap->freelist->next->prev = bp;
    217 			ap->freelist->next = bp;
    218 			bp->prev = ap->freelist;
    219 		}
    220 		bp->last = bp->cell + acells;
    221 		/* initial free list */
    222 		fp = bp->freelist = bp->cell + NOBJECT_FIELDS;
    223 		(fp-1)->size = acells - NOBJECT_FIELDS;
    224 		(fp-2)->block = bp;
    225 		fp->next = bp->last;
    226 		fpp = NULL;
    227 	}
    228 
    229   Found:
    230 	return asplit(ap, bp, fp, fpp, cells);
    231 }
    232 
    233 /* Do the work of splitting an object into allocated and (possibly) unallocated
    234  * objects.  Returns the `allocated' object.
    235  */
    236 static void *
    237 asplit(ap, bp, fp, fpp, cells)
    238 	Area *ap;
    239 	Block *bp;
    240 	Cell *fp;
    241 	Cell *fpp;
    242 	int cells;
    243 {
    244 	Cell *dp = fp;	/* allocated object */
    245 	int split = (fp-1)->size - cells;
    246 
    247 	ACHECK(ap);
    248 	if (split < 0)
    249 		aerror(ap, "allocated object too small");
    250 	if (split <= NOBJECT_FIELDS) {	/* allocate all */
    251 		fp = fp->next;
    252 	} else {		/* allocate head, free tail */
    253 		Cell *next = fp->next; /* needed, as cells may be 0 */
    254 		ap->freelist = bp; /* next time, start looking for space here */
    255 		(fp-1)->size = cells;
    256 		fp += cells + NOBJECT_FIELDS;
    257 		(fp-1)->size = split - NOBJECT_FIELDS;
    258 		(fp-2)->block = bp;
    259 		fp->next = next;
    260 	}
    261 	if (fpp == NULL)
    262 		bp->freelist = fp;
    263 	else
    264 		fpp->next = fp;
    265 	ACHECK(ap);
    266 	return (void*) dp;
    267 }
    268 
    269 /* change size of object -- like realloc */
    270 void *
    271 aresize(ptr, size, ap)
    272 	register void *ptr;
    273 	size_t size;
    274 	Area *ap;
    275 {
    276 	int cells;
    277 	Cell *dp = (Cell*) ptr;
    278 	int oldcells = dp ? (dp-1)->size : 0;
    279 
    280 	ACHECK(ap);
    281 	if (size <= 0)
    282 		aerror(ap, "allocate bad size");
    283 	/* New size (in cells) */
    284 	cells = (unsigned)(size - 1) / sizeof(Cell) + 1;
    285 
    286 	/* Is this a large object?  If so, let malloc deal with it
    287 	 * directly (unless we are crossing the ICELLS border, in
    288 	 * which case the alloc/free below handles it - this should
    289 	 * cut down on fragmentation, and will also keep the code
    290 	 * working (as it assumes size < ICELLS means it is not
    291 	 * a `large object').
    292 	 */
    293 	if (oldcells > ICELLS && cells > ICELLS) {
    294 		Block *bp = (dp-2)->block;
    295 		Block *nbp;
    296 		/* Saved in case realloc fails.. */
    297 		Block *next = bp->next, *prev = bp->prev;
    298 
    299 		if (bp->freelist != bp->last)
    300 			aerror(ap, "allocation resizing free pointer");
    301 		nbp = realloc((void *) bp,
    302 			      offsetof(Block, cell[cells + NOBJECT_FIELDS]));
    303 		if (!nbp) {
    304 			/* Have to clean up... */
    305 			/* NOTE: If this code changes, similar changes may be
    306 			 * needed in ablockfree().
    307 			 */
    308 			if (next == bp) /* only block */
    309 				ap->freelist = &aempty;
    310 			else {
    311 				next->prev = prev;
    312 				prev->next = next;
    313 				if (ap->freelist == bp)
    314 					ap->freelist = next;
    315 			}
    316 			aerror(ap, "cannot re-allocate");
    317 		}
    318 		/* If location changed, keep pointers straight... */
    319 		if (nbp != bp) {
    320 			if (next == bp) /* only one block */
    321 				nbp->next = nbp->prev = nbp;
    322 			else {
    323 				next->prev = nbp;
    324 				prev->next = nbp;
    325 			}
    326 			if (ap->freelist == bp)
    327 				ap->freelist = nbp;
    328 			dp = nbp->cell + NOBJECT_FIELDS;
    329 			(dp-2)->block = nbp;
    330 		}
    331 		(dp-1)->size = cells;
    332 		nbp->last = nbp->cell + cells + NOBJECT_FIELDS;
    333 		nbp->freelist = nbp->last;
    334 
    335 		ACHECK(ap);
    336 		return (void*) dp;
    337 	}
    338 
    339 	/* Check if we can just grow this cell
    340 	 * (need to check that cells < ICELLS so we don't make an
    341 	 * object a `large' - that would mess everything up).
    342 	 */
    343 	if (dp && cells > oldcells && cells <= ICELLS) {
    344 		Cell *fp, *fpp;
    345 		Block *bp = (dp-2)->block;
    346 		int need = cells - oldcells - NOBJECT_FIELDS;
    347 
    348 		/* XXX if we had a flag in an object indicating
    349 		 * if the object was free/allocated, we could
    350 		 * avoid this loop (perhaps)
    351 		 */
    352 		for (fpp = NULL, fp = bp->freelist;
    353 		     fp != bp->last
    354 		     && dp + oldcells + NOBJECT_FIELDS <= fp
    355 		     ; fpp = fp, fp = fp->next)
    356 		{
    357 			if (dp + oldcells + NOBJECT_FIELDS == fp
    358 			    && (fp-1)->size >= need)
    359 			{
    360 				Cell *np = asplit(ap, bp, fp, fpp, need);
    361 				/* May get more than we need here */
    362 				(dp-1)->size += (np-1)->size + NOBJECT_FIELDS;
    363 				ACHECK(ap);
    364 				return ptr;
    365 			}
    366 		}
    367 	}
    368 
    369 	/* Check if we can just shrink this cell
    370 	 * (if oldcells > ICELLS, this is a large object and we leave
    371 	 * it to malloc...)
    372 	 * Note: this also handles cells == oldcells (a no-op).
    373 	 */
    374 	if (dp && cells <= oldcells && oldcells <= ICELLS) {
    375 		int split;
    376 
    377 		split = oldcells - cells;
    378 		if (split <= NOBJECT_FIELDS) /* cannot split */
    379 			;
    380 		else {		/* shrink head, free tail */
    381 			Block *bp = (dp-2)->block;
    382 
    383 			(dp-1)->size = cells;
    384 			dp += cells + NOBJECT_FIELDS;
    385 			(dp-1)->size = split - NOBJECT_FIELDS;
    386 			(dp-2)->block = bp;
    387 			afree((void*)dp, ap);
    388 		}
    389 		/* ACHECK() done in afree() */
    390 		return ptr;
    391 	}
    392 
    393 	/* Have to do it the hard way... */
    394 	ptr = alloc(size, ap);
    395 	if (dp != NULL) {
    396 		size_t s = (dp-1)->size * sizeof(Cell);
    397 		if (s > size)
    398 			s = size;
    399 		memcpy(ptr, dp, s);
    400 		afree((void *) dp, ap);
    401 	}
    402 	/* ACHECK() done in alloc()/afree() */
    403 	return ptr;
    404 }
    405 
    406 void
    407 afree(ptr, ap)
    408 	void *ptr;
    409 	register Area *ap;
    410 {
    411 	register Block *bp;
    412 	register Cell *fp, *fpp;
    413 	register Cell *dp = (Cell*)ptr;
    414 
    415 	ACHECK(ap);
    416 	if (ptr == 0)
    417 		aerror(ap, "freeing null pointer");
    418 	bp = (dp-2)->block;
    419 
    420 	/* If this is a large object, just free it up... */
    421 	/* Release object... */
    422 	if ((dp-1)->size > ICELLS) {
    423 		ablockfree(bp, ap);
    424 		ACHECK(ap);
    425 		return;
    426 	}
    427 
    428 	if (dp < &bp->cell[NOBJECT_FIELDS] || dp >= bp->last)
    429 		aerror(ap, "freeing memory outside of block (corrupted?)");
    430 
    431 	/* find position in free list */
    432 	/* XXX if we had prev/next pointers for objects, this loop could go */
    433 	for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fp->next)
    434 		;
    435 
    436 	if (fp == dp)
    437 		aerror(ap, "freeing free object");
    438 
    439 	/* join object with next */
    440 	if (dp + (dp-1)->size == fp-NOBJECT_FIELDS) { /* adjacent */
    441 		(dp-1)->size += (fp-1)->size + NOBJECT_FIELDS;
    442 		dp->next = fp->next;
    443 	} else			/* non-adjacent */
    444 		dp->next = fp;
    445 
    446 	/* join previous with object */
    447 	if (fpp == NULL)
    448 		bp->freelist = dp;
    449 	else if (fpp + (fpp-1)->size == dp-NOBJECT_FIELDS) { /* adjacent */
    450 		(fpp-1)->size += (dp-1)->size + NOBJECT_FIELDS;
    451 		fpp->next = dp->next;
    452 	} else			/* non-adjacent */
    453 		fpp->next = dp;
    454 
    455 	/* If whole block is free (and we have some other blocks
    456 	 * around), release this block back to the system...
    457 	 */
    458 	if (bp->next != bp && bp->freelist == bp->cell + NOBJECT_FIELDS
    459 	    && bp->freelist + (bp->freelist-1)->size == bp->last
    460 	    /* XXX and the other block has some free memory? */
    461 	    )
    462 		ablockfree(bp, ap);
    463 	ACHECK(ap);
    464 }
    465 
    466 static void
    467 ablockfree(bp, ap)
    468 	Block *bp;
    469 	Area *ap;
    470 {
    471 	/* NOTE: If this code changes, similar changes may be
    472 	 * needed in alloc() (where realloc fails).
    473 	 */
    474 
    475 	if (bp->next == bp) /* only block */
    476 		ap->freelist = &aempty;
    477 	else {
    478 		bp->next->prev = bp->prev;
    479 		bp->prev->next = bp->next;
    480 		if (ap->freelist == bp)
    481 			ap->freelist = bp->next;
    482 	}
    483 	free((void*) bp);
    484 }
    485 
    486 # if DEBUG_ALLOC
    487 void
    488 acheck(ap)
    489 	Area *ap;
    490 {
    491 	Block *bp, *bpp;
    492 	Cell *dp, *dptmp, *fp;
    493 	int ok = 1;
    494 	int isfree;
    495 	static int disabled;
    496 
    497 	if (disabled)
    498 		return;
    499 
    500 	if (!ap) {
    501 		disabled = 1;
    502 		aerror(ap, "acheck: null area pointer");
    503 	}
    504 
    505 	bp = ap->freelist;
    506 	if (!bp) {
    507 		disabled = 1;
    508 		aerror(ap, "acheck: null area freelist");
    509 	}
    510 
    511 	/* Nothing to check... */
    512 	if (bp == &aempty)
    513 		return;
    514 
    515 	bpp = ap->freelist->prev;
    516 	while (1) {
    517 		if (bp->prev != bpp) {
    518 			shellf("acheck: bp->prev != previous\n");
    519 			ok = 0;
    520 		}
    521 		fp = bp->freelist;
    522 		for (dp = &bp->cell[NOBJECT_FIELDS]; dp != bp->last; ) {
    523 			if ((dp-2)->block != bp) {
    524 				shellf("acheck: fragment's block is wrong\n");
    525 				ok = 0;
    526 			}
    527 			isfree = dp == fp;
    528 			if ((dp-1)->size == 0 && isfree) {
    529 				shellf("acheck: 0 size frag\n");
    530 				ok = 0;
    531 			}
    532 			if ((dp-1)->size > ICELLS
    533 			    && !isfree
    534 			    && (dp != &bp->cell[NOBJECT_FIELDS]
    535 				|| dp + (dp-1)->size != bp->last))
    536 			{
    537 				shellf("acheck: big cell doesn't make up whole block\n");
    538 				ok = 0;
    539 			}
    540 			if (isfree) {
    541 				if (dp->next <= dp) {
    542 					shellf("acheck: free fragment's next <= self\n");
    543 					ok = 0;
    544 				}
    545 				if (dp->next > bp->last) {
    546 					shellf("acheck: free fragment's next > last\n");
    547 					ok = 0;
    548 				}
    549 				fp = dp->next;
    550 			}
    551 			dptmp = dp + (dp-1)->size;
    552 			if (dptmp > bp->last) {
    553 				shellf("acheck: next frag out of range\n");
    554 				ok = 0;
    555 				break;
    556 			} else if (dptmp != bp->last) {
    557 				dptmp += NOBJECT_FIELDS;
    558 				if (dptmp > bp->last) {
    559 					shellf("acheck: next frag just out of range\n");
    560 					ok = 0;
    561 					break;
    562 				}
    563 			}
    564 			if (isfree && dptmp == fp && dptmp != bp->last) {
    565 				shellf("acheck: adjacent free frags\n");
    566 				ok = 0;
    567 			} else if (dptmp > fp) {
    568 				shellf("acheck: free frag list messed up\n");
    569 				ok = 0;
    570 			}
    571 			dp = dptmp;
    572 		}
    573 		bpp = bp;
    574 		bp = bp->next;
    575 		if (bp == ap->freelist)
    576 			break;
    577 	}
    578 	if (!ok) {
    579 		disabled = 1;
    580 		aerror(ap, "acheck failed");
    581 	}
    582 }
    583 
    584 void
    585 aprint(ap, ptr, size)
    586 	register Area *ap;
    587 	void *ptr;
    588 	size_t size;
    589 {
    590 	Block *bp;
    591 
    592 	if (!ap)
    593 		shellf("aprint: null area pointer\n");
    594 	else if (!(bp = ap->freelist))
    595 		shellf("aprint: null area freelist\n");
    596 	else if (bp == &aempty)
    597 		shellf("aprint: area is empty\n");
    598 	else {
    599 		int i;
    600 		Cell *dp, *fp;
    601 		Block *bpp;
    602 
    603 		bpp = ap->freelist->prev;
    604 		for (i = 0; ; i++) {
    605 			if (ptr) {
    606 				void *eptr = (void *) (((char *) ptr) + size);
    607 				/* print block only if it overlaps ptr/size */
    608 				if (!((ptr >= (void *) bp
    609 				       && ptr <= (void *) bp->last)
    610 				      || (eptr >= (void *) bp
    611 				         && eptr <= (void *) bp->last)))
    612 					continue;
    613 				shellf("aprint: overlap of 0x%p .. 0x%p\n",
    614 					ptr, eptr);
    615 			}
    616 			if (bp->prev != bpp || bp->next->prev != bp)
    617 				shellf(
    618 	"aprint: BAD prev pointer: bp %p, bp->prev %p, bp->next %p, bpp=%p\n",
    619 					bp, bp->prev, bp->next, bpp);
    620 			shellf("aprint: block %2d (p=%p,%p,n=%p): 0x%p .. 0x%p (%ld)\n", i,
    621 				bp->prev, bp, bp->next,
    622 				bp->cell, bp->last,
    623 				(long) ((char *) bp->last - (char *) bp->cell));
    624 			fp = bp->freelist;
    625 			if (bp->last <= bp->cell + NOBJECT_FIELDS)
    626 				shellf(
    627 			"aprint: BAD bp->last too small: %p <= %p\n",
    628 					bp->last, bp->cell + NOBJECT_FIELDS);
    629 			if (bp->freelist < bp->cell + NOBJECT_FIELDS
    630 			    || bp->freelist > bp->last)
    631 				shellf(
    632 			"aprint: BAD bp->freelist %p out of range: %p .. %p\n",
    633 					bp->freelist,
    634 					bp->cell + NOBJECT_FIELDS, bp->last);
    635 			for (dp = bp->cell; dp != bp->last ; ) {
    636 				dp += NOBJECT_FIELDS;
    637 				shellf(
    638 				    "aprint:   0x%p .. 0x%p (%ld) %s\n",
    639 					(dp-NOBJECT_FIELDS),
    640 					(dp-NOBJECT_FIELDS) + (dp-1)->size
    641 						+ NOBJECT_FIELDS,
    642 					(long) ((dp-1)->size + NOBJECT_FIELDS)
    643 						* sizeof(Cell),
    644 					dp == fp ? "free" : "allocated");
    645 				if ((dp-2)->block != bp)
    646 					shellf(
    647 					"aprint: BAD dp->block %p != bp %p\n",
    648 						(dp-2)->block, bp);
    649 				if (dp > bp->last)
    650 					shellf(
    651 				"aprint: BAD dp gone past block: %p > %p\n",
    652 						dp, bp->last);
    653 				if (dp > fp)
    654 					shellf(
    655 				"aprint: BAD dp gone past free: %p > %p\n",
    656 						dp, fp);
    657 				if (dp == fp) {
    658 					fp = fp->next;
    659 					if (fp < dp || fp > bp->last)
    660 						shellf(
    661 			"aprint: BAD free object %p out of range: %p .. %p\n",
    662 							fp,
    663 							dp, bp->last);
    664 				}
    665 				dp += (dp-1)->size;
    666 			}
    667 			bpp = bp;
    668 			bp = bp->next;
    669 			if (bp == ap->freelist)
    670 				break;
    671 		}
    672 	}
    673 }
    674 # endif /* DEBUG_ALLOC */
    675 
    676 # ifdef TEST_ALLOC
    677 
    678 Area a;
    679 FILE *myout;
    680 
    681 int
    682 main(int argc, char **argv)
    683 {
    684 	char buf[1024];
    685 	struct info {
    686 		int size;
    687 		void *value;
    688 	};
    689 	struct info info[1024 * 2];
    690 	int size, ident;
    691 	int lineno = 0;
    692 
    693 	myout = stdout;
    694 	ainit(&a);
    695 	while (fgets(buf, sizeof(buf), stdin)) {
    696 		lineno++;
    697 		if (buf[0] == '\n' || buf[0] == '#')
    698 			continue;
    699 		if (sscanf(buf, " alloc %d = i%d", &size, &ident) == 2) {
    700 			if (ident < 0 || ident > NELEM(info)) {
    701 				fprintf(stderr, "bad ident (%d) on line %d\n",
    702 					ident, lineno);
    703 				exit(1);
    704 			}
    705 			info[ident].value = alloc(info[ident].size = size, &a);
    706 			printf("%p = alloc(%d) [%d,i%d]\n",
    707 				info[ident].value, info[ident].size,
    708 				lineno, ident);
    709 			memset(info[ident].value, 1, size);
    710 			continue;
    711 		}
    712 		if (sscanf(buf, " afree i%d", &ident) == 1) {
    713 			if (ident < 0 || ident > NELEM(info)) {
    714 				fprintf(stderr, "bad ident (%d) on line %d\n",
    715 					ident, lineno);
    716 				exit(1);
    717 			}
    718 			afree(info[ident].value, &a);
    719 			printf("afree(%p) [%d,i%d]\n", info[ident].value,
    720 				lineno, ident);
    721 			continue;
    722 		}
    723 		if (sscanf(buf, " aresize i%d , %d", &ident, &size) == 2) {
    724 			void *value;
    725 			if (ident < 0 || ident > NELEM(info)) {
    726 				fprintf(stderr, "bad ident (%d) on line %d\n",
    727 					ident, lineno);
    728 				exit(1);
    729 			}
    730 			value = info[ident].value;
    731 			info[ident].value = aresize(value,
    732 						    info[ident].size = size,
    733 						    &a);
    734 			printf("%p = aresize(%p, %d) [%d,i%d]\n",
    735 				info[ident].value, value, info[ident].size,
    736 				lineno, ident);
    737 			memset(info[ident].value, 1, size);
    738 			continue;
    739 		}
    740 		if (sscanf(buf, " aprint i%d , %d", &ident, &size) == 2) {
    741 			if (ident < 0 || ident > NELEM(info)) {
    742 				fprintf(stderr, "bad ident (%d) on line %d\n",
    743 					ident, lineno);
    744 				exit(1);
    745 			}
    746 			printf("aprint(%p, %d) [%d,i%d]\n",
    747 				info[ident].value, size, lineno, ident);
    748 			aprint(&a, info[ident].value, size);
    749 			continue;
    750 		}
    751 		if (sscanf(buf, " aprint %d", &ident) == 1) {
    752 			if (ident < 0 || ident > NELEM(info)) {
    753 				fprintf(stderr, "bad ident (%d) on line %d\n",
    754 					ident, lineno);
    755 				exit(1);
    756 			}
    757 			printf("aprint(0, 0) [%d]\n", lineno);
    758 			aprint(&a, 0, 0);
    759 			continue;
    760 		}
    761 		if (sscanf(buf, " afreeall %d", &ident) == 1) {
    762 			printf("afreeall() [%d]\n", lineno);
    763 			afreeall(&a);
    764 			memset(info, 0, sizeof(info));
    765 			continue;
    766 		}
    767 		fprintf(stderr, "unrecognized line (line %d)\n",
    768 			lineno);
    769 		exit(1);
    770 	}
    771 	return 0;
    772 }
    773 
    774 void
    775 aerror(Area *ap, const char *msg)
    776 {
    777 	printf("aerror: %s\n", msg);
    778 	fflush(stdout);
    779 	abort();
    780 }
    781 
    782 # endif /* TEST_ALLOC */
    783 
    784 #endif /* MEM_DEBUG */
    785