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