xref: /netbsd-src/bin/ksh/alloc.c (revision 001c68bd94f75ce9270b69227c4199fbf34ee396)
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