34ae6fd43c126b03cf35a9a57977f06d8159e925
[kai/samba.git] / source3 / mem_man / mem_man.c
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
33 extern 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
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);
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
110 typedef 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 */
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};
126
127 /* here is the data memory */
128
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 */
136
137
138 typedef struct
139 {
140   int status;
141   char *label;
142 } stat_str_type;
143
144 static 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   ********************************************************************/
162 static 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   ********************************************************************/
180 static 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   ********************************************************************/
192 static 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   ********************************************************************/
206 static 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   ********************************************************************/
246 static 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   ********************************************************************/
262 static 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   ********************************************************************/
282 static 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   ********************************************************************/
290 static 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   ********************************************************************/
313 static 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   ********************************************************************/
348 static 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   ********************************************************************/
360 void *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 /*******************************************************************
411 dup a string
412   ********************************************************************/
413 char *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   ********************************************************************/
423 int 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   ********************************************************************/
501 static 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) : 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));
510 }
511
512
513
514 /*******************************************************************
515   writes info on one pointer  
516   ********************************************************************/
517 void smb_mem_write_info(void *ptr,FILE *outfile)
518 {
519   int Index;
520   INIT_MANAGER();
521   Index = mem_find_Index(ptr);
522   if (Index<0) return;
523   mem_write_Index_info(Index,outfile);
524 }
525
526
527
528
529 /*******************************************************************
530   return the size of the mem block 
531   ********************************************************************/
532 size_t smb_mem_query_size(void *ptr)
533 {
534   int Index;
535   INIT_MANAGER();
536   Index = mem_find_Index(ptr);
537   if (Index<0) return(0);
538   return(memory_blocks[Index].present_size);
539 }
540
541 /*******************************************************************
542   return the allocated size of the mem block 
543   ********************************************************************/
544 size_t smb_mem_query_real_size(void *ptr)
545 {
546   int Index;
547   INIT_MANAGER();
548   Index = mem_find_Index(ptr);
549   if (Index<0) return(0);
550   return(memory_blocks[Index].allocated_size);
551 }
552
553
554
555
556 /*******************************************************************
557   return the file of caller of the mem block 
558   ********************************************************************/
559 char *smb_mem_query_file(void *ptr)
560 {
561   int Index;
562   INIT_MANAGER();
563   Index = mem_find_Index(ptr);
564   if (Index<0) return(NULL);
565   return(memory_blocks[Index].file);
566 }
567
568
569
570 /*******************************************************************
571   return the line in the file of caller of the mem block 
572   ********************************************************************/
573 int smb_mem_query_line(void *ptr)
574 {
575   int Index;
576   INIT_MANAGER();
577   Index = mem_find_Index(ptr);
578   if (Index<0) return(0);
579   return(memory_blocks[Index].line);
580 }
581
582 /*******************************************************************
583   return True if the pointer is OK
584   ********************************************************************/
585 int smb_mem_test(void *ptr)
586 {
587   int Index;
588   INIT_MANAGER();
589   Index = mem_find_Index(ptr);
590   if (Index<0) return(False);
591
592   return(mem_buffer_ok(Index));
593 }
594
595
596 /*******************************************************************
597   write brief info on mem status 
598   ********************************************************************/
599 void smb_mem_write_status(FILE *outfile)
600 {
601   int num_allocated=0;
602   int total_size=0;
603   int total_alloc_size=0;
604   int num_errors=0;
605   int i;
606   INIT_MANAGER();
607   mem_check_buffers();
608   for (i=0;i<mem_blocks_allocated;i++)
609     switch (memory_blocks[i].status)
610       {
611       case S_UNALLOCATED :
612         break;
613       case S_ALLOCATED :
614         num_allocated++;
615         total_size += memory_blocks[i].present_size;
616         total_alloc_size += memory_blocks[i].allocated_size;
617         break;
618       case S_ERROR_UNALLOCATED :
619       case S_ERROR_FREEING :
620       case S_CORRUPT_BACK :
621       case S_CORRUPT_FRONT :
622         num_errors++;
623         break;
624       }
625   
626   fprintf(outfile,
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),
629           num_errors);
630   fflush(outfile);
631 }
632
633
634 /*******************************************************************
635   write verbose info on allocated blocks 
636   ********************************************************************/
637 void smb_mem_write_verbose(FILE *outfile)
638 {
639   int Index;
640   /* first write a summary */
641   INIT_MANAGER();
642   smb_mem_write_status(outfile);
643   
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);
648 }
649
650 /*******************************************************************
651   write verbose info on error blocks 
652   ********************************************************************/
653 void smb_mem_write_errors(FILE *outfile)
654 {
655   int Index;
656   INIT_MANAGER();
657   mem_check_buffers();
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);
662 }
663
664
665 /*******************************************************************
666   sets the memory multiplier 
667   ********************************************************************/
668 void smb_mem_set_multiplier(int multiplier)
669 {
670   /* check it is valid */
671   if (multiplier < 1) return;
672   mem_multiplier = multiplier;
673 }
674
675 /*******************************************************************
676   increases or decreases the memory assigned to a pointer 
677   ********************************************************************/
678 void *smb_mem_resize(void *ptr,size_t newsize)
679 {
680   int Index;
681   size_t allocsize;
682   void *temp_ptr;
683   INIT_MANAGER();
684   Index = mem_find_Index(ptr);
685
686   /* if invalid return NULL */
687   if (Index<0) 
688     {
689 #ifdef BUG
690       int Error();
691       Error("Invalid mem_resize to size %d\n",newsize);
692 #endif
693       return(NULL);
694     }
695   
696   /* now - will it fit in the current allocation ? */
697   if (newsize <= memory_blocks[Index].allocated_size)
698     {
699       memory_blocks[Index].present_size = newsize;
700       mem_fill_buffer(Index);
701       return(ptr);
702     }
703
704   /* can it be allocated ? */
705   allocsize = newsize*mem_multiplier;
706   temp_ptr = malloc(newsize*mem_multiplier + BUF_SIZE);
707   
708   /* no? try with just the size asked for */
709   if (temp_ptr == NULL)
710     {
711       allocsize=newsize;
712       temp_ptr = malloc(newsize + BUF_SIZE);
713     }
714   
715   /* if it's still NULL give up */
716   if (temp_ptr == NULL)
717     return(NULL);
718   
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);
722   
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);
725   
726   
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;
732   
733   /* fill the buffer appropriately */
734   mem_fill_buffer(Index);
735   
736   
737   /* now return the new pointer */
738   return((char *)temp_ptr + BUF_OFFSET);
739 }
740
741 #else
742  void dummy_mem_man(void) {} 
743 #endif