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