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