1#if MEM_MAN 2/* a simple memory manager. All allocates and frees should go through here */ 3 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <errno.h> 8 9#define MEM_MAN_MAIN 10 11#include "mem_man.h" 12 13#ifdef MEM_SIGNAL_HANDLER 14#include <signal.h> 15#endif 16 17/* 18 this module is stand alone. typically a define will occur in a C file 19 like this 20 21 #define malloc(x) smb_mem_malloc(x,__FILE__,__LINE__) 22 #define free(x) smb_mem_free(x,__FILE__,__LINE__) 23 24 which redirects all calls to malloc and free through this module 25 26 Various configuration options can be set in mem_man.h. This file also 27 includes the defines above - so the complete system can be implemented 28 with just one include call. 29 30 31 */ 32 33extern FILE *dbf; 34 35/* 36 ACCESSING the memory manager : 37 38 mem_init_memory_manager() : 39 initialises internal data structures of memory manager 40 41 void *malloc(size_t size) : 42 allocates memory as per usual. also records lots of info 43 44 int free(void *ptr) : 45 frees some memory as per usual. writes errors if necessary. 46 47 void *smb_mem_resize(void *ptr,size_t newsize) : 48 changes the memory assignment size of a pointer. note it may return a 49 different pointer than the one given. memory can be sized up or down. 50 51 int smb_mem_query_size(void *ptr) : 52 returns the size of the allocated memory. 53 54 int smb_mem_query_real_size(void *ptr) : 55 returns the actual amount of memory allocated to a pointer. 56 57 char *smb_mem_query_file(void *ptr) : 58 returns the name of the file where the pointer was allocated. 59 60 int smb_mem_query_line(void *ptr) : 61 returns the line of the file where the memory was allocated. 62 63 void smb_mem_write_status(FILE *outfile) : 64 writes short summary of memory stats on the stream. 65 66 void smb_mem_write_verbose(FILE *outfile) : 67 writes lots of info on current allocations to stream. 68 69 void smb_mem_write_errors(FILE *outfile) : 70 writes info on error blocks 71 72 void smb_mem_write_info(void *ptr,FILE *outfile) 73 writes info on one pointer to outfile 74 75 int smb_mem_test(void *ptr) : 76 returns true if the pointer is OK - false if it is not. 77 78 void smb_mem_set_multiplier(int multiplier) : 79 sets defaults amount of memory allocated to multiplier times 80 amount requested. 81 82 int smb_mem_total_errors(void) : 83 returns the total number of error blocks 84 85 void smb_mem_check_buffers(void) : 86 checks all buffers for corruption. It marks them as corrupt if they are. 87 88 kill -USR1 <pid> : 89 this will send a signal to the memory manager to do a mem_write_verbose 90 it also checks them for corruption. Note that the signal number can be 91 set in the header file mem_man.h. This can also be turned off. 92 93 */ 94 95 96void smb_mem_write_errors(FILE *outfile); 97void smb_mem_write_verbose(FILE *outfile); 98void smb_mem_write_status(FILE *outfile); 99static void mem_check_buffers(void); 100 101 102#define FREE_FAILURE 0 103#define FREE_SUCCESS 1 104#define FN 105#define True (0==0) 106#define False (!True) 107#define BUF_SIZE (MEM_CORRUPT_BUFFER * sizeof(char) * 2) 108#define BUF_OFFSET (BUF_SIZE/2) 109 110typedef struct 111{ 112 void *pointer; 113 size_t present_size; 114 size_t allocated_size; 115 unsigned char status; 116 short error_number; 117 char file[MEM_FILE_STR_LENGTH]; 118 unsigned short line; 119} memory_struct; 120 121/* the order of this enum is important. everything greater than 122 S_ALLOCATED is considered an error */ 123enum status_types {S_UNALLOCATED,S_ALLOCATED, 124 S_ERROR_UNALLOCATED,S_ERROR_FREEING, 125 S_CORRUPT_FRONT,S_CORRUPT_BACK,S_CORRUPT_FRONT_BACK}; 126 127/* here is the data memory */ 128 129static memory_struct *memory_blocks=NULL; /* these hold the allocation data */ 130static int mem_blocks_allocated=0; /* how many mem blocks are allocated */ 131static int mem_multiplier; /* this is the current multiplier mor over allocation */ 132static int mem_manager_initialised=False; /* has it been initialised ? */ 133static int last_block_allocated=0; /* a speed up method - this will contain the 134 index of the last block allocated or freed 135 to cut down searching time for a new block */ 136 137 138typedef struct 139{ 140 int status; 141 char *label; 142} stat_str_type; 143 144static stat_str_type stat_str_struct[] = 145{ 146{S_UNALLOCATED,"S_UNALLOCATED"}, 147{S_ALLOCATED,"S_ALLOCATED"}, 148{S_ERROR_UNALLOCATED,"S_ERROR_UNALLOCATED"}, 149{S_ERROR_FREEING,"S_ERROR_FREEING"}, 150{S_CORRUPT_FRONT,"S_CORRUPT_FRONT"}, 151{S_CORRUPT_BACK,"S_CORRUPT_BACK"}, 152{S_CORRUPT_FRONT_BACK,"S_CORRUPT_FRONT_BACK"}, 153{-1,NULL} 154}; 155 156 157#define INIT_MANAGER() if (!mem_manager_initialised) mem_init_memory_manager() 158 159/******************************************************************* 160 returns a pointer to a static string for each status 161 ********************************************************************/ 162static char *status_to_str(int status) 163{ 164 int i=0; 165 while (stat_str_struct[i].label != NULL) 166 { 167 if (stat_str_struct[i].status == status) 168 return(stat_str_struct[i].label); 169 i++; 170 } 171 return(NULL); 172} 173 174 175 176#ifdef MEM_SIGNAL_HANDLER 177/******************************************************************* 178 this handles signals - causes a mem_write_verbose on stderr 179 ********************************************************************/ 180static void mem_signal_handler() 181{ 182 mem_check_buffers(); 183 smb_mem_write_verbose(dbf); 184 signal(MEM_SIGNAL_VECTOR,mem_signal_handler); 185} 186#endif 187 188#ifdef MEM_SIGNAL_HANDLER 189/******************************************************************* 190 this handles error signals - causes a mem_write_verbose on stderr 191 ********************************************************************/ 192static void error_signal_handler() 193{ 194 fprintf(dbf,"Received error signal!\n"); 195 mem_check_buffers(); 196 smb_mem_write_status(dbf); 197 smb_mem_write_errors(dbf); 198 abort(); 199} 200#endif 201 202 203/******************************************************************* 204 initialise memory manager data structures 205 ********************************************************************/ 206static void mem_init_memory_manager(void) 207{ 208 int i; 209 /* allocate the memory_blocks array */ 210 mem_blocks_allocated = MEM_MAX_MEM_OBJECTS; 211 212 while (mem_blocks_allocated > 0) 213 { 214 memory_blocks = (memory_struct *) 215 calloc(mem_blocks_allocated,sizeof(memory_struct)); 216 if (memory_blocks != NULL) break; 217 mem_blocks_allocated /= 2; 218 } 219 220 if (memory_blocks == NULL) 221 { 222 fprintf(dbf,"Panic ! can't allocate mem manager blocks!\n"); 223 abort(); 224 } 225 226 /* just loop setting status flag to unallocated */ 227 for (i=0;i<mem_blocks_allocated;i++) 228 memory_blocks[i].status = S_UNALLOCATED; 229 230 /* also set default mem multiplier */ 231 mem_multiplier = MEM_DEFAULT_MEM_MULTIPLIER; 232 mem_manager_initialised=True; 233 234#ifdef MEM_SIGNAL_HANDLER 235 signal(MEM_SIGNAL_VECTOR,mem_signal_handler); 236 signal(SIGSEGV,error_signal_handler); 237 signal(SIGBUS,error_signal_handler); 238#endif 239 240} 241 242 243/******************************************************************* 244 finds first available slot in memory blocks 245 ********************************************************************/ 246static int mem_first_avail_slot(void) 247{ 248 int i; 249 for (i=last_block_allocated;i<mem_blocks_allocated;i++) 250 if (memory_blocks[i].status == S_UNALLOCATED) 251 return(last_block_allocated=i); 252 for (i=0;i<last_block_allocated;i++) 253 if (memory_blocks[i].status == S_UNALLOCATED) 254 return(last_block_allocated=i); 255 return(-1); 256} 257 258 259/******************************************************************* 260 find which Index a pointer refers to 261 ********************************************************************/ 262static int mem_find_Index(void *ptr) 263{ 264 int i; 265 int start = last_block_allocated+mem_blocks_allocated/50; 266 if (start > mem_blocks_allocated-1) start = mem_blocks_allocated-1; 267 for (i=start;i>=0;i--) 268 if ((memory_blocks[i].status == S_ALLOCATED) && 269 (memory_blocks[i].pointer == ptr)) 270 return(i); 271 for (i=(start+1);i<mem_blocks_allocated;i++) 272 if ((memory_blocks[i].status == S_ALLOCATED) && 273 (memory_blocks[i].pointer == ptr)) 274 return(i); 275 /* it's not there! */ 276 return(-1); 277} 278 279/******************************************************************* 280 fill the buffer areas of a mem block 281 ********************************************************************/ 282static void mem_fill_bytes(void *p,int size,int Index) 283{ 284 memset(p,Index%256,size); 285} 286 287/******************************************************************* 288 fill the buffer areas of a mem block 289 ********************************************************************/ 290static void mem_fill_buffer(int Index) 291{ 292 char *iptr,*tailptr; 293 int i; 294 int seed; 295 296 /* fill the front and back ends */ 297 seed = MEM_CORRUPT_SEED; 298 iptr = (char *)((char *)memory_blocks[Index].pointer - BUF_OFFSET); 299 tailptr = (char *)((char *)memory_blocks[Index].pointer + 300 memory_blocks[Index].present_size); 301 302 for (i=0;i<MEM_CORRUPT_BUFFER;i++) 303 { 304 iptr[i] = seed; 305 tailptr[i] = seed; 306 seed += MEM_SEED_INCREMENT; 307 } 308} 309 310/******************************************************************* 311 check if a mem block is corrupt 312 ********************************************************************/ 313static int mem_buffer_ok(int Index) 314{ 315 char *iptr; 316 int i; 317 int corrupt_front = False; 318 int corrupt_back = False; 319 320 /* check the front end */ 321 iptr = (char *)((char *)memory_blocks[Index].pointer - BUF_OFFSET); 322 for (i=0;i<MEM_CORRUPT_BUFFER;i++) 323 if (iptr[i] != (char)(MEM_CORRUPT_SEED + i*MEM_SEED_INCREMENT)) 324 corrupt_front = True; 325 326 /* now check the tail end */ 327 iptr = (char *)((char *)memory_blocks[Index].pointer + 328 memory_blocks[Index].present_size); 329 for (i=0;i<MEM_CORRUPT_BUFFER;i++) 330 if (iptr[i] != (char)(MEM_CORRUPT_SEED + i*MEM_SEED_INCREMENT)) 331 corrupt_back = True; 332 333 if (corrupt_front && !corrupt_back) 334 memory_blocks[Index].status = S_CORRUPT_FRONT; 335 if (corrupt_back && !corrupt_front) 336 memory_blocks[Index].status = S_CORRUPT_BACK; 337 if (corrupt_front && corrupt_back) 338 memory_blocks[Index].status = S_CORRUPT_FRONT_BACK; 339 if (!corrupt_front && !corrupt_back) 340 return(True); 341 return(False); 342} 343 344 345/******************************************************************* 346 check all buffers for corruption 347 ********************************************************************/ 348static void mem_check_buffers(void) 349{ 350 int i; 351 for (i=0;i<mem_blocks_allocated;i++) 352 if (memory_blocks[i].status == S_ALLOCATED) 353 mem_buffer_ok(i); 354} 355 356 357/******************************************************************* 358 record stats and alloc memory 359 ********************************************************************/ 360void *smb_mem_malloc(size_t size,char *file,int line) 361{ 362 int Index; 363 INIT_MANAGER(); 364 365 /* find an open spot */ 366 367 Index = mem_first_avail_slot(); 368 if (Index<0) return(NULL); 369 370 /* record some info */ 371 memory_blocks[Index].present_size = size; 372 memory_blocks[Index].allocated_size = size*mem_multiplier; 373 memory_blocks[Index].line = line; 374 strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH); 375 memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0; 376 memory_blocks[Index].error_number = 0; 377 378 /* now try and actually get the memory */ 379 memory_blocks[Index].pointer = malloc(size*mem_multiplier + BUF_SIZE); 380 381 /* if that failed then try and get exactly what was actually requested */ 382 if (memory_blocks[Index].pointer == NULL) 383 { 384 memory_blocks[Index].allocated_size = size; 385 memory_blocks[Index].pointer = malloc(size + BUF_SIZE); 386 } 387 388 /* if it failed then return NULL */ 389 if (memory_blocks[Index].pointer == NULL) return(NULL); 390 391 392 /* it succeeded - set status flag and return */ 393 memory_blocks[Index].status = S_ALLOCATED; 394 395 /* add an offset */ 396 memory_blocks[Index].pointer = 397 (void *)((char *)memory_blocks[Index].pointer + BUF_OFFSET); 398 399 /* fill the buffer appropriately */ 400 mem_fill_buffer(Index); 401 402 /* and set the fill byte */ 403 mem_fill_bytes(memory_blocks[Index].pointer,memory_blocks[Index].present_size,Index); 404 405 /* return the allocated memory */ 406 return(memory_blocks[Index].pointer); 407} 408 409 410/******************************************************************* 411dup a string 412 ********************************************************************/ 413char *smb_mem_strdup(char *s, char *file, int line) 414{ 415 char *ret = (char *)smb_mem_malloc(strlen(s)+1, file, line); 416 strcpy(ret, s); 417 return ret; 418} 419 420/******************************************************************* 421 free some memory 422 ********************************************************************/ 423int smb_mem_free(void *ptr,char *file,int line) 424{ 425 int Index; 426 int free_ret; 427 static int count; 428 INIT_MANAGER(); 429 430 if (count % 100 == 0) { 431 smb_mem_write_errors(dbf); 432 } 433 count++; 434 435 Index = mem_find_Index(ptr); 436 437 if (Index<0) /* we are freeing a pointer that hasn't been allocated ! */ 438 { 439 /* set up an error block */ 440 Index = mem_first_avail_slot(); 441 if (Index < 0) /* I can't even allocate an Error! */ 442 { 443 fprintf(dbf,"Panic in memory manager - can't allocate error block!\n"); 444 fprintf(dbf,"freeing un allocated pointer at %s(%d)\n",file,line); 445 abort(); 446 } 447 /* fill in error block */ 448 memory_blocks[Index].present_size = 0; 449 memory_blocks[Index].allocated_size = 0; 450 memory_blocks[Index].line = line; 451 strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH); 452 memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0; 453 memory_blocks[Index].status = S_ERROR_UNALLOCATED; 454 memory_blocks[Index].pointer = ptr; 455 return(FREE_FAILURE); 456 } 457 458 /* it is a valid pointer - check for corruption */ 459 if (!mem_buffer_ok(Index)) 460 /* it's bad ! return an error */ 461 return(FREE_FAILURE); 462 463 /* the pointer is OK - try to free it */ 464#ifdef MEM_FREE_RETURNS_INT 465 free_ret = free((char *)ptr - BUF_OFFSET); 466#else 467 free((char *)ptr - BUF_OFFSET); 468 free_ret = FREE_SUCCESS; 469#endif 470 471 472 /* if this failed then make an error block again */ 473 if (free_ret == FREE_FAILURE) 474 { 475 memory_blocks[Index].present_size = 0; 476 memory_blocks[Index].allocated_size = 0; 477 memory_blocks[Index].line = line; 478 strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH); 479 memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0; 480 memory_blocks[Index].status = S_ERROR_FREEING; 481 memory_blocks[Index].pointer = ptr; 482 memory_blocks[Index].error_number = errno; 483 return(FREE_FAILURE); 484 } 485 486 /* all is OK - set status and return */ 487 memory_blocks[Index].status = S_UNALLOCATED; 488 489 /* this is a speedup - if it is freed then it can be allocated again ! */ 490 last_block_allocated = Index; 491 492 return(FREE_SUCCESS); 493} 494 495 496 497/******************************************************************* 498 writes info on just one Index 499 it must not be un allocated to do this 500 ********************************************************************/ 501static void mem_write_Index_info(int Index,FILE *outfile) 502{ 503 if (memory_blocks[Index].status != S_UNALLOCATED) 504 fprintf(outfile,"block %d file %s(%d) : size %d, alloc size %d, status %s\n", 505 Index,memory_blocks[Index].file,memory_blocks[Index].line, 506 memory_blocks[Index].present_size, 507 memory_blocks[Index].allocated_size, 508 status_to_str(memory_blocks[Index].status)); 509} 510 511 512 513/******************************************************************* 514 writes info on one pointer 515 ********************************************************************/ 516void smb_mem_write_info(void *ptr,FILE *outfile) 517{ 518 int Index; 519 INIT_MANAGER(); 520 Index = mem_find_Index(ptr); 521 if (Index<0) return; 522 mem_write_Index_info(Index,outfile); 523} 524 525 526 527 528/******************************************************************* 529 return the size of the mem block 530 ********************************************************************/ 531size_t smb_mem_query_size(void *ptr) 532{ 533 int Index; 534 INIT_MANAGER(); 535 Index = mem_find_Index(ptr); 536 if (Index<0) return(0); 537 return(memory_blocks[Index].present_size); 538} 539 540/******************************************************************* 541 return the allocated size of the mem block 542 ********************************************************************/ 543size_t smb_mem_query_real_size(void *ptr) 544{ 545 int Index; 546 INIT_MANAGER(); 547 Index = mem_find_Index(ptr); 548 if (Index<0) return(0); 549 return(memory_blocks[Index].allocated_size); 550} 551 552 553 554 555/******************************************************************* 556 return the file of caller of the mem block 557 ********************************************************************/ 558char *smb_mem_query_file(void *ptr) 559{ 560 int Index; 561 INIT_MANAGER(); 562 Index = mem_find_Index(ptr); 563 if (Index<0) return(NULL); 564 return(memory_blocks[Index].file); 565} 566 567 568 569/******************************************************************* 570 return the line in the file of caller of the mem block 571 ********************************************************************/ 572int smb_mem_query_line(void *ptr) 573{ 574 int Index; 575 INIT_MANAGER(); 576 Index = mem_find_Index(ptr); 577 if (Index<0) return(0); 578 return(memory_blocks[Index].line); 579} 580 581/******************************************************************* 582 return True if the pointer is OK 583 ********************************************************************/ 584int smb_mem_test(void *ptr) 585{ 586 int Index; 587 INIT_MANAGER(); 588 Index = mem_find_Index(ptr); 589 if (Index<0) return(False); 590 591 return(mem_buffer_ok(Index)); 592} 593 594 595/******************************************************************* 596 write brief info on mem status 597 ********************************************************************/ 598void smb_mem_write_status(FILE *outfile) 599{ 600 int num_allocated=0; 601 int total_size=0; 602 int total_alloc_size=0; 603 int num_errors=0; 604 int i; 605 INIT_MANAGER(); 606 mem_check_buffers(); 607 for (i=0;i<mem_blocks_allocated;i++) 608 switch (memory_blocks[i].status) 609 { 610 case S_UNALLOCATED : 611 break; 612 case S_ALLOCATED : 613 num_allocated++; 614 total_size += memory_blocks[i].present_size; 615 total_alloc_size += memory_blocks[i].allocated_size; 616 break; 617 case S_ERROR_UNALLOCATED : 618 case S_ERROR_FREEING : 619 case S_CORRUPT_BACK : 620 case S_CORRUPT_FRONT : 621 num_errors++; 622 break; 623 } 624 625 fprintf(outfile, 626 "Mem Manager : %d blocks, allocation %dK, real allocation %dK, %d errors\n", 627 num_allocated,(int)(total_size/1024),(int)(total_alloc_size/1024), 628 num_errors); 629 fflush(outfile); 630} 631 632 633/******************************************************************* 634 write verbose info on allocated blocks 635 ********************************************************************/ 636void smb_mem_write_verbose(FILE *outfile) 637{ 638 int Index; 639 /* first write a summary */ 640 INIT_MANAGER(); 641 smb_mem_write_status(outfile); 642 643 /* just loop writing info on relevant indices */ 644 for (Index=0;Index<mem_blocks_allocated;Index++) 645 if (memory_blocks[Index].status != S_UNALLOCATED) 646 mem_write_Index_info(Index,outfile); 647} 648 649/******************************************************************* 650 write verbose info on error blocks 651 ********************************************************************/ 652void smb_mem_write_errors(FILE *outfile) 653{ 654 int Index; 655 INIT_MANAGER(); 656 mem_check_buffers(); 657 /* just loop writing info on relevant indices */ 658 for (Index=0;Index<mem_blocks_allocated;Index++) 659 if (((int)memory_blocks[Index].status) > ((int)S_ALLOCATED)) 660 mem_write_Index_info(Index,outfile); 661} 662 663 664/******************************************************************* 665 sets the memory multiplier 666 ********************************************************************/ 667void smb_mem_set_multiplier(int multiplier) 668{ 669 /* check it is valid */ 670 if (multiplier < 1) return; 671 mem_multiplier = multiplier; 672} 673 674/******************************************************************* 675 increases or decreases the memory assigned to a pointer 676 ********************************************************************/ 677void *smb_mem_resize(void *ptr,size_t newsize) 678{ 679 int Index; 680 size_t allocsize; 681 void *temp_ptr; 682 INIT_MANAGER(); 683 Index = mem_find_Index(ptr); 684 685 /* if invalid return NULL */ 686 if (Index<0) 687 { 688#ifdef BUG 689 int Error(); 690 Error("Invalid mem_resize to size %d\n",newsize); 691#endif 692 return(NULL); 693 } 694 695 /* now - will it fit in the current allocation ? */ 696 if (newsize <= memory_blocks[Index].allocated_size) 697 { 698 memory_blocks[Index].present_size = newsize; 699 mem_fill_buffer(Index); 700 return(ptr); 701 } 702 703 /* can it be allocated ? */ 704 allocsize = newsize*mem_multiplier; 705 temp_ptr = malloc(newsize*mem_multiplier + BUF_SIZE); 706 707 /* no? try with just the size asked for */ 708 if (temp_ptr == NULL) 709 { 710 allocsize=newsize; 711 temp_ptr = malloc(newsize + BUF_SIZE); 712 } 713 714 /* if it's still NULL give up */ 715 if (temp_ptr == NULL) 716 return(NULL); 717 718 /* copy the old data to the new memory area */ 719 memcpy(temp_ptr,(char *)memory_blocks[Index].pointer - BUF_OFFSET, 720 memory_blocks[Index].allocated_size + BUF_SIZE); 721 722 /* fill the extra space */ 723 mem_fill_bytes((char *)temp_ptr + BUF_OFFSET + memory_blocks[Index].present_size,newsize - memory_blocks[Index].present_size,Index); 724 725 726 /* free the old mem and set vars */ 727 free((char *)ptr - BUF_OFFSET); 728 memory_blocks[Index].pointer = (void *)((char *)temp_ptr + BUF_OFFSET); 729 memory_blocks[Index].present_size = newsize; 730 memory_blocks[Index].allocated_size = allocsize; 731 732 /* fill the buffer appropriately */ 733 mem_fill_buffer(Index); 734 735 736 /* now return the new pointer */ 737 return((char *)temp_ptr + BUF_OFFSET); 738} 739 740#else 741 void dummy_mem_man(void) {} 742#endif 743