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