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