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