1 /* 2 * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 */ 9 10 #include <sm/gen.h> 11 SM_RCSID("@(#)$Id: heap.c,v 1.50 2001/09/11 04:04:48 gshapiro Exp $") 12 13 /* 14 ** debugging memory allocation package 15 ** See heap.html for documentation. 16 */ 17 18 #include <string.h> 19 20 #include <sm/assert.h> 21 #include <sm/debug.h> 22 #include <sm/exc.h> 23 #include <sm/heap.h> 24 #include <sm/io.h> 25 #include <sm/signal.h> 26 #include <sm/xtrap.h> 27 28 /* undef all macro versions of the "functions" so they can be specified here */ 29 #undef sm_malloc 30 #undef sm_malloc_x 31 #undef sm_malloc_tagged 32 #undef sm_malloc_tagged_x 33 #undef sm_free 34 #undef sm_free_tagged 35 #undef sm_realloc 36 #if SM_HEAP_CHECK 37 # undef sm_heap_register 38 # undef sm_heap_checkptr 39 # undef sm_heap_report 40 #endif /* SM_HEAP_CHECK */ 41 42 #if SM_HEAP_CHECK 43 SM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap", 44 "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $"); 45 # define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1) 46 #endif /* SM_HEAP_CHECK */ 47 48 const SM_EXC_TYPE_T SmHeapOutOfMemoryType = 49 { 50 SmExcTypeMagic, 51 "F:sm.heap", 52 "", 53 sm_etype_printf, 54 "out of memory", 55 }; 56 57 SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL); 58 59 60 /* 61 ** The behaviour of malloc with size==0 is platform dependent (it 62 ** says so in the C standard): it can return NULL or non-NULL. We 63 ** don't want sm_malloc_x(0) to raise an exception on some platforms 64 ** but not others, so this case requires special handling. We've got 65 ** two choices: "size = 1" or "return NULL". We use the former in the 66 ** following. 67 ** If we had something like autoconf we could figure out the 68 ** behaviour of the platform and either use this hack or just 69 ** use size. 70 */ 71 72 #define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size)) 73 74 /* 75 ** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error. 76 ** 77 ** Parameters: 78 ** size -- size of requested memory. 79 ** 80 ** Returns: 81 ** Pointer to memory region. 82 ** 83 ** Note: 84 ** sm_malloc_x only gets called from source files in which heap 85 ** debugging is disabled at compile time. Otherwise, a call to 86 ** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x. 87 ** 88 ** Exceptions: 89 ** F:sm_heap -- out of memory 90 */ 91 92 void * 93 sm_malloc_x(size) 94 size_t size; 95 { 96 void *ptr; 97 98 ENTER_CRITICAL(); 99 ptr = malloc(MALLOC_SIZE(size)); 100 LEAVE_CRITICAL(); 101 if (ptr == NULL) 102 sm_exc_raise_x(&SmHeapOutOfMemory); 103 return ptr; 104 } 105 106 #if !SM_HEAP_CHECK 107 108 /* 109 ** SM_MALLOC -- wrapper around malloc() 110 ** 111 ** Parameters: 112 ** size -- size of requested memory. 113 ** 114 ** Returns: 115 ** Pointer to memory region. 116 */ 117 118 void * 119 sm_malloc(size) 120 size_t size; 121 { 122 void *ptr; 123 124 ENTER_CRITICAL(); 125 ptr = malloc(MALLOC_SIZE(size)); 126 LEAVE_CRITICAL(); 127 return ptr; 128 } 129 130 /* 131 ** SM_REALLOC -- wrapper for realloc() 132 ** 133 ** Parameters: 134 ** ptr -- pointer to old memory area. 135 ** size -- size of requested memory. 136 ** 137 ** Returns: 138 ** Pointer to new memory area, NULL on failure. 139 */ 140 141 void * 142 sm_realloc(ptr, size) 143 void *ptr; 144 size_t size; 145 { 146 void *newptr; 147 148 ENTER_CRITICAL(); 149 newptr = realloc(ptr, MALLOC_SIZE(size)); 150 LEAVE_CRITICAL(); 151 return newptr; 152 } 153 154 /* 155 ** SM_REALLOC_X -- wrapper for realloc() 156 ** 157 ** Parameters: 158 ** ptr -- pointer to old memory area. 159 ** size -- size of requested memory. 160 ** 161 ** Returns: 162 ** Pointer to new memory area. 163 ** 164 ** Exceptions: 165 ** F:sm_heap -- out of memory 166 */ 167 168 void * 169 sm_realloc_x(ptr, size) 170 void *ptr; 171 size_t size; 172 { 173 void *newptr; 174 175 ENTER_CRITICAL(); 176 newptr = realloc(ptr, MALLOC_SIZE(size)); 177 LEAVE_CRITICAL(); 178 if (newptr == NULL) 179 sm_exc_raise_x(&SmHeapOutOfMemory); 180 return newptr; 181 } 182 /* 183 ** SM_FREE -- wrapper around free() 184 ** 185 ** Parameters: 186 ** ptr -- pointer to memory region. 187 ** 188 ** Returns: 189 ** none. 190 */ 191 192 void 193 sm_free(ptr) 194 void *ptr; 195 { 196 if (ptr == NULL) 197 return; 198 ENTER_CRITICAL(); 199 free(ptr); 200 LEAVE_CRITICAL(); 201 return; 202 } 203 204 #else /* !SM_HEAP_CHECK */ 205 206 /* 207 ** Each allocated block is assigned a "group number". 208 ** By default, all blocks are assigned to group #1. 209 ** By convention, group #0 is for memory that is never freed. 210 ** You can use group numbers any way you want, in order to help make 211 ** sense of sm_heap_report output. 212 */ 213 214 int SmHeapGroup = 1; 215 int SmHeapMaxGroup = 1; 216 217 /* 218 ** Total number of bytes allocated. 219 ** This is only maintained if the sm_check_heap debug category is active. 220 */ 221 222 size_t SmHeapTotal = 0; 223 224 /* 225 ** High water mark: the most that SmHeapTotal has ever been. 226 */ 227 228 size_t SmHeapMaxTotal = 0; 229 230 /* 231 ** Maximum number of bytes that may be allocated at any one time. 232 ** 0 means no limit. 233 ** This is only honoured if sm_check_heap is active. 234 */ 235 236 SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit", 237 "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $"); 238 239 /* 240 ** This is the data structure that keeps track of all currently 241 ** allocated blocks of memory known to the heap package. 242 */ 243 244 typedef struct sm_heap_item SM_HEAP_ITEM_T; 245 struct sm_heap_item 246 { 247 void *hi_ptr; 248 size_t hi_size; 249 char *hi_tag; 250 int hi_num; 251 int hi_group; 252 SM_HEAP_ITEM_T *hi_next; 253 }; 254 255 #define SM_HEAP_TABLE_SIZE 256 256 static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE]; 257 258 /* 259 ** This is a randomly generated table 260 ** which contains exactly one occurrence 261 ** of each of the numbers between 0 and 255. 262 ** It is used by ptrhash. 263 */ 264 265 static unsigned char hashtab[SM_HEAP_TABLE_SIZE] = 266 { 267 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86, 268 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179, 269 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73, 270 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193, 271 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91, 272 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106, 273 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190, 274 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142, 275 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67, 276 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17, 277 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173, 278 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170, 279 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53, 280 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159, 281 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132, 282 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228 283 }; 284 285 /* 286 ** PTRHASH -- hash a pointer value 287 ** 288 ** Parameters: 289 ** p -- pointer. 290 ** 291 ** Returns: 292 ** hash value. 293 ** 294 ** ptrhash hashes a pointer value to a uniformly distributed random 295 ** number between 0 and 255. 296 ** 297 ** This hash algorithm is based on Peter K. Pearson, 298 ** "Fast Hashing of Variable-Length Text Strings", 299 ** in Communications of the ACM, June 1990, vol 33 no 6. 300 */ 301 302 static int 303 ptrhash(p) 304 void *p; 305 { 306 int h; 307 308 if (sizeof(void*) == 4 && sizeof(unsigned long) == 4) 309 { 310 unsigned long n = (unsigned long)p; 311 312 h = hashtab[n & 0xFF]; 313 h = hashtab[h ^ ((n >> 8) & 0xFF)]; 314 h = hashtab[h ^ ((n >> 16) & 0xFF)]; 315 h = hashtab[h ^ ((n >> 24) & 0xFF)]; 316 } 317 # if 0 318 else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8) 319 { 320 unsigned long n = (unsigned long)p; 321 322 h = hashtab[n & 0xFF]; 323 h = hashtab[h ^ ((n >> 8) & 0xFF)]; 324 h = hashtab[h ^ ((n >> 16) & 0xFF)]; 325 h = hashtab[h ^ ((n >> 24) & 0xFF)]; 326 h = hashtab[h ^ ((n >> 32) & 0xFF)]; 327 h = hashtab[h ^ ((n >> 40) & 0xFF)]; 328 h = hashtab[h ^ ((n >> 48) & 0xFF)]; 329 h = hashtab[h ^ ((n >> 56) & 0xFF)]; 330 } 331 # endif /* 0 */ 332 else 333 { 334 unsigned char *cp = (unsigned char *)&p; 335 int i; 336 337 h = 0; 338 for (i = 0; i < sizeof(void*); ++i) 339 h = hashtab[h ^ cp[i]]; 340 } 341 return h; 342 } 343 344 /* 345 ** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version. 346 ** 347 ** Parameters: 348 ** size -- size of requested memory. 349 ** tag -- tag for debugging. 350 ** num -- additional value for debugging. 351 ** group -- heap group for debugging. 352 ** 353 ** Returns: 354 ** Pointer to memory region. 355 */ 356 357 void * 358 sm_malloc_tagged(size, tag, num, group) 359 size_t size; 360 char *tag; 361 int num; 362 int group; 363 { 364 void *ptr; 365 366 if (!HEAP_CHECK) 367 { 368 ENTER_CRITICAL(); 369 ptr = malloc(MALLOC_SIZE(size)); 370 LEAVE_CRITICAL(); 371 return ptr; 372 } 373 374 if (sm_xtrap_check()) 375 return NULL; 376 if (sm_debug_active(&SmHeapLimit, 1) 377 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) 378 return NULL; 379 ENTER_CRITICAL(); 380 ptr = malloc(MALLOC_SIZE(size)); 381 LEAVE_CRITICAL(); 382 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) 383 { 384 ENTER_CRITICAL(); 385 free(ptr); 386 LEAVE_CRITICAL(); 387 ptr = NULL; 388 } 389 SmHeapTotal += size; 390 if (SmHeapTotal > SmHeapMaxTotal) 391 SmHeapMaxTotal = SmHeapTotal; 392 return ptr; 393 } 394 395 /* 396 ** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version. 397 ** 398 ** Parameters: 399 ** size -- size of requested memory. 400 ** tag -- tag for debugging. 401 ** num -- additional value for debugging. 402 ** group -- heap group for debugging. 403 ** 404 ** Returns: 405 ** Pointer to memory region. 406 ** 407 ** Exceptions: 408 ** F:sm_heap -- out of memory 409 */ 410 411 void * 412 sm_malloc_tagged_x(size, tag, num, group) 413 size_t size; 414 char *tag; 415 int num; 416 int group; 417 { 418 void *ptr; 419 420 if (!HEAP_CHECK) 421 { 422 ENTER_CRITICAL(); 423 ptr = malloc(MALLOC_SIZE(size)); 424 LEAVE_CRITICAL(); 425 if (ptr == NULL) 426 sm_exc_raise_x(&SmHeapOutOfMemory); 427 return ptr; 428 } 429 430 sm_xtrap_raise_x(&SmHeapOutOfMemory); 431 if (sm_debug_active(&SmHeapLimit, 1) 432 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) 433 { 434 sm_exc_raise_x(&SmHeapOutOfMemory); 435 } 436 ENTER_CRITICAL(); 437 ptr = malloc(MALLOC_SIZE(size)); 438 LEAVE_CRITICAL(); 439 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) 440 { 441 ENTER_CRITICAL(); 442 free(ptr); 443 LEAVE_CRITICAL(); 444 ptr = NULL; 445 } 446 if (ptr == NULL) 447 sm_exc_raise_x(&SmHeapOutOfMemory); 448 SmHeapTotal += size; 449 if (SmHeapTotal > SmHeapMaxTotal) 450 SmHeapMaxTotal = SmHeapTotal; 451 return ptr; 452 } 453 454 /* 455 ** SM_HEAP_REGISTER -- register a pointer into the heap for debugging. 456 ** 457 ** Parameters: 458 ** ptr -- pointer to register. 459 ** size -- size of requested memory. 460 ** tag -- tag for debugging. 461 ** num -- additional value for debugging. 462 ** group -- heap group for debugging. 463 ** 464 ** Returns: 465 ** true iff successfully registered (not yet in table). 466 */ 467 468 bool 469 sm_heap_register(ptr, size, tag, num, group) 470 void *ptr; 471 size_t size; 472 char *tag; 473 int num; 474 int group; 475 { 476 int i; 477 SM_HEAP_ITEM_T *hi; 478 479 if (!HEAP_CHECK) 480 return true; 481 SM_REQUIRE(ptr != NULL); 482 i = ptrhash(ptr); 483 # if SM_CHECK_REQUIRE 484 485 /* 486 ** We require that ptr is not already in SmHeapTable. 487 */ 488 489 for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next) 490 { 491 if (hi->hi_ptr == ptr) 492 sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)", 493 ptr, hi->hi_tag, hi->hi_num); 494 } 495 # endif /* SM_CHECK_REQUIRE */ 496 ENTER_CRITICAL(); 497 hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T)); 498 LEAVE_CRITICAL(); 499 if (hi == NULL) 500 return false; 501 hi->hi_ptr = ptr; 502 hi->hi_size = size; 503 hi->hi_tag = tag; 504 hi->hi_num = num; 505 hi->hi_group = group; 506 hi->hi_next = SmHeapTable[i]; 507 SmHeapTable[i] = hi; 508 return true; 509 } 510 /* 511 ** SM_REALLOC -- wrapper for realloc(), debugging version. 512 ** 513 ** Parameters: 514 ** ptr -- pointer to old memory area. 515 ** size -- size of requested memory. 516 ** 517 ** Returns: 518 ** Pointer to new memory area, NULL on failure. 519 */ 520 521 void * 522 sm_realloc(ptr, size) 523 void *ptr; 524 size_t size; 525 { 526 void *newptr; 527 SM_HEAP_ITEM_T *hi, **hp; 528 529 if (!HEAP_CHECK) 530 { 531 ENTER_CRITICAL(); 532 newptr = realloc(ptr, MALLOC_SIZE(size)); 533 LEAVE_CRITICAL(); 534 return newptr; 535 } 536 537 if (ptr == NULL) 538 return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup); 539 540 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) 541 { 542 if ((**hp).hi_ptr == ptr) 543 { 544 if (sm_xtrap_check()) 545 return NULL; 546 hi = *hp; 547 if (sm_debug_active(&SmHeapLimit, 1) 548 && sm_debug_level(&SmHeapLimit) 549 < SmHeapTotal - hi->hi_size + size) 550 { 551 return NULL; 552 } 553 ENTER_CRITICAL(); 554 newptr = realloc(ptr, MALLOC_SIZE(size)); 555 LEAVE_CRITICAL(); 556 if (newptr == NULL) 557 return NULL; 558 SmHeapTotal = SmHeapTotal - hi->hi_size + size; 559 if (SmHeapTotal > SmHeapMaxTotal) 560 SmHeapMaxTotal = SmHeapTotal; 561 *hp = hi->hi_next; 562 hi->hi_ptr = newptr; 563 hi->hi_size = size; 564 hp = &SmHeapTable[ptrhash(newptr)]; 565 hi->hi_next = *hp; 566 *hp = hi; 567 return newptr; 568 } 569 } 570 sm_abort("sm_realloc: bad argument (%p)", ptr); 571 /* NOTREACHED */ 572 return NULL; /* keep Irix compiler happy */ 573 } 574 575 /* 576 ** SM_REALLOC_X -- wrapper for realloc(), debugging version. 577 ** 578 ** Parameters: 579 ** ptr -- pointer to old memory area. 580 ** size -- size of requested memory. 581 ** 582 ** Returns: 583 ** Pointer to new memory area. 584 ** 585 ** Exceptions: 586 ** F:sm_heap -- out of memory 587 */ 588 589 void * 590 sm_realloc_x(ptr, size) 591 void *ptr; 592 size_t size; 593 { 594 void *newptr; 595 SM_HEAP_ITEM_T *hi, **hp; 596 597 if (!HEAP_CHECK) 598 { 599 ENTER_CRITICAL(); 600 newptr = realloc(ptr, MALLOC_SIZE(size)); 601 LEAVE_CRITICAL(); 602 if (newptr == NULL) 603 sm_exc_raise_x(&SmHeapOutOfMemory); 604 return newptr; 605 } 606 607 if (ptr == NULL) 608 return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup); 609 610 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) 611 { 612 if ((**hp).hi_ptr == ptr) 613 { 614 sm_xtrap_raise_x(&SmHeapOutOfMemory); 615 hi = *hp; 616 if (sm_debug_active(&SmHeapLimit, 1) 617 && sm_debug_level(&SmHeapLimit) 618 < SmHeapTotal - hi->hi_size + size) 619 { 620 sm_exc_raise_x(&SmHeapOutOfMemory); 621 } 622 ENTER_CRITICAL(); 623 newptr = realloc(ptr, MALLOC_SIZE(size)); 624 LEAVE_CRITICAL(); 625 if (newptr == NULL) 626 sm_exc_raise_x(&SmHeapOutOfMemory); 627 SmHeapTotal = SmHeapTotal - hi->hi_size + size; 628 if (SmHeapTotal > SmHeapMaxTotal) 629 SmHeapMaxTotal = SmHeapTotal; 630 *hp = hi->hi_next; 631 hi->hi_ptr = newptr; 632 hi->hi_size = size; 633 hp = &SmHeapTable[ptrhash(newptr)]; 634 hi->hi_next = *hp; 635 *hp = hi; 636 return newptr; 637 } 638 } 639 sm_abort("sm_realloc_x: bad argument (%p)", ptr); 640 /* NOTREACHED */ 641 return NULL; /* keep Irix compiler happy */ 642 } 643 644 /* 645 ** SM_FREE_TAGGED -- wrapper around free(), debugging version. 646 ** 647 ** Parameters: 648 ** ptr -- pointer to memory region. 649 ** tag -- tag for debugging. 650 ** num -- additional value for debugging. 651 ** 652 ** Returns: 653 ** none. 654 */ 655 656 void 657 sm_free_tagged(ptr, tag, num) 658 void *ptr; 659 char *tag; 660 int num; 661 { 662 SM_HEAP_ITEM_T **hp; 663 664 if (ptr == NULL) 665 return; 666 if (!HEAP_CHECK) 667 { 668 ENTER_CRITICAL(); 669 free(ptr); 670 LEAVE_CRITICAL(); 671 return; 672 } 673 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) 674 { 675 if ((**hp).hi_ptr == ptr) 676 { 677 SM_HEAP_ITEM_T *hi = *hp; 678 679 *hp = hi->hi_next; 680 681 /* 682 ** Fill the block with zeros before freeing. 683 ** This is intended to catch problems with 684 ** dangling pointers. The block is filled with 685 ** zeros, not with some non-zero value, because 686 ** it is common practice in some C code to store 687 ** a zero in a structure member before freeing the 688 ** structure, as a defense against dangling pointers. 689 */ 690 691 (void) memset(ptr, 0, hi->hi_size); 692 SmHeapTotal -= hi->hi_size; 693 ENTER_CRITICAL(); 694 free(ptr); 695 free(hi); 696 LEAVE_CRITICAL(); 697 return; 698 } 699 } 700 sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num); 701 } 702 703 /* 704 ** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free 705 ** 706 ** Parameters: 707 ** ptr -- pointer to memory region. 708 ** tag -- tag for debugging. 709 ** num -- additional value for debugging. 710 ** 711 ** Returns: 712 ** none. 713 ** 714 ** Side Effects: 715 ** aborts if check fails. 716 */ 717 718 void 719 sm_heap_checkptr_tagged(ptr, tag, num) 720 void *ptr; 721 char *tag; 722 int num; 723 { 724 SM_HEAP_ITEM_T *hp; 725 726 if (!HEAP_CHECK) 727 return; 728 if (ptr == NULL) 729 return; 730 for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next) 731 { 732 if (hp->hi_ptr == ptr) 733 return; 734 } 735 sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num); 736 } 737 738 /* 739 ** SM_HEAP_REPORT -- output "map" of used heap. 740 ** 741 ** Parameters: 742 ** stream -- the file pointer to write to. 743 ** verbosity -- how much info? 744 ** 745 ** Returns: 746 ** none. 747 */ 748 749 void 750 sm_heap_report(stream, verbosity) 751 SM_FILE_T *stream; 752 int verbosity; 753 { 754 int i; 755 unsigned long group0total, group1total, otherstotal, grandtotal; 756 757 if (!HEAP_CHECK || verbosity <= 0) 758 return; 759 group0total = group1total = otherstotal = grandtotal = 0; 760 for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i) 761 { 762 SM_HEAP_ITEM_T *hi = SmHeapTable[i]; 763 764 while (hi != NULL) 765 { 766 if (verbosity > 2 767 || (verbosity > 1 && hi->hi_group != 0)) 768 { 769 sm_io_fprintf(stream, SM_TIME_DEFAULT, 770 "%4d %*lx %7lu bytes", 771 hi->hi_group, 772 (int) sizeof(void *) * 2, 773 (long)hi->hi_ptr, 774 (unsigned long)hi->hi_size); 775 if (hi->hi_tag != NULL) 776 { 777 sm_io_fprintf(stream, SM_TIME_DEFAULT, 778 " %s", 779 hi->hi_tag); 780 if (hi->hi_num) 781 { 782 sm_io_fprintf(stream, 783 SM_TIME_DEFAULT, 784 ":%d", 785 hi->hi_num); 786 } 787 } 788 sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n"); 789 } 790 switch (hi->hi_group) 791 { 792 case 0: 793 group0total += hi->hi_size; 794 break; 795 case 1: 796 group1total += hi->hi_size; 797 break; 798 default: 799 otherstotal += hi->hi_size; 800 break; 801 } 802 grandtotal += hi->hi_size; 803 hi = hi->hi_next; 804 } 805 } 806 sm_io_fprintf(stream, SM_TIME_DEFAULT, 807 "heap max=%lu, total=%lu, ", 808 (unsigned long) SmHeapMaxTotal, grandtotal); 809 sm_io_fprintf(stream, SM_TIME_DEFAULT, 810 "group 0=%lu, group 1=%lu, others=%lu\n", 811 group0total, group1total, otherstotal); 812 if (grandtotal != SmHeapTotal) 813 { 814 sm_io_fprintf(stream, SM_TIME_DEFAULT, 815 "BUG => SmHeapTotal: got %lu, expected %lu\n", 816 (unsigned long) SmHeapTotal, grandtotal); 817 } 818 } 819 #endif /* !SM_HEAP_CHECK */ 820