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