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