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