2 /* a simple memory manager. All allocates and frees should go through here */
13 #ifdef MEM_SIGNAL_HANDLER
18 this module is stand alone. typically a define will occur in a C file
21 #define malloc(x) smb_mem_malloc(x,__FILE__,__LINE__)
22 #define free(x) smb_mem_free(x,__FILE__,__LINE__)
24 which redirects all calls to malloc and free through this module
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.
36 ACCESSING the memory manager :
38 mem_init_memory_manager() :
39 initialises internal data structures of memory manager
41 void *malloc(size_t size) :
42 allocates memory as per usual. also records lots of info
45 frees some memory as per usual. writes errors if necessary.
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.
51 int smb_mem_query_size(void *ptr) :
52 returns the size of the allocated memory.
54 int smb_mem_query_real_size(void *ptr) :
55 returns the actual amount of memory allocated to a pointer.
57 char *smb_mem_query_file(void *ptr) :
58 returns the name of the file where the pointer was allocated.
60 int smb_mem_query_line(void *ptr) :
61 returns the line of the file where the memory was allocated.
63 void smb_mem_write_status(FILE *outfile) :
64 writes short summary of memory stats on the stream.
66 void smb_mem_write_verbose(FILE *outfile) :
67 writes lots of info on current allocations to stream.
69 void smb_mem_write_errors(FILE *outfile) :
70 writes info on error blocks
72 void smb_mem_write_info(void *ptr,FILE *outfile)
73 writes info on one pointer to outfile
75 int smb_mem_test(void *ptr) :
76 returns true if the pointer is OK - false if it is not.
78 void smb_mem_set_multiplier(int multiplier) :
79 sets defaults amount of memory allocated to multiplier times
82 int smb_mem_total_errors(void) :
83 returns the total number of error blocks
85 void smb_mem_check_buffers(void) :
86 checks all buffers for corruption. It marks them as corrupt if they are.
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.
96 void smb_mem_write_errors(FILE *outfile);
97 void smb_mem_write_verbose(FILE *outfile);
98 void smb_mem_write_status(FILE *outfile);
99 static void mem_check_buffers(void);
102 #define FREE_FAILURE 0
103 #define FREE_SUCCESS 1
106 #define False (!True)
107 #define BUF_SIZE (MEM_CORRUPT_BUFFER * sizeof(char) * 2)
108 #define BUF_OFFSET (BUF_SIZE/2)
114 size_t allocated_size;
115 unsigned char status;
117 char file[MEM_FILE_STR_LENGTH];
121 /* the order of this enum is important. everything greater than
122 S_ALLOCATED is considered an error */
123 enum status_types {S_UNALLOCATED,S_ALLOCATED,
124 S_ERROR_UNALLOCATED,S_ERROR_FREEING,
125 S_CORRUPT_FRONT,S_CORRUPT_BACK,S_CORRUPT_FRONT_BACK};
127 /* here is the data memory */
129 static memory_struct *memory_blocks=NULL; /* these hold the allocation data */
130 static int mem_blocks_allocated=0; /* how many mem blocks are allocated */
131 static int mem_multiplier; /* this is the current multiplier mor over allocation */
132 static int mem_manager_initialised=False; /* has it been initialised ? */
133 static 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 */
144 static stat_str_type stat_str_struct[] =
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"},
157 #define INIT_MANAGER() if (!mem_manager_initialised) mem_init_memory_manager()
159 /*******************************************************************
160 returns a pointer to a static string for each status
161 ********************************************************************/
162 static char *status_to_str(int status)
165 while (stat_str_struct[i].label != NULL)
167 if (stat_str_struct[i].status == status)
168 return(stat_str_struct[i].label);
176 #ifdef MEM_SIGNAL_HANDLER
177 /*******************************************************************
178 this handles signals - causes a mem_write_verbose on stderr
179 ********************************************************************/
180 static void mem_signal_handler()
183 smb_mem_write_verbose(dbf);
184 signal(MEM_SIGNAL_VECTOR,mem_signal_handler);
188 #ifdef MEM_SIGNAL_HANDLER
189 /*******************************************************************
190 this handles error signals - causes a mem_write_verbose on stderr
191 ********************************************************************/
192 static void error_signal_handler()
194 fprintf(dbf,"Received error signal!\n");
196 smb_mem_write_status(dbf);
197 smb_mem_write_errors(dbf);
203 /*******************************************************************
204 initialise memory manager data structures
205 ********************************************************************/
206 static void mem_init_memory_manager(void)
209 /* allocate the memory_blocks array */
210 mem_blocks_allocated = MEM_MAX_MEM_OBJECTS;
212 while (mem_blocks_allocated > 0)
214 memory_blocks = (memory_struct *)
215 calloc(mem_blocks_allocated,sizeof(memory_struct));
216 if (memory_blocks != NULL) break;
217 mem_blocks_allocated /= 2;
220 if (memory_blocks == NULL)
222 fprintf(dbf,"Panic ! can't allocate mem manager blocks!\n");
226 /* just loop setting status flag to unallocated */
227 for (i=0;i<mem_blocks_allocated;i++)
228 memory_blocks[i].status = S_UNALLOCATED;
230 /* also set default mem multiplier */
231 mem_multiplier = MEM_DEFAULT_MEM_MULTIPLIER;
232 mem_manager_initialised=True;
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);
243 /*******************************************************************
244 finds first available slot in memory blocks
245 ********************************************************************/
246 static int mem_first_avail_slot(void)
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);
259 /*******************************************************************
260 find which Index a pointer refers to
261 ********************************************************************/
262 static int mem_find_Index(void *ptr)
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))
271 for (i=(start+1);i<mem_blocks_allocated;i++)
272 if ((memory_blocks[i].status == S_ALLOCATED) &&
273 (memory_blocks[i].pointer == ptr))
275 /* it's not there! */
279 /*******************************************************************
280 fill the buffer areas of a mem block
281 ********************************************************************/
282 static void mem_fill_bytes(void *p,int size,int Index)
284 memset(p,Index%256,size);
287 /*******************************************************************
288 fill the buffer areas of a mem block
289 ********************************************************************/
290 static void mem_fill_buffer(int Index)
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);
302 for (i=0;i<MEM_CORRUPT_BUFFER;i++)
306 seed += MEM_SEED_INCREMENT;
310 /*******************************************************************
311 check if a mem block is corrupt
312 ********************************************************************/
313 static int mem_buffer_ok(int Index)
317 int corrupt_front = False;
318 int corrupt_back = False;
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;
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))
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)
345 /*******************************************************************
346 check all buffers for corruption
347 ********************************************************************/
348 static void mem_check_buffers(void)
351 for (i=0;i<mem_blocks_allocated;i++)
352 if (memory_blocks[i].status == S_ALLOCATED)
357 /*******************************************************************
358 record stats and alloc memory
359 ********************************************************************/
360 void *smb_mem_malloc(size_t size,char *file,int line)
365 /* find an open spot */
367 Index = mem_first_avail_slot();
368 if (Index<0) return(NULL);
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;
378 /* now try and actually get the memory */
379 memory_blocks[Index].pointer = malloc(size*mem_multiplier + BUF_SIZE);
381 /* if that failed then try and get exactly what was actually requested */
382 if (memory_blocks[Index].pointer == NULL)
384 memory_blocks[Index].allocated_size = size;
385 memory_blocks[Index].pointer = malloc(size + BUF_SIZE);
388 /* if it failed then return NULL */
389 if (memory_blocks[Index].pointer == NULL) return(NULL);
392 /* it succeeded - set status flag and return */
393 memory_blocks[Index].status = S_ALLOCATED;
396 memory_blocks[Index].pointer =
397 (void *)((char *)memory_blocks[Index].pointer + BUF_OFFSET);
399 /* fill the buffer appropriately */
400 mem_fill_buffer(Index);
402 /* and set the fill byte */
403 mem_fill_bytes(memory_blocks[Index].pointer,memory_blocks[Index].present_size,Index);
405 /* return the allocated memory */
406 return(memory_blocks[Index].pointer);
410 /*******************************************************************
412 ********************************************************************/
413 char *smb_mem_strdup(char *s, char *file, int line)
415 char *ret = (char *)smb_mem_malloc(strlen(s)+1, file, line);
420 /*******************************************************************
422 ********************************************************************/
423 int smb_mem_free(void *ptr,char *file,int line)
430 if (count % 100 == 0) {
431 smb_mem_write_errors(dbf);
435 Index = mem_find_Index(ptr);
437 if (Index<0) /* we are freeing a pointer that hasn't been allocated ! */
439 /* set up an error block */
440 Index = mem_first_avail_slot();
441 if (Index < 0) /* I can't even allocate an Error! */
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);
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);
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);
463 /* the pointer is OK - try to free it */
464 #ifdef MEM_FREE_RETURNS_INT
465 free_ret = free((char *)ptr - BUF_OFFSET);
467 free((char *)ptr - BUF_OFFSET);
468 free_ret = FREE_SUCCESS;
472 /* if this failed then make an error block again */
473 if (free_ret == FREE_FAILURE)
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);
486 /* all is OK - set status and return */
487 memory_blocks[Index].status = S_UNALLOCATED;
489 /* this is a speedup - if it is freed then it can be allocated again ! */
490 last_block_allocated = Index;
492 return(FREE_SUCCESS);
497 /*******************************************************************
498 writes info on just one Index
499 it must not be un allocated to do this
500 ********************************************************************/
501 static void mem_write_Index_info(int Index,FILE *outfile)
503 if (memory_blocks[Index].status != S_UNALLOCATED)
504 fprintf(outfile,"block %d file %s(%d) : ptr: %p size %d, alloc size %d, status %s\n",
505 Index,memory_blocks[Index].file,memory_blocks[Index].line,
506 memory_blocks[Index].pointer,
507 memory_blocks[Index].present_size,
508 memory_blocks[Index].allocated_size,
509 status_to_str(memory_blocks[Index].status));
514 /*******************************************************************
515 writes info on one pointer
516 ********************************************************************/
517 void smb_mem_write_info(void *ptr,FILE *outfile)
521 Index = mem_find_Index(ptr);
523 mem_write_Index_info(Index,outfile);
529 /*******************************************************************
530 return the size of the mem block
531 ********************************************************************/
532 size_t smb_mem_query_size(void *ptr)
536 Index = mem_find_Index(ptr);
537 if (Index<0) return(0);
538 return(memory_blocks[Index].present_size);
541 /*******************************************************************
542 return the allocated size of the mem block
543 ********************************************************************/
544 size_t smb_mem_query_real_size(void *ptr)
548 Index = mem_find_Index(ptr);
549 if (Index<0) return(0);
550 return(memory_blocks[Index].allocated_size);
556 /*******************************************************************
557 return the file of caller of the mem block
558 ********************************************************************/
559 char *smb_mem_query_file(void *ptr)
563 Index = mem_find_Index(ptr);
564 if (Index<0) return(NULL);
565 return(memory_blocks[Index].file);
570 /*******************************************************************
571 return the line in the file of caller of the mem block
572 ********************************************************************/
573 int smb_mem_query_line(void *ptr)
577 Index = mem_find_Index(ptr);
578 if (Index<0) return(0);
579 return(memory_blocks[Index].line);
582 /*******************************************************************
583 return True if the pointer is OK
584 ********************************************************************/
585 int smb_mem_test(void *ptr)
589 Index = mem_find_Index(ptr);
590 if (Index<0) return(False);
592 return(mem_buffer_ok(Index));
596 /*******************************************************************
597 write brief info on mem status
598 ********************************************************************/
599 void smb_mem_write_status(FILE *outfile)
603 int total_alloc_size=0;
608 for (i=0;i<mem_blocks_allocated;i++)
609 switch (memory_blocks[i].status)
615 total_size += memory_blocks[i].present_size;
616 total_alloc_size += memory_blocks[i].allocated_size;
618 case S_ERROR_UNALLOCATED :
619 case S_ERROR_FREEING :
620 case S_CORRUPT_BACK :
621 case S_CORRUPT_FRONT :
627 "Mem Manager : %d blocks, allocation %dK, real allocation %dK, %d errors\n",
628 num_allocated,(int)(total_size/1024),(int)(total_alloc_size/1024),
634 /*******************************************************************
635 write verbose info on allocated blocks
636 ********************************************************************/
637 void smb_mem_write_verbose(FILE *outfile)
640 /* first write a summary */
642 smb_mem_write_status(outfile);
644 /* just loop writing info on relevant indices */
645 for (Index=0;Index<mem_blocks_allocated;Index++)
646 if (memory_blocks[Index].status != S_UNALLOCATED)
647 mem_write_Index_info(Index,outfile);
650 /*******************************************************************
651 write verbose info on error blocks
652 ********************************************************************/
653 void smb_mem_write_errors(FILE *outfile)
658 /* just loop writing info on relevant indices */
659 for (Index=0;Index<mem_blocks_allocated;Index++)
660 if (((int)memory_blocks[Index].status) > ((int)S_ALLOCATED))
661 mem_write_Index_info(Index,outfile);
665 /*******************************************************************
666 sets the memory multiplier
667 ********************************************************************/
668 void smb_mem_set_multiplier(int multiplier)
670 /* check it is valid */
671 if (multiplier < 1) return;
672 mem_multiplier = multiplier;
675 /*******************************************************************
676 increases or decreases the memory assigned to a pointer
677 ********************************************************************/
678 void *smb_mem_resize(void *ptr,size_t newsize)
684 Index = mem_find_Index(ptr);
686 /* if invalid return NULL */
691 Error("Invalid mem_resize to size %d\n",newsize);
696 /* now - will it fit in the current allocation ? */
697 if (newsize <= memory_blocks[Index].allocated_size)
699 memory_blocks[Index].present_size = newsize;
700 mem_fill_buffer(Index);
704 /* can it be allocated ? */
705 allocsize = newsize*mem_multiplier;
706 temp_ptr = malloc(newsize*mem_multiplier + BUF_SIZE);
708 /* no? try with just the size asked for */
709 if (temp_ptr == NULL)
712 temp_ptr = malloc(newsize + BUF_SIZE);
715 /* if it's still NULL give up */
716 if (temp_ptr == NULL)
719 /* copy the old data to the new memory area */
720 memcpy(temp_ptr,(char *)memory_blocks[Index].pointer - BUF_OFFSET,
721 memory_blocks[Index].allocated_size + BUF_SIZE);
723 /* fill the extra space */
724 mem_fill_bytes((char *)temp_ptr + BUF_OFFSET + memory_blocks[Index].present_size,newsize - memory_blocks[Index].present_size,Index);
727 /* free the old mem and set vars */
728 free((char *)ptr - BUF_OFFSET);
729 memory_blocks[Index].pointer = (void *)((char *)temp_ptr + BUF_OFFSET);
730 memory_blocks[Index].present_size = newsize;
731 memory_blocks[Index].allocated_size = allocsize;
733 /* fill the buffer appropriately */
734 mem_fill_buffer(Index);
737 /* now return the new pointer */
738 return((char *)temp_ptr + BUF_OFFSET);
742 void dummy_mem_man(void) {}