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