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