blob: d693ba3aeadd79f649357968bfbe4352799939f5 [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 +000036void xmlMallocBreakpoint(void);
37void * xmlMemMalloc(int size);
38void * xmlMallocLoc(int size, const char * file, int line);
39void * xmlMemRealloc(void *ptr,int size);
40void xmlMemFree(void *ptr);
41char * xmlMemoryStrdup(const char *str);
42
43/************************************************************************
44 * *
45 * Macros, variables and associated types *
46 * *
47 ************************************************************************/
48
49
Owen Taylor3473f882001-02-23 17:55:21 +000050#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
71typedef 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
99static unsigned long debugMemSize = 0;
100static unsigned long debugMaxMemSize = 0;
101static int block=0;
102int xmlMemStopAtBlock = 0;
103int xmlMemInitialized = 0;
104#ifdef MEM_LIST
105static MEMHDR *memlist = NULL;
106#endif
107
108void debugmem_tag_error(void *addr);
109#ifdef MEM_LIST
110void debugmem_list_add(MEMHDR *);
111void 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
127void
128xmlMallocBreakpoint(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
144void *
145xmlMallocLoc(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
198void *
199xmlMemMalloc(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
216void *
217xmlReallocLoc(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
257error:
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
271void *
272xmlMemRealloc(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 */
282void
283xmlMemFree(void *ptr)
284{
285 MEMHDR *p;
Daniel Veillard92ad2102001-03-27 12:47:33 +0000286 char *target;
Owen Taylor3473f882001-02-23 17:55:21 +0000287
288 TEST_POINT
289
Daniel Veillard92ad2102001-03-27 12:47:33 +0000290 target = (char *) ptr;
291
Owen Taylor3473f882001-02-23 17:55:21 +0000292 p = CLIENT_2_HDR(ptr);
293 if (p->mh_tag != MEMTAG) {
294 Mem_Tag_Err(p);
295 goto error;
296 }
297 p->mh_tag = ~MEMTAG;
298 debugMemSize -= p->mh_size;
Daniel Veillard92ad2102001-03-27 12:47:33 +0000299 memset(target, -1, p->mh_size);
Owen Taylor3473f882001-02-23 17:55:21 +0000300
301#ifdef MEM_LIST
302 debugmem_list_delete(p);
303#endif
304 free(p);
305
306 TEST_POINT
307
308 return;
309
310error:
311 xmlGenericError(xmlGenericErrorContext,
312 "xmlFree(%X) error\n", (unsigned int) ptr);
313 return;
314}
315
316/**
317 * xmlMemStrdupLoc:
318 * @ptr: the initial string pointer
319 * @file: the file name or NULL
320 * @line: the line number
321 *
322 * a strdup() equivalent, with logging of the allocation info.
323 *
324 * Returns a pointer to the new string or NULL if allocation error occured.
325 */
326
327char *
328xmlMemStrdupLoc(const char *str, const char *file, int line)
329{
330 char *s;
331 size_t size = strlen(str) + 1;
332 MEMHDR *p;
333
334 if (!xmlMemInitialized) xmlInitMemory();
335 TEST_POINT
336
337 p = (MEMHDR *) malloc(RESERVE_SIZE+size);
338 if (!p) {
339 goto error;
340 }
341 p->mh_tag = MEMTAG;
342 p->mh_number = ++block;
343 p->mh_size = size;
344 p->mh_type = STRDUP_TYPE;
345 p->mh_file = file;
346 p->mh_line = line;
347 debugMemSize += size;
348 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
349#ifdef MEM_LIST
350 debugmem_list_add(p);
351#endif
352 s = (char *) HDR_2_CLIENT(p);
353
354 if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
355
356 if (s != NULL)
357 strcpy(s,str);
358 else
359 goto error;
360
361 TEST_POINT
362
363 return(s);
364
365error:
366 return(NULL);
367}
368
369/**
370 * xmlMemoryStrdup:
371 * @ptr: the initial string pointer
372 *
373 * a strdup() equivalent, with logging of the allocation info.
374 *
375 * Returns a pointer to the new string or NULL if allocation error occured.
376 */
377
378char *
379xmlMemoryStrdup(const char *str) {
380 return(xmlMemStrdupLoc(str, "none", 0));
381}
382
383/**
384 * xmlMemUsed:
385 *
386 * returns the amount of memory currenly allocated
387 *
388 * Returns an int representing the amount of memory allocated.
389 */
390
391int
392xmlMemUsed(void) {
393 return(debugMemSize);
394}
395
396#ifdef MEM_LIST
397/**
398 * xmlMemContentShow:
399 * @fp: a FILE descriptor used as the output file
400 * @p: a memory block header
401 *
402 * tries to show some content from the memory block
403 */
404
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000405static void
Owen Taylor3473f882001-02-23 17:55:21 +0000406xmlMemContentShow(FILE *fp, MEMHDR *p)
407{
408 int i,j,len = p->mh_size;
409 const char *buf = (const char *) HDR_2_CLIENT(p);
410
411 if (p == NULL) {
412 fprintf(fp, " NULL");
413 return;
414 }
415
416 for (i = 0;i < len;i++) {
417 if (buf[i] == 0) break;
418 if (!isprint(buf[i])) break;
419 }
420 if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
421 if (len >= 4) {
422 MEMHDR *q;
423 void *cur;
424
425 for (j = 0;j < len -3;j += 4) {
426 cur = *((void **) &buf[j]);
427 q = CLIENT_2_HDR(cur);
428 p = memlist;
429 while (p != NULL) {
430 if (p == q) break;
431 p = p->mh_next;
432 }
433 if ((p != NULL) && (p == q)) {
434 fprintf(fp, " pointer to #%lu at index %d",
435 p->mh_number, j);
436 return;
437 }
438 }
439 }
440 } else if ((i == 0) && (buf[i] == 0)) {
441 fprintf(fp," null");
442 } else {
443 if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
444 else {
445 fprintf(fp," [");
446 for (j = 0;j < i;j++)
447 fprintf(fp,"%c", buf[j]);
448 fprintf(fp,"]");
449 }
450 }
451}
452#endif
453
454/**
455 * xmlMemShow:
456 * @fp: a FILE descriptor used as the output file
457 * @nr: number of entries to dump
458 *
459 * show a show display of the memory allocated, and dump
460 * the @nr last allocated areas which were not freed
461 */
462
463void
464xmlMemShow(FILE *fp, int nr)
465{
466#ifdef MEM_LIST
467 MEMHDR *p;
468#endif
469
470 if (fp != NULL)
471 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
472 debugMemSize, debugMaxMemSize);
473#ifdef MEM_LIST
474 if (nr > 0) {
475 fprintf(fp,"NUMBER SIZE TYPE WHERE\n");
476 p = memlist;
477 while ((p) && nr > 0) {
478 fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
479 switch (p->mh_type) {
480 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
481 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
482 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
483 default:fprintf(fp," ??? in ");break;
484 }
485 if (p->mh_file != NULL)
486 fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
487 if (p->mh_tag != MEMTAG)
488 fprintf(fp," INVALID");
489 xmlMemContentShow(fp, p);
490 fprintf(fp,"\n");
491 nr--;
492 p = p->mh_next;
493 }
494 }
495#endif /* MEM_LIST */
496}
497
498/**
499 * xmlMemDisplay:
500 * @fp: a FILE descriptor used as the output file, if NULL, the result is
501 * written to the file .memorylist
502 *
503 * show in-extenso the memory blocks allocated
504 */
505
506void
507xmlMemDisplay(FILE *fp)
508{
509#ifdef MEM_LIST
510 MEMHDR *p;
511 int idx;
512#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
513 time_t currentTime;
514 char buf[500];
515 struct tm * tstruct;
516
517 currentTime = time(NULL);
518 tstruct = localtime(&currentTime);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000519 strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p", tstruct);
Owen Taylor3473f882001-02-23 17:55:21 +0000520 fprintf(fp," %s\n\n", buf);
521#endif
522
523
524 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
525 debugMemSize, debugMaxMemSize);
526 fprintf(fp,"BLOCK NUMBER SIZE TYPE\n");
527 idx = 0;
528 p = memlist;
529 while (p) {
530 fprintf(fp,"%-5u %6lu %6u ",idx++,p->mh_number,p->mh_size);
531 switch (p->mh_type) {
532 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
533 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
534 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
535 default:fprintf(fp," ??? in ");break;
536 }
537 if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
538 if (p->mh_tag != MEMTAG)
539 fprintf(fp," INVALID");
540 xmlMemContentShow(fp, p);
541 fprintf(fp,"\n");
542 p = p->mh_next;
543 }
544#else
545 fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
546#endif
547}
548
549#ifdef MEM_LIST
550
551void debugmem_list_add(MEMHDR *p)
552{
553 p->mh_next = memlist;
554 p->mh_prev = NULL;
555 if (memlist) memlist->mh_prev = p;
556 memlist = p;
557#ifdef MEM_LIST_DEBUG
558 if (stderr)
559 Mem_Display(stderr);
560#endif
561}
562
563void debugmem_list_delete(MEMHDR *p)
564{
565 if (p->mh_next)
566 p->mh_next->mh_prev = p->mh_prev;
567 if (p->mh_prev)
568 p->mh_prev->mh_next = p->mh_next;
569 else memlist = p->mh_next;
570#ifdef MEM_LIST_DEBUG
571 if (stderr)
572 Mem_Display(stderr);
573#endif
574}
575
576#endif
577
578/*
579 * debugmem_tag_error : internal error function.
580 */
581
582void debugmem_tag_error(void *p)
583{
584 xmlGenericError(xmlGenericErrorContext,
585 "Memory tag error occurs :%p \n\t bye\n", p);
586#ifdef MEM_LIST
587 if (stderr)
588 xmlMemDisplay(stderr);
589#endif
590}
591
592FILE *xmlMemoryDumpFile = NULL;
593
594
595/**
596 * xmlMemoryDump:
597 *
598 * Dump in-extenso the memory blocks allocated to the file .memorylist
599 */
600
601void
602xmlMemoryDump(void)
603{
604#if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
605 FILE *dump;
606
607 dump = fopen(".memdump", "w");
608 if (dump == NULL) xmlMemoryDumpFile = stdout;
609 else xmlMemoryDumpFile = dump;
610
611 xmlMemDisplay(xmlMemoryDumpFile);
612
613 if (dump != NULL) fclose(dump);
614#endif
615}
616
617
618/****************************************************************
619 * *
620 * Initialization Routines *
621 * *
622 ****************************************************************/
623
624#if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
625xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree;
626xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
627xmlReallocFunc xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
628xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
629#else
630xmlFreeFunc xmlFree = (xmlFreeFunc) free;
631xmlMallocFunc xmlMalloc = (xmlMallocFunc) malloc;
632xmlReallocFunc xmlRealloc = (xmlReallocFunc) realloc;
633xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) strdup;
634#endif
635
636/**
637 * xmlInitMemory:
638 *
639 * Initialize the memory layer.
640 *
641 * Returns 0 on success
642 */
643
644static int xmlInitMemoryDone = 0;
645
646int
647xmlInitMemory(void)
648{
649 int ret;
650
651#ifdef HAVE_STDLIB_H
652 char *breakpoint;
653#endif
654
655 if (xmlInitMemoryDone) return(-1);
656
657#ifdef HAVE_STDLIB_H
658 breakpoint = getenv("XML_MEM_BREAKPOINT");
659 if (breakpoint != NULL) {
660 sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
661 }
662#endif
663
664#ifdef DEBUG_MEMORY
665 xmlGenericError(xmlGenericErrorContext,
666 "xmlInitMemory() Ok\n");
667#endif
668 ret = 0;
669 return(ret);
670}
671
672/**
673 * xmlMemSetup:
674 * @freeFunc: the free() function to use
675 * @mallocFunc: the malloc() function to use
676 * @reallocFunc: the realloc() function to use
677 * @strdupFunc: the strdup() function to use
678 *
679 * Override the default memory access functions with a new set
680 * This has to be called before any other libxml routines !
681 *
682 * Should this be blocked if there was already some allocations
683 * done ?
684 *
685 * Returns 0 on success
686 */
687int
688xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
689 xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
690 if (freeFunc == NULL)
691 return(-1);
692 if (mallocFunc == NULL)
693 return(-1);
694 if (reallocFunc == NULL)
695 return(-1);
696 if (strdupFunc == NULL)
697 return(-1);
698 xmlFree = freeFunc;
699 xmlMalloc = mallocFunc;
700 xmlRealloc = reallocFunc;
701 xmlMemStrdup = strdupFunc;
702 return(0);
703}
704
705/**
706 * xmlMemGet:
707 * @freeFunc: the free() function in use
708 * @mallocFunc: the malloc() function in use
709 * @reallocFunc: the realloc() function in use
710 * @strdupFunc: the strdup() function in use
711 *
712 * Return the memory access functions set currently in use
713 *
714 * Returns 0 on success
715 */
716int
717xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
718 xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
719 if (freeFunc != NULL) *freeFunc = xmlFree;
720 if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
721 if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
722 if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
723 return(0);
724}
725