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