- xmlmemory.[ch] commiting the files finally
[baserock-morphs:libxml2.git] / xmlmemory.c
1 /*
2  * memory.c:  libxml memory allocator wrapper.
3  *
4  * Daniel.Veillard@w3.org
5  */
6
7 #ifdef WIN32
8 #include "win32config.h"
9 #else
10 #include "config.h"
11 #endif
12
13 #include <stdio.h>
14 #include <string.h>
15
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #ifdef HAVE_TIME_H
20 #include <time.h>
21 #endif
22 #ifdef HAVE_MALLOC_H
23 #include <malloc.h>
24 #endif
25 #ifdef HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_CTYPE_H
29 #include <ctype.h>
30 #endif
31
32
33 #include "xmlmemory.h"
34
35 #ifdef xmlMalloc
36 #undef xmlMalloc
37 #endif
38 #ifdef xmlRealloc
39 #undef xmlRealloc
40 #endif
41 #ifdef xmlMemStrdup
42 #undef xmlMemStrdup
43 #endif
44
45
46 /*
47  * Each of the blocks allocated begin with a header containing informations
48  */
49
50 #define MEMTAG 0x5aa5
51
52 #define MALLOC_TYPE 1
53 #define REALLOC_TYPE 2
54 #define STRDUP_TYPE 3
55
56 typedef struct memnod {
57     unsigned int   mh_tag;
58     unsigned int   mh_type;
59     unsigned long  mh_number;
60     size_t         mh_size;
61 #ifdef MEM_LIST
62    struct memnod *mh_next;
63    struct memnod *mh_prev;
64 #endif
65    const char    *mh_file;
66    unsigned int   mh_line;
67 }  MEMHDR;
68
69
70 #ifdef SUN4
71 #define ALIGN_SIZE  16
72 #else
73 #define ALIGN_SIZE  sizeof(double)
74 #endif
75 #define HDR_SIZE    sizeof(MEMHDR)
76 #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
77                       / ALIGN_SIZE ) * ALIGN_SIZE)
78
79
80 #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
81 #define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))
82
83
84 static unsigned long  debugMemSize = 0;
85 static unsigned long  debugMaxMemSize = 0;
86 static int block=0;
87 int xmlMemStopAtBlock = 0;
88 int xmlMemInitialized = 0;
89 #ifdef MEM_LIST
90 static MEMHDR *memlist = NULL;
91 #endif
92
93 void debugmem_tag_error(void *addr);
94 #ifdef MEM_LIST
95 void  debugmem_list_add(MEMHDR *);
96 void debugmem_list_delete(MEMHDR *);
97 #endif
98 #define Mem_Tag_Err(a) debugmem_tag_error(a);
99
100 #ifndef TEST_POINT
101 #define TEST_POINT
102 #endif
103
104 /**
105  * xmlMallocBreakpoint:
106  *
107  * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
108  * number reaches the specified value this function is called. One need to add a breakpoint
109  * to it to get the context in which the given block is allocated.
110  */
111
112 void
113 xmlMallocBreakpoint(void) {
114     fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
115 }
116
117 /**
118  * xmlMallocLoc:
119  * @size:  an int specifying the size in byte to allocate.
120  * @file:  the file name or NULL
121   @file:  the line number
122  *
123  * a malloc() equivalent, with logging of the allocation info.
124  *
125  * Returns a pointer to the allocated area or NULL in case of lack of memory.
126  */
127
128 void *
129 xmlMallocLoc(int size, const char * file, int line)
130 {
131     MEMHDR *p;
132     
133     if (!xmlMemInitialized) xmlInitMemory();
134 #ifdef DEBUG_MEMORY
135     fprintf(stderr, "Malloc(%d)\n",size);
136 #endif
137
138     TEST_POINT
139     
140     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
141
142     if (!p) {
143         fprintf(stderr, "xmlMalloc : Out of free space\n");
144         xmlMemoryDump();
145         return(NULL);
146     }   
147     p->mh_tag = MEMTAG;
148     p->mh_number = ++block;
149     p->mh_size = size;
150     p->mh_type = MALLOC_TYPE;
151     p->mh_file = file;
152     p->mh_line = line;
153     debugMemSize += size;
154     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
155 #ifdef MEM_LIST
156     debugmem_list_add(p);
157 #endif
158
159 #ifdef DEBUG_MEMORY
160     fprintf(stderr, "Malloc(%d) Ok\n",size);
161 #endif
162     
163     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
164
165     TEST_POINT
166
167     return(HDR_2_CLIENT(p));
168 }
169
170 /**
171  * xmlMemMalloc:
172  * @size:  an int specifying the size in byte to allocate.
173  *
174  * a malloc() equivalent, with logging of the allocation info.
175  *
176  * Returns a pointer to the allocated area or NULL in case of lack of memory.
177  */
178
179 void *
180 xmlMemMalloc(int size)
181 {
182     return(xmlMallocLoc(size, "none", 0));
183 }
184
185 /**
186  * xmlReallocLoc:
187  * @ptr:  the initial memory block pointer
188  * @size:  an int specifying the size in byte to allocate.
189  * @file:  the file name or NULL
190  * @file:  the line number
191  *
192  * a realloc() equivalent, with logging of the allocation info.
193  *
194  * Returns a pointer to the allocated area or NULL in case of lack of memory.
195  */
196
197 void *
198 xmlReallocLoc(void *ptr,int size, const char * file, int line)
199 {
200     MEMHDR *p;
201     unsigned long number;
202
203     if (!xmlMemInitialized) xmlInitMemory();
204     TEST_POINT
205
206     p = CLIENT_2_HDR(ptr);
207     number = p->mh_number;
208     if (p->mh_tag != MEMTAG) {
209        Mem_Tag_Err(p);
210          goto error;
211     }
212     p->mh_tag = ~MEMTAG;
213     debugMemSize -= p->mh_size;
214 #ifdef MEM_LIST
215     debugmem_list_delete(p);
216 #endif
217
218     p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
219     if (!p) {
220          goto error;
221     }
222     p->mh_tag = MEMTAG;
223     p->mh_number = number;
224     p->mh_type = REALLOC_TYPE;
225     p->mh_size = size;
226     p->mh_file = file;
227     p->mh_line = line;
228     debugMemSize += size;
229     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
230 #ifdef MEM_LIST
231     debugmem_list_add(p);
232 #endif
233
234     TEST_POINT
235
236     return(HDR_2_CLIENT(p));
237     
238 error:    
239     return(NULL);
240 }
241
242 /**
243  * xmlMemRealloc:
244  * @ptr:  the initial memory block pointer
245  * @size:  an int specifying the size in byte to allocate.
246  *
247  * a realloc() equivalent, with logging of the allocation info.
248  *
249  * Returns a pointer to the allocated area or NULL in case of lack of memory.
250  */
251
252 void *
253 xmlMemRealloc(void *ptr,int size) {
254     return(xmlReallocLoc(ptr, size, "none", 0));
255 }
256
257 /**
258  * xmlMemFree:
259  * @ptr:  the memory block pointer
260  *
261  * a free() equivalent, with error checking.
262  */
263 void
264 xmlMemFree(void *ptr)
265 {
266     MEMHDR *p;
267
268     TEST_POINT
269
270     p = CLIENT_2_HDR(ptr);
271     if (p->mh_tag != MEMTAG) {
272        Mem_Tag_Err(p);
273        goto error;
274     }
275     p->mh_tag = ~MEMTAG;
276     debugMemSize -= p->mh_size;
277
278 #ifdef MEM_LIST
279     debugmem_list_delete(p);
280 #endif
281     free(p);
282
283     TEST_POINT
284
285     return;
286     
287 error:    
288     fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
289     return;
290 }
291
292 /**
293  * xmlMemStrdupLoc:
294  * @ptr:  the initial string pointer
295  * @file:  the file name or NULL
296  * @file:  the line number
297  *
298  * a strdup() equivalent, with logging of the allocation info.
299  *
300  * Returns a pointer to the new string or NULL if allocation error occured.
301  */
302
303 char *
304 xmlMemStrdupLoc(const char *str, const char *file, int line)
305 {
306     char *s;
307     size_t size = strlen(str) + 1;
308     MEMHDR *p;
309
310     if (!xmlMemInitialized) xmlInitMemory();
311     TEST_POINT
312
313     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
314     if (!p) {
315       goto error;
316     }
317     p->mh_tag = MEMTAG;
318     p->mh_number = ++block;
319     p->mh_size = size;
320     p->mh_type = STRDUP_TYPE;
321     p->mh_file = file;
322     p->mh_line = line;
323     debugMemSize += size;
324     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
325 #ifdef MEM_LIST
326     debugmem_list_add(p);
327 #endif
328     s = HDR_2_CLIENT(p);
329     
330     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
331
332     if (s != NULL)
333       strcpy(s,str);
334     else
335       goto error;
336     
337     TEST_POINT
338
339     return(s);
340
341 error:
342     return(NULL);
343 }
344
345 /**
346  * xmlMemoryStrdup:
347  * @ptr:  the initial string pointer
348  *
349  * a strdup() equivalent, with logging of the allocation info.
350  *
351  * Returns a pointer to the new string or NULL if allocation error occured.
352  */
353
354 char *
355 xmlMemoryStrdup(const char *str) {
356     return(xmlMemStrdupLoc(str, "none", 0));
357 }
358
359 /**
360  * xmlMemUsed:
361  *
362  * returns the amount of memory currenly allocated
363  *
364  * Returns an int representing the amount of memory allocated.
365  */
366
367 int
368 xmlMemUsed(void) {
369      return(debugMemSize);
370 }
371
372 #ifdef MEM_LIST
373 /**
374  * xmlMemContentShow:
375  * @fp:  a FILE descriptor used as the output file
376  * @p:  a memory block header
377  *
378  * tries to show some content from the memory block
379  */
380
381 void
382 xmlMemContentShow(FILE *fp, MEMHDR *p)
383 {
384     int i,j,len = p->mh_size;
385     const char *buf = HDR_2_CLIENT(p);
386
387     for (i = 0;i < len;i++) {
388         if (buf[i] == 0) break;
389         if (!isprint(buf[i])) break;
390     }
391     if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
392         if (len >= 4) {
393             MEMHDR *q;
394             void *cur;
395
396             for (j = 0;j < len -3;j += 4) {
397                 cur = *((void **) &buf[j]);
398                 q = CLIENT_2_HDR(cur);
399                 p = memlist;
400                 while (p != NULL) {
401                     if (p == q) break;
402                     p = p->mh_next;
403                 }
404                 if (p == q) {
405                     fprintf(fp, " pointer to #%lu at index %d",
406                             p->mh_number, j);
407                     return;
408                 }
409             }
410         }
411     } else if ((i == 0) && (buf[i] == 0)) {
412         fprintf(fp," null");
413     } else {
414         if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf); 
415         else {
416             fprintf(fp," [");
417             for (j = 0;j < i;j++)
418                 fprintf(fp,"%c", buf[j]);
419             fprintf(fp,"]");
420         }
421     }
422 }
423 #endif
424
425 /**
426  * xmlMemShow:
427  * @fp:  a FILE descriptor used as the output file
428  * @nr:  number of entries to dump
429  *
430  * show a show display of the memory allocated, and dump
431  * the @nr last allocated areas which were not freed
432  */
433
434 void
435 xmlMemShow(FILE *fp, int nr)
436 {
437 #ifdef MEM_LIST
438     MEMHDR *p;
439 #endif
440
441     if (fp != NULL)
442         fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
443                 debugMemSize, debugMaxMemSize);
444 #ifdef MEM_LIST
445     if (nr > 0) {
446         fprintf(fp,"NUMBER   SIZE  TYPE   WHERE\n");
447         p = memlist;
448         while ((p) && nr > 0) {
449               fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
450             switch (p->mh_type) {
451                case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
452                case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
453               case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
454                         default:fprintf(fp,"   ???    in ");break;
455             }
456             if (p->mh_file != NULL)
457                 fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
458             if (p->mh_tag != MEMTAG)
459                 fprintf(fp,"  INVALID");
460             xmlMemContentShow(fp, p);
461             fprintf(fp,"\n");
462             nr--;
463             p = p->mh_next;
464         }
465     }
466 #endif /* MEM_LIST */    
467 }
468
469 /**
470  * xmlMemDisplay:
471  * @fp:  a FILE descriptor used as the output file, if NULL, the result is
472  *       written to the file .memorylist
473  *
474  * show in-extenso the memory blocks allocated
475  */
476
477 void
478 xmlMemDisplay(FILE *fp)
479 {
480 #ifdef MEM_LIST
481     MEMHDR *p;
482     int     idx;
483 #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
484     time_t currentTime;
485     char buf[500];
486     struct tm * tstruct;
487
488     currentTime = time(NULL);
489     tstruct = localtime(&currentTime);
490     strftime(buf, sizeof(buf) - 1, "%c", tstruct);
491     fprintf(fp,"      %s\n\n", buf);
492 #endif
493
494     
495     fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
496             debugMemSize, debugMaxMemSize);
497     fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
498     idx = 0;
499     p = memlist;
500     while (p) {
501           fprintf(fp,"%-5u  %6lu %6u ",idx++,p->mh_number,p->mh_size);
502         switch (p->mh_type) {
503            case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
504            case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
505           case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
506                     default:fprintf(fp,"   ???    in ");break;
507         }
508           if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
509         if (p->mh_tag != MEMTAG)
510               fprintf(fp,"  INVALID");
511         xmlMemContentShow(fp, p);
512         fprintf(fp,"\n");
513         p = p->mh_next;
514     }
515 #else
516     fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
517 #endif
518 }
519
520 #ifdef MEM_LIST
521
522 void debugmem_list_add(MEMHDR *p)
523 {
524      p->mh_next = memlist;
525      p->mh_prev = NULL;
526      if (memlist) memlist->mh_prev = p;
527      memlist = p;
528 #ifdef MEM_LIST_DEBUG
529      if (stderr)
530      Mem_Display(stderr);
531 #endif
532 }
533
534 void debugmem_list_delete(MEMHDR *p)
535 {
536      if (p->mh_next)
537      p->mh_next->mh_prev = p->mh_prev;
538      if (p->mh_prev)
539      p->mh_prev->mh_next = p->mh_next;
540      else memlist = p->mh_next;
541 #ifdef MEM_LIST_DEBUG
542      if (stderr)
543      Mem_Display(stderr);
544 #endif
545 }
546
547 #endif
548
549 /*
550  * debugmem_tag_error : internal error function.
551  */
552  
553 void debugmem_tag_error(void *p)
554 {
555      fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
556 #ifdef MEM_LIST
557      if (stderr)
558      xmlMemDisplay(stderr);
559 #endif
560 }
561
562 FILE *xmlMemoryDumpFile = NULL;
563
564
565 /**
566  * xmlMemoryDump:
567  *
568  * Dump in-extenso the memory blocks allocated to the file .memorylist
569  */
570
571 void
572 xmlMemoryDump(void)
573 {
574 #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
575     FILE *dump;
576
577     dump = fopen(".memdump", "w");
578     if (dump == NULL) xmlMemoryDumpFile = stdout;
579     else xmlMemoryDumpFile = dump;
580
581     xmlMemDisplay(xmlMemoryDumpFile);
582
583     if (dump != NULL) fclose(dump);
584 #endif
585 }
586
587
588 /****************************************************************
589  *                                                              *
590  *              Initialization Routines                         *
591  *                                                              *
592  ****************************************************************/
593
594 #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
595 xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree;
596 xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
597 xmlReallocFunc xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
598 xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
599 #else
600 xmlFreeFunc xmlFree = (xmlFreeFunc) free;
601 xmlMallocFunc xmlMalloc = (xmlMallocFunc) malloc;
602 xmlReallocFunc xmlRealloc = (xmlReallocFunc) realloc;
603 xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) strdup;
604 #endif
605
606 /**
607  * xmlInitMemory:
608  *
609  * Initialize the memory layer.
610  *
611  * Returns 0 on success
612  */
613
614 static int xmlInitMemoryDone = 0;
615
616 int
617 xmlInitMemory(void)
618 {
619      int ret;
620      
621 #ifdef HAVE_STDLIB_H
622      char *breakpoint;
623 #endif     
624
625      if (xmlInitMemoryDone) return(-1);
626
627 #ifdef HAVE_STDLIB_H
628      breakpoint = getenv("XML_MEM_BREAKPOINT");
629      if (breakpoint != NULL) {
630          sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
631      }
632 #endif     
633     
634 #ifdef DEBUG_MEMORY
635      fprintf(stderr, "xmlInitMemory() Ok\n");
636 #endif     
637      ret = 0;
638      return(ret);
639 }
640
641 /**
642  * xmlMemSetup:
643  * @freeFunc: the free() function to use
644  * @mallocFunc: the malloc() function to use
645  * @reallocFunc: the realloc() function to use
646  * @strdupFunc: the strdup() function to use
647  *
648  * Override the default memory access functions with a new set
649  * This has to be called before any other libxml routines !
650  *
651  * Should this be blocked if there was already some allocations
652  * done ?
653  *
654  * Returns 0 on success
655  */
656 int
657 xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
658             xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
659     if (freeFunc != NULL)
660         return(-1);
661     if (mallocFunc != NULL)
662         return(-1);
663     if (reallocFunc != NULL)
664         return(-1);
665     if (strdupFunc != NULL)
666         return(-1);
667     xmlFree = freeFunc;
668     xmlMalloc = mallocFunc;
669     xmlRealloc = reallocFunc;
670     xmlMemStrdup = strdupFunc;
671     return(0);
672 }
673
674 /**
675  * xmlMemGet:
676  * @freeFunc: the free() function in use
677  * @mallocFunc: the malloc() function in use
678  * @reallocFunc: the realloc() function in use
679  * @strdupFunc: the strdup() function in use
680  *
681  * Return the memory access functions set currently in use
682  *
683  * Returns 0 on success
684  */
685 int
686 xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
687           xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
688     if (freeFunc != NULL) *freeFunc = xmlFree;
689     if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
690     if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
691     if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
692     return(0);
693 }
694