summaryrefslogtreecommitdiff
path: root/source3/mem_man/mem_man.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/mem_man/mem_man.c')
-rw-r--r--source3/mem_man/mem_man.c742
1 files changed, 742 insertions, 0 deletions
diff --git a/source3/mem_man/mem_man.c b/source3/mem_man/mem_man.c
new file mode 100644
index 0000000000..758fac4386
--- /dev/null
+++ b/source3/mem_man/mem_man.c
@@ -0,0 +1,742 @@
+#if MEM_MAN
+/* a simple memory manager. All allocates and frees should go through here */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define MEM_MAN_MAIN
+
+#include "mem_man.h"
+
+#ifdef MEM_SIGNAL_HANDLER
+#include <signal.h>
+#endif
+
+/*
+ this module is stand alone. typically a define will occur in a C file
+ like this
+
+ #define malloc(x) smb_mem_malloc(x,__FILE__,__LINE__)
+ #define free(x) smb_mem_free(x,__FILE__,__LINE__)
+
+ which redirects all calls to malloc and free through this module
+
+ Various configuration options can be set in mem_man.h. This file also
+ includes the defines above - so the complete system can be implemented
+ with just one include call.
+
+
+ */
+
+extern FILE *dbf;
+
+/*
+ ACCESSING the memory manager :
+
+ mem_init_memory_manager() :
+ initialises internal data structures of memory manager
+
+ void *malloc(size_t size) :
+ allocates memory as per usual. also records lots of info
+
+ int free(void *ptr) :
+ frees some memory as per usual. writes errors if necessary.
+
+ void *smb_mem_resize(void *ptr,size_t newsize) :
+ changes the memory assignment size of a pointer. note it may return a
+ different pointer than the one given. memory can be sized up or down.
+
+ int smb_mem_query_size(void *ptr) :
+ returns the size of the allocated memory.
+
+ int smb_mem_query_real_size(void *ptr) :
+ returns the actual amount of memory allocated to a pointer.
+
+ char *smb_mem_query_file(void *ptr) :
+ returns the name of the file where the pointer was allocated.
+
+ int smb_mem_query_line(void *ptr) :
+ returns the line of the file where the memory was allocated.
+
+ void smb_mem_write_status(FILE *outfile) :
+ writes short summary of memory stats on the stream.
+
+ void smb_mem_write_verbose(FILE *outfile) :
+ writes lots of info on current allocations to stream.
+
+ void smb_mem_write_errors(FILE *outfile) :
+ writes info on error blocks
+
+ void smb_mem_write_info(void *ptr,FILE *outfile)
+ writes info on one pointer to outfile
+
+ int smb_mem_test(void *ptr) :
+ returns true if the pointer is OK - false if it is not.
+
+ void smb_mem_set_multiplier(int multiplier) :
+ sets defaults amount of memory allocated to multiplier times
+ amount requested.
+
+ int smb_mem_total_errors(void) :
+ returns the total number of error blocks
+
+ void smb_mem_check_buffers(void) :
+ checks all buffers for corruption. It marks them as corrupt if they are.
+
+ kill -USR1 <pid> :
+ this will send a signal to the memory manager to do a mem_write_verbose
+ it also checks them for corruption. Note that the signal number can be
+ set in the header file mem_man.h. This can also be turned off.
+
+ */
+
+
+void smb_mem_write_errors(FILE *outfile);
+void smb_mem_write_verbose(FILE *outfile);
+void smb_mem_write_status(FILE *outfile);
+static void mem_check_buffers(void);
+
+
+#define FREE_FAILURE 0
+#define FREE_SUCCESS 1
+#define FN
+#define True (0==0)
+#define False (!True)
+#define BUF_SIZE (MEM_CORRUPT_BUFFER * sizeof(char) * 2)
+#define BUF_OFFSET (BUF_SIZE/2)
+
+typedef struct
+{
+ void *pointer;
+ size_t present_size;
+ size_t allocated_size;
+ unsigned char status;
+ short error_number;
+ char file[MEM_FILE_STR_LENGTH];
+ unsigned short line;
+} memory_struct;
+
+/* the order of this enum is important. everything greater than
+ S_ALLOCATED is considered an error */
+enum status_types {S_UNALLOCATED,S_ALLOCATED,
+ S_ERROR_UNALLOCATED,S_ERROR_FREEING,
+ S_CORRUPT_FRONT,S_CORRUPT_BACK,S_CORRUPT_FRONT_BACK};
+
+/* here is the data memory */
+
+static memory_struct *memory_blocks=NULL; /* these hold the allocation data */
+static int mem_blocks_allocated=0; /* how many mem blocks are allocated */
+static int mem_multiplier; /* this is the current multiplier mor over allocation */
+static int mem_manager_initialised=False; /* has it been initialised ? */
+static int last_block_allocated=0; /* a speed up method - this will contain the
+ index of the last block allocated or freed
+ to cut down searching time for a new block */
+
+
+typedef struct
+{
+ int status;
+ char *label;
+} stat_str_type;
+
+static stat_str_type stat_str_struct[] =
+{
+{S_UNALLOCATED,"S_UNALLOCATED"},
+{S_ALLOCATED,"S_ALLOCATED"},
+{S_ERROR_UNALLOCATED,"S_ERROR_UNALLOCATED"},
+{S_ERROR_FREEING,"S_ERROR_FREEING"},
+{S_CORRUPT_FRONT,"S_CORRUPT_FRONT"},
+{S_CORRUPT_BACK,"S_CORRUPT_BACK"},
+{S_CORRUPT_FRONT_BACK,"S_CORRUPT_FRONT_BACK"},
+{-1,NULL}
+};
+
+
+#define INIT_MANAGER() if (!mem_manager_initialised) mem_init_memory_manager()
+
+/*******************************************************************
+ returns a pointer to a static string for each status
+ ********************************************************************/
+static char *status_to_str(int status)
+{
+ int i=0;
+ while (stat_str_struct[i].label != NULL)
+ {
+ if (stat_str_struct[i].status == status)
+ return(stat_str_struct[i].label);
+ i++;
+ }
+ return(NULL);
+}
+
+
+
+#ifdef MEM_SIGNAL_HANDLER
+/*******************************************************************
+ this handles signals - causes a mem_write_verbose on stderr
+ ********************************************************************/
+static void mem_signal_handler()
+{
+ mem_check_buffers();
+ smb_mem_write_verbose(dbf);
+ signal(MEM_SIGNAL_VECTOR,mem_signal_handler);
+}
+#endif
+
+#ifdef MEM_SIGNAL_HANDLER
+/*******************************************************************
+ this handles error signals - causes a mem_write_verbose on stderr
+ ********************************************************************/
+static void error_signal_handler()
+{
+ fprintf(dbf,"Received error signal!\n");
+ mem_check_buffers();
+ smb_mem_write_status(dbf);
+ smb_mem_write_errors(dbf);
+ abort();
+}
+#endif
+
+
+/*******************************************************************
+ initialise memory manager data structures
+ ********************************************************************/
+static void mem_init_memory_manager(void)
+{
+ int i;
+ /* allocate the memory_blocks array */
+ mem_blocks_allocated = MEM_MAX_MEM_OBJECTS;
+
+ while (mem_blocks_allocated > 0)
+ {
+ memory_blocks = (memory_struct *)
+ calloc(mem_blocks_allocated,sizeof(memory_struct));
+ if (memory_blocks != NULL) break;
+ mem_blocks_allocated /= 2;
+ }
+
+ if (memory_blocks == NULL)
+ {
+ fprintf(dbf,"Panic ! can't allocate mem manager blocks!\n");
+ abort();
+ }
+
+ /* just loop setting status flag to unallocated */
+ for (i=0;i<mem_blocks_allocated;i++)
+ memory_blocks[i].status = S_UNALLOCATED;
+
+ /* also set default mem multiplier */
+ mem_multiplier = MEM_DEFAULT_MEM_MULTIPLIER;
+ mem_manager_initialised=True;
+
+#ifdef MEM_SIGNAL_HANDLER
+ signal(MEM_SIGNAL_VECTOR,mem_signal_handler);
+ signal(SIGSEGV,error_signal_handler);
+ signal(SIGBUS,error_signal_handler);
+#endif
+
+}
+
+
+/*******************************************************************
+ finds first available slot in memory blocks
+ ********************************************************************/
+static int mem_first_avail_slot(void)
+{
+ int i;
+ for (i=last_block_allocated;i<mem_blocks_allocated;i++)
+ if (memory_blocks[i].status == S_UNALLOCATED)
+ return(last_block_allocated=i);
+ for (i=0;i<last_block_allocated;i++)
+ if (memory_blocks[i].status == S_UNALLOCATED)
+ return(last_block_allocated=i);
+ return(-1);
+}
+
+
+/*******************************************************************
+ find which Index a pointer refers to
+ ********************************************************************/
+static int mem_find_Index(void *ptr)
+{
+ int i;
+ int start = last_block_allocated+mem_blocks_allocated/50;
+ if (start > mem_blocks_allocated-1) start = mem_blocks_allocated-1;
+ for (i=start;i>=0;i--)
+ if ((memory_blocks[i].status == S_ALLOCATED) &&
+ (memory_blocks[i].pointer == ptr))
+ return(i);
+ for (i=(start+1);i<mem_blocks_allocated;i++)
+ if ((memory_blocks[i].status == S_ALLOCATED) &&
+ (memory_blocks[i].pointer == ptr))
+ return(i);
+ /* it's not there! */
+ return(-1);
+}
+
+/*******************************************************************
+ fill the buffer areas of a mem block
+ ********************************************************************/
+static void mem_fill_bytes(void *p,int size,int Index)
+{
+ memset(p,Index%256,size);
+}
+
+/*******************************************************************
+ fill the buffer areas of a mem block
+ ********************************************************************/
+static void mem_fill_buffer(int Index)
+{
+ char *iptr,*tailptr;
+ int i;
+ int seed;
+
+ /* fill the front and back ends */
+ seed = MEM_CORRUPT_SEED;
+ iptr = (char *)((char *)memory_blocks[Index].pointer - BUF_OFFSET);
+ tailptr = (char *)((char *)memory_blocks[Index].pointer +
+ memory_blocks[Index].present_size);
+
+ for (i=0;i<MEM_CORRUPT_BUFFER;i++)
+ {
+ iptr[i] = seed;
+ tailptr[i] = seed;
+ seed += MEM_SEED_INCREMENT;
+ }
+}
+
+/*******************************************************************
+ check if a mem block is corrupt
+ ********************************************************************/
+static int mem_buffer_ok(int Index)
+{
+ char *iptr;
+ int i;
+ int corrupt_front = False;
+ int corrupt_back = False;
+
+ /* check the front end */
+ iptr = (char *)((char *)memory_blocks[Index].pointer - BUF_OFFSET);
+ for (i=0;i<MEM_CORRUPT_BUFFER;i++)
+ if (iptr[i] != (char)(MEM_CORRUPT_SEED + i*MEM_SEED_INCREMENT))
+ corrupt_front = True;
+
+ /* now check the tail end */
+ iptr = (char *)((char *)memory_blocks[Index].pointer +
+ memory_blocks[Index].present_size);
+ for (i=0;i<MEM_CORRUPT_BUFFER;i++)
+ if (iptr[i] != (char)(MEM_CORRUPT_SEED + i*MEM_SEED_INCREMENT))
+ corrupt_back = True;
+
+ if (corrupt_front && !corrupt_back)
+ memory_blocks[Index].status = S_CORRUPT_FRONT;
+ if (corrupt_back && !corrupt_front)
+ memory_blocks[Index].status = S_CORRUPT_BACK;
+ if (corrupt_front && corrupt_back)
+ memory_blocks[Index].status = S_CORRUPT_FRONT_BACK;
+ if (!corrupt_front && !corrupt_back)
+ return(True);
+ return(False);
+}
+
+
+/*******************************************************************
+ check all buffers for corruption
+ ********************************************************************/
+static void mem_check_buffers(void)
+{
+ int i;
+ for (i=0;i<mem_blocks_allocated;i++)
+ if (memory_blocks[i].status == S_ALLOCATED)
+ mem_buffer_ok(i);
+}
+
+
+/*******************************************************************
+ record stats and alloc memory
+ ********************************************************************/
+void *smb_mem_malloc(size_t size,char *file,int line)
+{
+ int Index;
+ INIT_MANAGER();
+
+ /* find an open spot */
+
+ Index = mem_first_avail_slot();
+ if (Index<0) return(NULL);
+
+ /* record some info */
+ memory_blocks[Index].present_size = size;
+ memory_blocks[Index].allocated_size = size*mem_multiplier;
+ memory_blocks[Index].line = line;
+ strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH);
+ memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0;
+ memory_blocks[Index].error_number = 0;
+
+ /* now try and actually get the memory */
+ memory_blocks[Index].pointer = malloc(size*mem_multiplier + BUF_SIZE);
+
+ /* if that failed then try and get exactly what was actually requested */
+ if (memory_blocks[Index].pointer == NULL)
+ {
+ memory_blocks[Index].allocated_size = size;
+ memory_blocks[Index].pointer = malloc(size + BUF_SIZE);
+ }
+
+ /* if it failed then return NULL */
+ if (memory_blocks[Index].pointer == NULL) return(NULL);
+
+
+ /* it succeeded - set status flag and return */
+ memory_blocks[Index].status = S_ALLOCATED;
+
+ /* add an offset */
+ memory_blocks[Index].pointer =
+ (void *)((char *)memory_blocks[Index].pointer + BUF_OFFSET);
+
+ /* fill the buffer appropriately */
+ mem_fill_buffer(Index);
+
+ /* and set the fill byte */
+ mem_fill_bytes(memory_blocks[Index].pointer,memory_blocks[Index].present_size,Index);
+
+ /* return the allocated memory */
+ return(memory_blocks[Index].pointer);
+}
+
+
+/*******************************************************************
+dup a string
+ ********************************************************************/
+char *smb_mem_strdup(char *s, char *file, int line)
+{
+ char *ret = (char *)smb_mem_malloc(strlen(s)+1, file, line);
+ strcpy(ret, s);
+ return ret;
+}
+
+/*******************************************************************
+ free some memory
+ ********************************************************************/
+int smb_mem_free(void *ptr,char *file,int line)
+{
+ int Index;
+ int free_ret;
+ static int count;
+ INIT_MANAGER();
+
+ if (count % 100 == 0) {
+ smb_mem_write_errors(dbf);
+ }
+ count++;
+
+ Index = mem_find_Index(ptr);
+
+ if (Index<0) /* we are freeing a pointer that hasn't been allocated ! */
+ {
+ /* set up an error block */
+ Index = mem_first_avail_slot();
+ if (Index < 0) /* I can't even allocate an Error! */
+ {
+ fprintf(dbf,"Panic in memory manager - can't allocate error block!\n");
+ fprintf(dbf,"freeing un allocated pointer at %s(%d)\n",file,line);
+ abort();
+ }
+ /* fill in error block */
+ memory_blocks[Index].present_size = 0;
+ memory_blocks[Index].allocated_size = 0;
+ memory_blocks[Index].line = line;
+ strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH);
+ memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0;
+ memory_blocks[Index].status = S_ERROR_UNALLOCATED;
+ memory_blocks[Index].pointer = ptr;
+ return(FREE_FAILURE);
+ }
+
+ /* it is a valid pointer - check for corruption */
+ if (!mem_buffer_ok(Index))
+ /* it's bad ! return an error */
+ return(FREE_FAILURE);
+
+ /* the pointer is OK - try to free it */
+#ifdef MEM_FREE_RETURNS_INT
+ free_ret = free((char *)ptr - BUF_OFFSET);
+#else
+ free((char *)ptr - BUF_OFFSET);
+ free_ret = FREE_SUCCESS;
+#endif
+
+
+ /* if this failed then make an error block again */
+ if (free_ret == FREE_FAILURE)
+ {
+ memory_blocks[Index].present_size = 0;
+ memory_blocks[Index].allocated_size = 0;
+ memory_blocks[Index].line = line;
+ strncpy(memory_blocks[Index].file,file,MEM_FILE_STR_LENGTH);
+ memory_blocks[Index].file[MEM_FILE_STR_LENGTH-1] = 0;
+ memory_blocks[Index].status = S_ERROR_FREEING;
+ memory_blocks[Index].pointer = ptr;
+ memory_blocks[Index].error_number = errno;
+ return(FREE_FAILURE);
+ }
+
+ /* all is OK - set status and return */
+ memory_blocks[Index].status = S_UNALLOCATED;
+
+ /* this is a speedup - if it is freed then it can be allocated again ! */
+ last_block_allocated = Index;
+
+ return(FREE_SUCCESS);
+}
+
+
+
+/*******************************************************************
+ writes info on just one Index
+ it must not be un allocated to do this
+ ********************************************************************/
+static void mem_write_Index_info(int Index,FILE *outfile)
+{
+ if (memory_blocks[Index].status != S_UNALLOCATED)
+ fprintf(outfile,"block %d file %s(%d) : size %d, alloc size %d, status %s\n",
+ Index,memory_blocks[Index].file,memory_blocks[Index].line,
+ memory_blocks[Index].present_size,
+ memory_blocks[Index].allocated_size,
+ status_to_str(memory_blocks[Index].status));
+}
+
+
+
+/*******************************************************************
+ writes info on one pointer
+ ********************************************************************/
+void smb_mem_write_info(void *ptr,FILE *outfile)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return;
+ mem_write_Index_info(Index,outfile);
+}
+
+
+
+
+/*******************************************************************
+ return the size of the mem block
+ ********************************************************************/
+size_t smb_mem_query_size(void *ptr)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return(0);
+ return(memory_blocks[Index].present_size);
+}
+
+/*******************************************************************
+ return the allocated size of the mem block
+ ********************************************************************/
+size_t smb_mem_query_real_size(void *ptr)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return(0);
+ return(memory_blocks[Index].allocated_size);
+}
+
+
+
+
+/*******************************************************************
+ return the file of caller of the mem block
+ ********************************************************************/
+char *smb_mem_query_file(void *ptr)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return(NULL);
+ return(memory_blocks[Index].file);
+}
+
+
+
+/*******************************************************************
+ return the line in the file of caller of the mem block
+ ********************************************************************/
+int smb_mem_query_line(void *ptr)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return(0);
+ return(memory_blocks[Index].line);
+}
+
+/*******************************************************************
+ return True if the pointer is OK
+ ********************************************************************/
+int smb_mem_test(void *ptr)
+{
+ int Index;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+ if (Index<0) return(False);
+
+ return(mem_buffer_ok(Index));
+}
+
+
+/*******************************************************************
+ write brief info on mem status
+ ********************************************************************/
+void smb_mem_write_status(FILE *outfile)
+{
+ int num_allocated=0;
+ int total_size=0;
+ int total_alloc_size=0;
+ int num_errors=0;
+ int i;
+ INIT_MANAGER();
+ mem_check_buffers();
+ for (i=0;i<mem_blocks_allocated;i++)
+ switch (memory_blocks[i].status)
+ {
+ case S_UNALLOCATED :
+ break;
+ case S_ALLOCATED :
+ num_allocated++;
+ total_size += memory_blocks[i].present_size;
+ total_alloc_size += memory_blocks[i].allocated_size;
+ break;
+ case S_ERROR_UNALLOCATED :
+ case S_ERROR_FREEING :
+ case S_CORRUPT_BACK :
+ case S_CORRUPT_FRONT :
+ num_errors++;
+ break;
+ }
+
+ fprintf(outfile,
+ "Mem Manager : %d blocks, allocation %dK, real allocation %dK, %d errors\n",
+ num_allocated,(int)(total_size/1024),(int)(total_alloc_size/1024),
+ num_errors);
+ fflush(outfile);
+}
+
+
+/*******************************************************************
+ write verbose info on allocated blocks
+ ********************************************************************/
+void smb_mem_write_verbose(FILE *outfile)
+{
+ int Index;
+ /* first write a summary */
+ INIT_MANAGER();
+ smb_mem_write_status(outfile);
+
+ /* just loop writing info on relevant indices */
+ for (Index=0;Index<mem_blocks_allocated;Index++)
+ if (memory_blocks[Index].status != S_UNALLOCATED)
+ mem_write_Index_info(Index,outfile);
+}
+
+/*******************************************************************
+ write verbose info on error blocks
+ ********************************************************************/
+void smb_mem_write_errors(FILE *outfile)
+{
+ int Index;
+ INIT_MANAGER();
+ mem_check_buffers();
+ /* just loop writing info on relevant indices */
+ for (Index=0;Index<mem_blocks_allocated;Index++)
+ if (((int)memory_blocks[Index].status) > ((int)S_ALLOCATED))
+ mem_write_Index_info(Index,outfile);
+}
+
+
+/*******************************************************************
+ sets the memory multiplier
+ ********************************************************************/
+void smb_mem_set_multiplier(int multiplier)
+{
+ /* check it is valid */
+ if (multiplier < 1) return;
+ mem_multiplier = multiplier;
+}
+
+/*******************************************************************
+ increases or decreases the memory assigned to a pointer
+ ********************************************************************/
+void *smb_mem_resize(void *ptr,size_t newsize)
+{
+ int Index;
+ size_t allocsize;
+ void *temp_ptr;
+ INIT_MANAGER();
+ Index = mem_find_Index(ptr);
+
+ /* if invalid return NULL */
+ if (Index<0)
+ {
+#ifdef BUG
+ int Error();
+ Error("Invalid mem_resize to size %d\n",newsize);
+#endif
+ return(NULL);
+ }
+
+ /* now - will it fit in the current allocation ? */
+ if (newsize <= memory_blocks[Index].allocated_size)
+ {
+ memory_blocks[Index].present_size = newsize;
+ mem_fill_buffer(Index);
+ return(ptr);
+ }
+
+ /* can it be allocated ? */
+ allocsize = newsize*mem_multiplier;
+ temp_ptr = malloc(newsize*mem_multiplier + BUF_SIZE);
+
+ /* no? try with just the size asked for */
+ if (temp_ptr == NULL)
+ {
+ allocsize=newsize;
+ temp_ptr = malloc(newsize + BUF_SIZE);
+ }
+
+ /* if it's still NULL give up */
+ if (temp_ptr == NULL)
+ return(NULL);
+
+ /* copy the old data to the new memory area */
+ memcpy(temp_ptr,(char *)memory_blocks[Index].pointer - BUF_OFFSET,
+ memory_blocks[Index].allocated_size + BUF_SIZE);
+
+ /* fill the extra space */
+ mem_fill_bytes((char *)temp_ptr + BUF_OFFSET + memory_blocks[Index].present_size,newsize - memory_blocks[Index].present_size,Index);
+
+
+ /* free the old mem and set vars */
+ free((char *)ptr - BUF_OFFSET);
+ memory_blocks[Index].pointer = (void *)((char *)temp_ptr + BUF_OFFSET);
+ memory_blocks[Index].present_size = newsize;
+ memory_blocks[Index].allocated_size = allocsize;
+
+ /* fill the buffer appropriately */
+ mem_fill_buffer(Index);
+
+
+ /* now return the new pointer */
+ return((char *)temp_ptr + BUF_OFFSET);
+}
+
+#else
+ void dummy_mem_man(void) {}
+#endif