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