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 /* #define DEBUG_MEMORY */
33 
34 /**
35  * MEM_LIST:
36  *
37  * keep track of all allocated blocks for error reporting
38  * Always build the memory list !
39  */
40 #ifdef DEBUG_MEMORY_LOCATION
41 #ifndef MEM_LIST
42 #define MEM_LIST /* keep a list of all the allocated memory blocks */
43 #endif
44 #endif
45 
46 #include <libxml/globals.h>	/* must come before xmlmemory.h */
47 #include <libxml/xmlmemory.h>
48 #include <libxml/xmlerror.h>
49 #include <libxml/threads.h>
50 
51 static int xmlMemInitialized = 0;
52 static unsigned long  debugMemSize = 0;
53 static unsigned long  debugMemBlocks = 0;
54 static unsigned long  debugMaxMemSize = 0;
55 static xmlMutexPtr xmlMemMutex = NULL;
56 
57 void xmlMallocBreakpoint(void);
58 
59 /************************************************************************
60  *									*
61  *		Macros, variables and associated types			*
62  *									*
63  ************************************************************************/
64 
65 #if !defined(LIBXML_THREAD_ENABLED) && !defined(LIBXML_THREAD_ALLOC_ENABLED)
66 #ifdef xmlMalloc
67 #undef xmlMalloc
68 #endif
69 #ifdef xmlRealloc
70 #undef xmlRealloc
71 #endif
72 #ifdef xmlMemStrdup
73 #undef xmlMemStrdup
74 #endif
75 #endif
76 
77 /*
78  * Each of the blocks allocated begin with a header containing informations
79  */
80 
81 #define MEMTAG 0x5aa5
82 
83 #define MALLOC_TYPE 1
84 #define REALLOC_TYPE 2
85 #define STRDUP_TYPE 3
86 #define MALLOC_ATOMIC_TYPE 4
87 #define REALLOC_ATOMIC_TYPE 5
88 
89 typedef struct memnod {
90     unsigned int   mh_tag;
91     unsigned int   mh_type;
92     unsigned long  mh_number;
93     size_t         mh_size;
94 #ifdef MEM_LIST
95    struct memnod *mh_next;
96    struct memnod *mh_prev;
97 #endif
98    const char    *mh_file;
99    unsigned int   mh_line;
100 }  MEMHDR;
101 
102 
103 #ifdef SUN4
104 #define ALIGN_SIZE  16
105 #else
106 #define ALIGN_SIZE  sizeof(double)
107 #endif
108 #define HDR_SIZE    sizeof(MEMHDR)
109 #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
110 		      / ALIGN_SIZE ) * ALIGN_SIZE)
111 
112 #define MAX_SIZE_T ((size_t)-1)
113 
114 #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
115 #define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))
116 
117 
118 static unsigned int block=0;
119 static unsigned int xmlMemStopAtBlock = 0;
120 static void *xmlMemTraceBlockAt = NULL;
121 #ifdef MEM_LIST
122 static MEMHDR *memlist = NULL;
123 #endif
124 
125 static void debugmem_tag_error(void *addr);
126 #ifdef MEM_LIST
127 static void  debugmem_list_add(MEMHDR *);
128 static void debugmem_list_delete(MEMHDR *);
129 #endif
130 #define Mem_Tag_Err(a) debugmem_tag_error(a);
131 
132 #ifndef TEST_POINT
133 #define TEST_POINT
134 #endif
135 
136 /**
137  * xmlMallocBreakpoint:
138  *
139  * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
140  * number reaches the specified value this function is called. One need to add a breakpoint
141  * to it to get the context in which the given block is allocated.
142  */
143 
144 void
xmlMallocBreakpoint(void)145 xmlMallocBreakpoint(void) {
146     xmlGenericError(xmlGenericErrorContext,
147 	    "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
148 }
149 
150 /**
151  * xmlMallocLoc:
152  * @size:  an int specifying the size in byte to allocate.
153  * @file:  the file name or NULL
154  * @line:  the line number
155  *
156  * a malloc() equivalent, with logging of the allocation info.
157  *
158  * Returns a pointer to the allocated area or NULL in case of lack of memory.
159  */
160 
161 void *
xmlMallocLoc(size_t size,const char * file,int line)162 xmlMallocLoc(size_t size, const char * file, int line)
163 {
164     MEMHDR *p;
165     void *ret;
166 
167     if (!xmlMemInitialized) xmlInitMemory();
168 #ifdef DEBUG_MEMORY
169     xmlGenericError(xmlGenericErrorContext,
170 	    "Malloc(%d)\n",size);
171 #endif
172 
173     TEST_POINT
174 
175     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
176 
177     if (!p) {
178 	xmlGenericError(xmlGenericErrorContext,
179 		"xmlMallocLoc : Out of free space\n");
180 	xmlMemoryDump();
181 	return(NULL);
182     }
183     p->mh_tag = MEMTAG;
184     p->mh_size = size;
185     p->mh_type = MALLOC_TYPE;
186     p->mh_file = file;
187     p->mh_line = line;
188     xmlMutexLock(xmlMemMutex);
189     p->mh_number = ++block;
190     debugMemSize += size;
191     debugMemBlocks++;
192     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
193 #ifdef MEM_LIST
194     debugmem_list_add(p);
195 #endif
196     xmlMutexUnlock(xmlMemMutex);
197 
198 #ifdef DEBUG_MEMORY
199     xmlGenericError(xmlGenericErrorContext,
200 	    "Malloc(%d) Ok\n",size);
201 #endif
202 
203     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
204 
205     ret = HDR_2_CLIENT(p);
206 
207     if (xmlMemTraceBlockAt == ret) {
208 	xmlGenericError(xmlGenericErrorContext,
209 			"%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt,
210 			(long unsigned)size);
211 	xmlMallocBreakpoint();
212     }
213 
214     TEST_POINT
215 
216     return(ret);
217 }
218 
219 /**
220  * xmlMallocAtomicLoc:
221  * @size:  an unsigned int specifying the size in byte to allocate.
222  * @file:  the file name or NULL
223  * @line:  the line number
224  *
225  * a malloc() equivalent, with logging of the allocation info.
226  *
227  * Returns a pointer to the allocated area or NULL in case of lack of memory.
228  */
229 
230 void *
xmlMallocAtomicLoc(size_t size,const char * file,int line)231 xmlMallocAtomicLoc(size_t size, const char * file, int line)
232 {
233     MEMHDR *p;
234     void *ret;
235 
236     if (!xmlMemInitialized) xmlInitMemory();
237 #ifdef DEBUG_MEMORY
238     xmlGenericError(xmlGenericErrorContext,
239 	    "Malloc(%d)\n",size);
240 #endif
241 
242     TEST_POINT
243 
244     if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
245 	xmlGenericError(xmlGenericErrorContext,
246 		"xmlMallocAtomicLoc : Unsigned overflow prevented\n");
247 	xmlMemoryDump();
248 	return(NULL);
249     }
250 
251     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
252 
253     if (!p) {
254 	xmlGenericError(xmlGenericErrorContext,
255 		"xmlMallocAtomicLoc : Out of free space\n");
256 	xmlMemoryDump();
257 	return(NULL);
258     }
259     p->mh_tag = MEMTAG;
260     p->mh_size = size;
261     p->mh_type = MALLOC_ATOMIC_TYPE;
262     p->mh_file = file;
263     p->mh_line = line;
264     xmlMutexLock(xmlMemMutex);
265     p->mh_number = ++block;
266     debugMemSize += size;
267     debugMemBlocks++;
268     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
269 #ifdef MEM_LIST
270     debugmem_list_add(p);
271 #endif
272     xmlMutexUnlock(xmlMemMutex);
273 
274 #ifdef DEBUG_MEMORY
275     xmlGenericError(xmlGenericErrorContext,
276 	    "Malloc(%d) Ok\n",size);
277 #endif
278 
279     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
280 
281     ret = HDR_2_CLIENT(p);
282 
283     if (xmlMemTraceBlockAt == ret) {
284 	xmlGenericError(xmlGenericErrorContext,
285 			"%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt,
286 			(long unsigned)size);
287 	xmlMallocBreakpoint();
288     }
289 
290     TEST_POINT
291 
292     return(ret);
293 }
294 /**
295  * xmlMemMalloc:
296  * @size:  an int specifying the size in byte to allocate.
297  *
298  * a malloc() equivalent, with logging of the allocation info.
299  *
300  * Returns a pointer to the allocated area or NULL in case of lack of memory.
301  */
302 
303 void *
xmlMemMalloc(size_t size)304 xmlMemMalloc(size_t size)
305 {
306     return(xmlMallocLoc(size, "none", 0));
307 }
308 
309 /**
310  * xmlReallocLoc:
311  * @ptr:  the initial memory block pointer
312  * @size:  an int specifying the size in byte to allocate.
313  * @file:  the file name or NULL
314  * @line:  the line number
315  *
316  * a realloc() equivalent, with logging of the allocation info.
317  *
318  * Returns a pointer to the allocated area or NULL in case of lack of memory.
319  */
320 
321 void *
xmlReallocLoc(void * ptr,size_t size,const char * file,int line)322 xmlReallocLoc(void *ptr,size_t size, const char * file, int line)
323 {
324     MEMHDR *p, *tmp;
325     unsigned long number;
326 #ifdef DEBUG_MEMORY
327     size_t oldsize;
328 #endif
329 
330     if (ptr == NULL)
331         return(xmlMallocLoc(size, file, line));
332 
333     if (!xmlMemInitialized) xmlInitMemory();
334     TEST_POINT
335 
336     p = CLIENT_2_HDR(ptr);
337     number = p->mh_number;
338     if (xmlMemStopAtBlock == number) xmlMallocBreakpoint();
339     if (p->mh_tag != MEMTAG) {
340        Mem_Tag_Err(p);
341 	 goto error;
342     }
343     p->mh_tag = ~MEMTAG;
344     xmlMutexLock(xmlMemMutex);
345     debugMemSize -= p->mh_size;
346     debugMemBlocks--;
347 #ifdef DEBUG_MEMORY
348     oldsize = p->mh_size;
349 #endif
350 #ifdef MEM_LIST
351     debugmem_list_delete(p);
352 #endif
353     xmlMutexUnlock(xmlMemMutex);
354 
355     tmp = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
356     if (!tmp) {
357 	 free(p);
358 	 goto error;
359     }
360     p = tmp;
361     if (xmlMemTraceBlockAt == ptr) {
362 	xmlGenericError(xmlGenericErrorContext,
363 			"%p : Realloced(%lu -> %lu) Ok\n",
364 			xmlMemTraceBlockAt, (long unsigned)p->mh_size,
365 			(long unsigned)size);
366 	xmlMallocBreakpoint();
367     }
368     p->mh_tag = MEMTAG;
369     p->mh_number = number;
370     p->mh_type = REALLOC_TYPE;
371     p->mh_size = size;
372     p->mh_file = file;
373     p->mh_line = line;
374     xmlMutexLock(xmlMemMutex);
375     debugMemSize += size;
376     debugMemBlocks++;
377     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
378 #ifdef MEM_LIST
379     debugmem_list_add(p);
380 #endif
381     xmlMutexUnlock(xmlMemMutex);
382 
383     TEST_POINT
384 
385 #ifdef DEBUG_MEMORY
386     xmlGenericError(xmlGenericErrorContext,
387 	    "Realloced(%d to %d) Ok\n", oldsize, size);
388 #endif
389     return(HDR_2_CLIENT(p));
390 
391 error:
392     return(NULL);
393 }
394 
395 /**
396  * xmlMemRealloc:
397  * @ptr:  the initial memory block pointer
398  * @size:  an int specifying the size in byte to allocate.
399  *
400  * a realloc() equivalent, with logging of the allocation info.
401  *
402  * Returns a pointer to the allocated area or NULL in case of lack of memory.
403  */
404 
405 void *
xmlMemRealloc(void * ptr,size_t size)406 xmlMemRealloc(void *ptr,size_t size) {
407     return(xmlReallocLoc(ptr, size, "none", 0));
408 }
409 
410 /**
411  * xmlMemFree:
412  * @ptr:  the memory block pointer
413  *
414  * a free() equivalent, with error checking.
415  */
416 void
xmlMemFree(void * ptr)417 xmlMemFree(void *ptr)
418 {
419     MEMHDR *p;
420     char *target;
421 #ifdef DEBUG_MEMORY
422     size_t size;
423 #endif
424 
425     if (ptr == NULL)
426 	return;
427 
428     if (ptr == (void *) -1) {
429 	xmlGenericError(xmlGenericErrorContext,
430 	    "trying to free pointer from freed area\n");
431         goto error;
432     }
433 
434     if (xmlMemTraceBlockAt == ptr) {
435 	xmlGenericError(xmlGenericErrorContext,
436 			"%p : Freed()\n", xmlMemTraceBlockAt);
437 	xmlMallocBreakpoint();
438     }
439 
440     TEST_POINT
441 
442     target = (char *) ptr;
443 
444     p = CLIENT_2_HDR(ptr);
445     if (p->mh_tag != MEMTAG) {
446         Mem_Tag_Err(p);
447         goto error;
448     }
449     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
450     p->mh_tag = ~MEMTAG;
451     memset(target, -1, p->mh_size);
452     xmlMutexLock(xmlMemMutex);
453     debugMemSize -= p->mh_size;
454     debugMemBlocks--;
455 #ifdef DEBUG_MEMORY
456     size = p->mh_size;
457 #endif
458 #ifdef MEM_LIST
459     debugmem_list_delete(p);
460 #endif
461     xmlMutexUnlock(xmlMemMutex);
462 
463     free(p);
464 
465     TEST_POINT
466 
467 #ifdef DEBUG_MEMORY
468     xmlGenericError(xmlGenericErrorContext,
469 	    "Freed(%d) Ok\n", size);
470 #endif
471 
472     return;
473 
474 error:
475     xmlGenericError(xmlGenericErrorContext,
476 	    "xmlMemFree(%lX) error\n", (unsigned long) ptr);
477     xmlMallocBreakpoint();
478     return;
479 }
480 
481 /**
482  * xmlMemStrdupLoc:
483  * @str:  the initial string pointer
484  * @file:  the file name or NULL
485  * @line:  the line number
486  *
487  * a strdup() equivalent, with logging of the allocation info.
488  *
489  * Returns a pointer to the new string or NULL if allocation error occurred.
490  */
491 
492 char *
xmlMemStrdupLoc(const char * str,const char * file,int line)493 xmlMemStrdupLoc(const char *str, const char *file, int line)
494 {
495     char *s;
496     size_t size = strlen(str) + 1;
497     MEMHDR *p;
498 
499     if (!xmlMemInitialized) xmlInitMemory();
500     TEST_POINT
501 
502     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
503     if (!p) {
504       goto error;
505     }
506     p->mh_tag = MEMTAG;
507     p->mh_size = size;
508     p->mh_type = STRDUP_TYPE;
509     p->mh_file = file;
510     p->mh_line = line;
511     xmlMutexLock(xmlMemMutex);
512     p->mh_number = ++block;
513     debugMemSize += size;
514     debugMemBlocks++;
515     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
516 #ifdef MEM_LIST
517     debugmem_list_add(p);
518 #endif
519     xmlMutexUnlock(xmlMemMutex);
520 
521     s = (char *) HDR_2_CLIENT(p);
522 
523     if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
524 
525     strcpy(s,str);
526 
527     TEST_POINT
528 
529     if (xmlMemTraceBlockAt == s) {
530 	xmlGenericError(xmlGenericErrorContext,
531 			"%p : Strdup() Ok\n", xmlMemTraceBlockAt);
532 	xmlMallocBreakpoint();
533     }
534 
535     return(s);
536 
537 error:
538     return(NULL);
539 }
540 
541 /**
542  * xmlMemoryStrdup:
543  * @str:  the initial string pointer
544  *
545  * a strdup() equivalent, with logging of the allocation info.
546  *
547  * Returns a pointer to the new string or NULL if allocation error occurred.
548  */
549 
550 char *
xmlMemoryStrdup(const char * str)551 xmlMemoryStrdup(const char *str) {
552     return(xmlMemStrdupLoc(str, "none", 0));
553 }
554 
555 /**
556  * xmlMemUsed:
557  *
558  * Provides the amount of memory currently allocated
559  *
560  * Returns an int representing the amount of memory allocated.
561  */
562 
563 int
xmlMemUsed(void)564 xmlMemUsed(void) {
565     int res;
566 
567     xmlMutexLock(xmlMemMutex);
568     res = debugMemSize;
569     xmlMutexUnlock(xmlMemMutex);
570     return(res);
571 }
572 
573 /**
574  * xmlMemBlocks:
575  *
576  * Provides the number of memory areas currently allocated
577  *
578  * Returns an int representing the number of blocks
579  */
580 
581 int
xmlMemBlocks(void)582 xmlMemBlocks(void) {
583     int res;
584 
585     xmlMutexLock(xmlMemMutex);
586     res = debugMemBlocks;
587     xmlMutexUnlock(xmlMemMutex);
588     return(res);
589 }
590 
591 #ifdef MEM_LIST
592 /**
593  * xmlMemContentShow:
594  * @fp:  a FILE descriptor used as the output file
595  * @p:  a memory block header
596  *
597  * tries to show some content from the memory block
598  */
599 
600 static void
xmlMemContentShow(FILE * fp,MEMHDR * p)601 xmlMemContentShow(FILE *fp, MEMHDR *p)
602 {
603     int i,j,k,len;
604     const char *buf;
605 
606     if (p == NULL) {
607 	fprintf(fp, " NULL");
608 	return;
609     }
610     len = p->mh_size;
611     buf = (const char *) HDR_2_CLIENT(p);
612 
613     for (i = 0;i < len;i++) {
614         if (buf[i] == 0) break;
615 	if (!isprint((unsigned char) buf[i])) break;
616     }
617     if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
618         if (len >= 4) {
619 	    MEMHDR *q;
620 	    void *cur;
621 
622             for (j = 0;(j < len -3) && (j < 40);j += 4) {
623 		cur = *((void **) &buf[j]);
624 		q = CLIENT_2_HDR(cur);
625 		p = memlist;
626 		k = 0;
627 		while (p != NULL) {
628 		    if (p == q) break;
629 		    p = p->mh_next;
630 		    if (k++ > 100) break;
631 		}
632 		if ((p != NULL) && (p == q)) {
633 		    fprintf(fp, " pointer to #%lu at index %d",
634 		            p->mh_number, j);
635 		    return;
636 		}
637 	    }
638 	}
639     } else if ((i == 0) && (buf[i] == 0)) {
640         fprintf(fp," null");
641     } else {
642         if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
643 	else {
644             fprintf(fp," [");
645 	    for (j = 0;j < i;j++)
646                 fprintf(fp,"%c", buf[j]);
647             fprintf(fp,"]");
648 	}
649     }
650 }
651 #endif
652 
653 /**
654  * xmlMemDisplayLast:
655  * @fp:  a FILE descriptor used as the output file, if NULL, the result is
656  *       written to the file .memorylist
657  * @nbBytes: the amount of memory to dump
658  *
659  * the last nbBytes of memory allocated and not freed, useful for dumping
660  * the memory left allocated between two places at runtime.
661  */
662 
663 void
xmlMemDisplayLast(FILE * fp,long nbBytes)664 xmlMemDisplayLast(FILE *fp, long nbBytes)
665 {
666 #ifdef MEM_LIST
667     MEMHDR *p;
668     unsigned idx;
669     int     nb = 0;
670 #endif
671     FILE *old_fp = fp;
672 
673     if (nbBytes <= 0)
674         return;
675 
676     if (fp == NULL) {
677 	fp = fopen(".memorylist", "w");
678 	if (fp == NULL)
679 	    return;
680     }
681 
682 #ifdef MEM_LIST
683     fprintf(fp,"   Last %li MEMORY ALLOCATED : %lu, MAX was %lu\n",
684             nbBytes, debugMemSize, debugMaxMemSize);
685     fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
686     idx = 0;
687     xmlMutexLock(xmlMemMutex);
688     p = memlist;
689     while ((p) && (nbBytes > 0)) {
690 	  fprintf(fp,"%-5u  %6lu %6lu ",idx++,p->mh_number,
691 		  (unsigned long)p->mh_size);
692         switch (p->mh_type) {
693            case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
694            case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
695            case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
696            case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
697            case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
698            default:
699 	        fprintf(fp,"Unknown memory block, may be corrupted");
700 		xmlMutexUnlock(xmlMemMutex);
701 		if (old_fp == NULL)
702 		    fclose(fp);
703 		return;
704         }
705 	if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
706         if (p->mh_tag != MEMTAG)
707 	      fprintf(fp,"  INVALID");
708         nb++;
709 	if (nb < 100)
710 	    xmlMemContentShow(fp, p);
711 	else
712 	    fprintf(fp," skip");
713 
714         fprintf(fp,"\n");
715 	nbBytes -= (unsigned long)p->mh_size;
716         p = p->mh_next;
717     }
718     xmlMutexUnlock(xmlMemMutex);
719 #else
720     fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
721 #endif
722     if (old_fp == NULL)
723 	fclose(fp);
724 }
725 
726 /**
727  * xmlMemDisplay:
728  * @fp:  a FILE descriptor used as the output file, if NULL, the result is
729  *       written to the file .memorylist
730  *
731  * show in-extenso the memory blocks allocated
732  */
733 
734 void
xmlMemDisplay(FILE * fp)735 xmlMemDisplay(FILE *fp)
736 {
737 #ifdef MEM_LIST
738     MEMHDR *p;
739     unsigned idx;
740     int     nb = 0;
741 #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
742     time_t currentTime;
743     char buf[500];
744     struct tm * tstruct;
745 #endif
746 #endif
747     FILE *old_fp = fp;
748 
749     if (fp == NULL) {
750 	fp = fopen(".memorylist", "w");
751 	if (fp == NULL)
752 	    return;
753     }
754 
755 #ifdef MEM_LIST
756 #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
757     currentTime = time(NULL);
758     tstruct = localtime(&currentTime);
759     strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p", tstruct);
760     fprintf(fp,"      %s\n\n", buf);
761 #endif
762 
763 
764     fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
765             debugMemSize, debugMaxMemSize);
766     fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
767     idx = 0;
768     xmlMutexLock(xmlMemMutex);
769     p = memlist;
770     while (p) {
771 	  fprintf(fp,"%-5u  %6lu %6lu ",idx++,p->mh_number,
772 		  (unsigned long)p->mh_size);
773         switch (p->mh_type) {
774            case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
775            case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
776            case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
777            case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
778            case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
779            default:
780 	        fprintf(fp,"Unknown memory block, may be corrupted");
781 		xmlMutexUnlock(xmlMemMutex);
782 		if (old_fp == NULL)
783 		    fclose(fp);
784 		return;
785         }
786 	if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
787         if (p->mh_tag != MEMTAG)
788 	      fprintf(fp,"  INVALID");
789         nb++;
790 	if (nb < 100)
791 	    xmlMemContentShow(fp, p);
792 	else
793 	    fprintf(fp," skip");
794 
795         fprintf(fp,"\n");
796         p = p->mh_next;
797     }
798     xmlMutexUnlock(xmlMemMutex);
799 #else
800     fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
801 #endif
802     if (old_fp == NULL)
803 	fclose(fp);
804 }
805 
806 #ifdef MEM_LIST
807 
debugmem_list_add(MEMHDR * p)808 static void debugmem_list_add(MEMHDR *p)
809 {
810      p->mh_next = memlist;
811      p->mh_prev = NULL;
812      if (memlist) memlist->mh_prev = p;
813      memlist = p;
814 #ifdef MEM_LIST_DEBUG
815      if (stderr)
816      Mem_Display(stderr);
817 #endif
818 }
819 
debugmem_list_delete(MEMHDR * p)820 static void debugmem_list_delete(MEMHDR *p)
821 {
822      if (p->mh_next)
823      p->mh_next->mh_prev = p->mh_prev;
824      if (p->mh_prev)
825      p->mh_prev->mh_next = p->mh_next;
826      else memlist = p->mh_next;
827 #ifdef MEM_LIST_DEBUG
828      if (stderr)
829      Mem_Display(stderr);
830 #endif
831 }
832 
833 #endif
834 
835 /*
836  * debugmem_tag_error:
837  *
838  * internal error function.
839  */
840 
debugmem_tag_error(void * p)841 static void debugmem_tag_error(void *p)
842 {
843      xmlGenericError(xmlGenericErrorContext,
844 	     "Memory tag error occurs :%p \n\t bye\n", p);
845 #ifdef MEM_LIST
846      if (stderr)
847      xmlMemDisplay(stderr);
848 #endif
849 }
850 
851 #ifdef MEM_LIST
852 static FILE *xmlMemoryDumpFile = NULL;
853 #endif
854 
855 /**
856  * xmlMemShow:
857  * @fp:  a FILE descriptor used as the output file
858  * @nr:  number of entries to dump
859  *
860  * show a show display of the memory allocated, and dump
861  * the @nr last allocated areas which were not freed
862  */
863 
864 void
xmlMemShow(FILE * fp,int nr ATTRIBUTE_UNUSED)865 xmlMemShow(FILE *fp, int nr ATTRIBUTE_UNUSED)
866 {
867 #ifdef MEM_LIST
868     MEMHDR *p;
869 #endif
870 
871     if (fp != NULL)
872 	fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
873 		debugMemSize, debugMaxMemSize);
874 #ifdef MEM_LIST
875     xmlMutexLock(xmlMemMutex);
876     if (nr > 0) {
877 	fprintf(fp,"NUMBER   SIZE  TYPE   WHERE\n");
878 	p = memlist;
879 	while ((p) && nr > 0) {
880 	      fprintf(fp,"%6lu %6lu ",p->mh_number,(unsigned long)p->mh_size);
881 	    switch (p->mh_type) {
882 	       case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
883 	       case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
884 	       case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
885 	      case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
886 	      case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
887 		default:fprintf(fp,"   ???    in ");break;
888 	    }
889 	    if (p->mh_file != NULL)
890 	        fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
891 	    if (p->mh_tag != MEMTAG)
892 		fprintf(fp,"  INVALID");
893 	    xmlMemContentShow(fp, p);
894 	    fprintf(fp,"\n");
895 	    nr--;
896 	    p = p->mh_next;
897 	}
898     }
899     xmlMutexUnlock(xmlMemMutex);
900 #endif /* MEM_LIST */
901 }
902 
903 /**
904  * xmlMemoryDump:
905  *
906  * Dump in-extenso the memory blocks allocated to the file .memorylist
907  */
908 
909 void
xmlMemoryDump(void)910 xmlMemoryDump(void)
911 {
912 #ifdef MEM_LIST
913     FILE *dump;
914 
915     if (debugMaxMemSize == 0)
916 	return;
917     dump = fopen(".memdump", "w");
918     if (dump == NULL)
919 	xmlMemoryDumpFile = stderr;
920     else xmlMemoryDumpFile = dump;
921 
922     xmlMemDisplay(xmlMemoryDumpFile);
923 
924     if (dump != NULL) fclose(dump);
925 #endif /* MEM_LIST */
926 }
927 
928 
929 /****************************************************************
930  *								*
931  *		Initialization Routines				*
932  *								*
933  ****************************************************************/
934 
935 /**
936  * xmlInitMemory:
937  *
938  * Initialize the memory layer.
939  *
940  * Returns 0 on success
941  */
942 int
xmlInitMemory(void)943 xmlInitMemory(void)
944 {
945 #ifdef HAVE_STDLIB_H
946      char *breakpoint;
947 #endif
948 #ifdef DEBUG_MEMORY
949      xmlGenericError(xmlGenericErrorContext,
950 	     "xmlInitMemory()\n");
951 #endif
952     /*
953      This is really not good code (see Bug 130419).  Suggestions for
954      improvement will be welcome!
955     */
956      if (xmlMemInitialized) return(-1);
957      xmlMemInitialized = 1;
958      xmlMemMutex = xmlNewMutex();
959 
960 #ifdef HAVE_STDLIB_H
961      breakpoint = getenv("XML_MEM_BREAKPOINT");
962      if (breakpoint != NULL) {
963          sscanf(breakpoint, "%ud", &xmlMemStopAtBlock);
964      }
965 #endif
966 #ifdef HAVE_STDLIB_H
967      breakpoint = getenv("XML_MEM_TRACE");
968      if (breakpoint != NULL) {
969          sscanf(breakpoint, "%p", &xmlMemTraceBlockAt);
970      }
971 #endif
972 
973 #ifdef DEBUG_MEMORY
974      xmlGenericError(xmlGenericErrorContext,
975 	     "xmlInitMemory() Ok\n");
976 #endif
977      return(0);
978 }
979 
980 /**
981  * xmlCleanupMemory:
982  *
983  * Free up all the memory allocated by the library for its own
984  * use. This should not be called by user level code.
985  */
986 void
xmlCleanupMemory(void)987 xmlCleanupMemory(void) {
988 #ifdef DEBUG_MEMORY
989      xmlGenericError(xmlGenericErrorContext,
990 	     "xmlCleanupMemory()\n");
991 #endif
992     if (xmlMemInitialized == 0)
993         return;
994 
995     xmlFreeMutex(xmlMemMutex);
996     xmlMemMutex = NULL;
997     xmlMemInitialized = 0;
998 #ifdef DEBUG_MEMORY
999      xmlGenericError(xmlGenericErrorContext,
1000 	     "xmlCleanupMemory() Ok\n");
1001 #endif
1002 }
1003 
1004 /**
1005  * xmlMemSetup:
1006  * @freeFunc: the free() function to use
1007  * @mallocFunc: the malloc() function to use
1008  * @reallocFunc: the realloc() function to use
1009  * @strdupFunc: the strdup() function to use
1010  *
1011  * Override the default memory access functions with a new set
1012  * This has to be called before any other libxml routines !
1013  *
1014  * Should this be blocked if there was already some allocations
1015  * done ?
1016  *
1017  * Returns 0 on success
1018  */
1019 int
xmlMemSetup(xmlFreeFunc freeFunc,xmlMallocFunc mallocFunc,xmlReallocFunc reallocFunc,xmlStrdupFunc strdupFunc)1020 xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
1021             xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
1022 #ifdef DEBUG_MEMORY
1023      xmlGenericError(xmlGenericErrorContext,
1024 	     "xmlMemSetup()\n");
1025 #endif
1026     if (freeFunc == NULL)
1027 	return(-1);
1028     if (mallocFunc == NULL)
1029 	return(-1);
1030     if (reallocFunc == NULL)
1031 	return(-1);
1032     if (strdupFunc == NULL)
1033 	return(-1);
1034     xmlFree = freeFunc;
1035     xmlMalloc = mallocFunc;
1036     xmlMallocAtomic = mallocFunc;
1037     xmlRealloc = reallocFunc;
1038     xmlMemStrdup = strdupFunc;
1039 #ifdef DEBUG_MEMORY
1040      xmlGenericError(xmlGenericErrorContext,
1041 	     "xmlMemSetup() Ok\n");
1042 #endif
1043     return(0);
1044 }
1045 
1046 /**
1047  * xmlMemGet:
1048  * @freeFunc: place to save the free() function in use
1049  * @mallocFunc: place to save the malloc() function in use
1050  * @reallocFunc: place to save the realloc() function in use
1051  * @strdupFunc: place to save the strdup() function in use
1052  *
1053  * Provides the memory access functions set currently in use
1054  *
1055  * Returns 0 on success
1056  */
1057 int
xmlMemGet(xmlFreeFunc * freeFunc,xmlMallocFunc * mallocFunc,xmlReallocFunc * reallocFunc,xmlStrdupFunc * strdupFunc)1058 xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
1059 	  xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
1060     if (freeFunc != NULL) *freeFunc = xmlFree;
1061     if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
1062     if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
1063     if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
1064     return(0);
1065 }
1066 
1067 /**
1068  * xmlGcMemSetup:
1069  * @freeFunc: the free() function to use
1070  * @mallocFunc: the malloc() function to use
1071  * @mallocAtomicFunc: the malloc() function to use for atomic allocations
1072  * @reallocFunc: the realloc() function to use
1073  * @strdupFunc: the strdup() function to use
1074  *
1075  * Override the default memory access functions with a new set
1076  * This has to be called before any other libxml routines !
1077  * The mallocAtomicFunc is specialized for atomic block
1078  * allocations (i.e. of areas  useful for garbage collected memory allocators
1079  *
1080  * Should this be blocked if there was already some allocations
1081  * done ?
1082  *
1083  * Returns 0 on success
1084  */
1085 int
xmlGcMemSetup(xmlFreeFunc freeFunc,xmlMallocFunc mallocFunc,xmlMallocFunc mallocAtomicFunc,xmlReallocFunc reallocFunc,xmlStrdupFunc strdupFunc)1086 xmlGcMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
1087               xmlMallocFunc mallocAtomicFunc, xmlReallocFunc reallocFunc,
1088 	      xmlStrdupFunc strdupFunc) {
1089 #ifdef DEBUG_MEMORY
1090      xmlGenericError(xmlGenericErrorContext,
1091 	     "xmlGcMemSetup()\n");
1092 #endif
1093     if (freeFunc == NULL)
1094 	return(-1);
1095     if (mallocFunc == NULL)
1096 	return(-1);
1097     if (mallocAtomicFunc == NULL)
1098 	return(-1);
1099     if (reallocFunc == NULL)
1100 	return(-1);
1101     if (strdupFunc == NULL)
1102 	return(-1);
1103     xmlFree = freeFunc;
1104     xmlMalloc = mallocFunc;
1105     xmlMallocAtomic = mallocAtomicFunc;
1106     xmlRealloc = reallocFunc;
1107     xmlMemStrdup = strdupFunc;
1108 #ifdef DEBUG_MEMORY
1109      xmlGenericError(xmlGenericErrorContext,
1110 	     "xmlGcMemSetup() Ok\n");
1111 #endif
1112     return(0);
1113 }
1114 
1115 /**
1116  * xmlGcMemGet:
1117  * @freeFunc: place to save the free() function in use
1118  * @mallocFunc: place to save the malloc() function in use
1119  * @mallocAtomicFunc: place to save the atomic malloc() function in use
1120  * @reallocFunc: place to save the realloc() function in use
1121  * @strdupFunc: place to save the strdup() function in use
1122  *
1123  * Provides the memory access functions set currently in use
1124  * The mallocAtomicFunc is specialized for atomic block
1125  * allocations (i.e. of areas  useful for garbage collected memory allocators
1126  *
1127  * Returns 0 on success
1128  */
1129 int
xmlGcMemGet(xmlFreeFunc * freeFunc,xmlMallocFunc * mallocFunc,xmlMallocFunc * mallocAtomicFunc,xmlReallocFunc * reallocFunc,xmlStrdupFunc * strdupFunc)1130 xmlGcMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
1131             xmlMallocFunc *mallocAtomicFunc, xmlReallocFunc *reallocFunc,
1132 	    xmlStrdupFunc *strdupFunc) {
1133     if (freeFunc != NULL) *freeFunc = xmlFree;
1134     if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
1135     if (mallocAtomicFunc != NULL) *mallocAtomicFunc = xmlMallocAtomic;
1136     if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
1137     if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
1138     return(0);
1139 }
1140 
1141 #define bottom_xmlmemory
1142 #include "elfgcchack.h"
1143