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