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