blob: 1013e7e6930f20850572c054be8c9e775693dcf8 [file] [log] [blame]
Daniel Veillard6454aec1999-09-02 22:04:43 +00001/*
2 * memory.c: libxml memory allocator wrapper.
3 *
4 * Daniel.Veillard@w3.org
5 */
6
Daniel Veillard7f7d1111999-09-22 09:46:25 +00007#ifdef WIN32
Daniel Veillard3c558c31999-12-22 11:30:41 +00008#include "win32config.h"
Daniel Veillard7f7d1111999-09-22 09:46:25 +00009#else
10#include "config.h"
11#endif
12
Daniel Veillard6454aec1999-09-02 22:04:43 +000013#include <stdio.h>
Daniel Veillard7f7d1111999-09-22 09:46:25 +000014#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
Daniel Veillard6454aec1999-09-02 22:04:43 +000023#include <malloc.h>
Daniel Veillard7f7d1111999-09-22 09:46:25 +000024#endif
Daniel Veillard7c1206f1999-10-14 09:10:25 +000025#ifdef HAVE_STDLIB_H
26#include <stdlib.h>
27#endif
Daniel Veillard5e5c6231999-12-29 12:49:06 +000028#ifdef HAVE_CTYPE_H
29#include <ctype.h>
30#endif
Daniel Veillard7c1206f1999-10-14 09:10:25 +000031
Daniel Veillard7f7d1111999-09-22 09:46:25 +000032
Daniel Veillard6454aec1999-09-02 22:04:43 +000033#include "xmlmemory.h"
34
35#ifndef NO_DEBUG_MEMORY
Daniel Veillard6454aec1999-09-02 22:04:43 +000036#ifdef xmlMalloc
37#undef xmlMalloc
38#endif
39#ifdef xmlRealloc
40#undef xmlRealloc
41#endif
42#ifdef xmlMemStrdup
43#undef xmlMemStrdup
44#endif
Daniel Veillard00fdf371999-10-08 09:40:39 +000045
Daniel Veillard6454aec1999-09-02 22:04:43 +000046extern void xmlMemoryDump(void);
47
48/*
49 * Each of the blocks allocated begin with a header containing informations
50 */
51
52#define MEMTAG 0x5aa5
53
54#define MALLOC_TYPE 1
55#define REALLOC_TYPE 2
56#define STRDUP_TYPE 3
57
58typedef struct memnod {
59 unsigned int mh_tag;
60 unsigned int mh_type;
61 unsigned long mh_number;
62 size_t mh_size;
63#ifdef MEM_LIST
64 struct memnod *mh_next;
65 struct memnod *mh_prev;
66#endif
67 const char *mh_file;
68 unsigned int mh_line;
69} MEMHDR;
70
71
72#ifdef SUN4
73#define ALIGN_SIZE 16
74#else
75#define ALIGN_SIZE sizeof(double)
76#endif
77#define HDR_SIZE sizeof(MEMHDR)
78#define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
79 / ALIGN_SIZE ) * ALIGN_SIZE)
80
81
82#define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
83#define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE))
84
85
86static unsigned long debugMemSize = 0;
Daniel Veillard7c1206f1999-10-14 09:10:25 +000087static unsigned long debugMaxMemSize = 0;
Daniel Veillard6454aec1999-09-02 22:04:43 +000088static int block=0;
Daniel Veillard7c1206f1999-10-14 09:10:25 +000089int xmlMemStopAtBlock = 0;
90int xmlMemInitialized = 0;
Daniel Veillard6454aec1999-09-02 22:04:43 +000091#ifdef MEM_LIST
92static MEMHDR *memlist = NULL;
93#endif
94
95void debugmem_tag_error(void *addr);
96#ifdef MEM_LIST
97void debugmem_list_add(MEMHDR *);
98void debugmem_list_delete(MEMHDR *);
99#endif
100#define Mem_Tag_Err(a) debugmem_tag_error(a);
101
102#ifndef TEST_POINT
103#define TEST_POINT
104#endif
105
106/**
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000107 * xmlMallocBreakpoint:
108 *
109 * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
110 * number reaches the specified value this function is called. One need to add a breakpoint
111 * to it to get the context in which the given block is allocated.
112 */
113
114void
115xmlMallocBreakpoint(void) {
116 fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
117}
118
119/**
Daniel Veillard6454aec1999-09-02 22:04:43 +0000120 * xmlMallocLoc:
121 * @size: an int specifying the size in byte to allocate.
122 * @file: the file name or NULL
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000123 @file: the line number
Daniel Veillard6454aec1999-09-02 22:04:43 +0000124 *
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
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000135 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000136#ifdef DEBUG_MEMORY
137 fprintf(stderr, "Malloc(%d)\n",size);
138#endif
139
140 TEST_POINT
141
142 p = (MEMHDR *) malloc(RESERVE_SIZE+size);
143
144 if (!p) {
Daniel Veillard10a2c651999-12-12 13:03:50 +0000145 fprintf(stderr, "xmlMalloc : Out of free space\n");
146 xmlMemoryDump();
147 return(NULL);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000148 }
149 p->mh_tag = MEMTAG;
150 p->mh_number = ++block;
151 p->mh_size = size;
152 p->mh_type = MALLOC_TYPE;
153 p->mh_file = file;
154 p->mh_line = line;
155 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000156 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000157#ifdef MEM_LIST
158 debugmem_list_add(p);
159#endif
160
161#ifdef DEBUG_MEMORY
162 fprintf(stderr, "Malloc(%d) Ok\n",size);
163#endif
164
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000165 if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000166
167 TEST_POINT
168
169 return(HDR_2_CLIENT(p));
170}
171
172/**
173 * xmlMalloc:
174 * @size: an int specifying the size in byte to allocate.
175 *
176 * a malloc() equivalent, with logging of the allocation info.
177 *
178 * Returns a pointer to the allocated area or NULL in case of lack of memory.
179 */
180
181void *
182xmlMalloc(int size)
183{
184 return(xmlMallocLoc(size, "none", 0));
185}
186
187/**
188 * xmlReallocLoc:
189 * @ptr: the initial memory block pointer
190 * @size: an int specifying the size in byte to allocate.
191 * @file: the file name or NULL
192 * @file: the line number
193 *
194 * a realloc() equivalent, with logging of the allocation info.
195 *
196 * Returns a pointer to the allocated area or NULL in case of lack of memory.
197 */
198
199void *
200xmlReallocLoc(void *ptr,int size, const char * file, int line)
201{
202 MEMHDR *p;
203 unsigned long number;
204
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000205 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000206 TEST_POINT
207
208 p = CLIENT_2_HDR(ptr);
209 number = p->mh_number;
210 if (p->mh_tag != MEMTAG) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000211 Mem_Tag_Err(p);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000212 goto error;
213 }
214 p->mh_tag = ~MEMTAG;
215 debugMemSize -= p->mh_size;
216#ifdef MEM_LIST
217 debugmem_list_delete(p);
218#endif
219
220 p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
221 if (!p) {
222 goto error;
223 }
224 p->mh_tag = MEMTAG;
225 p->mh_number = number;
226 p->mh_type = REALLOC_TYPE;
227 p->mh_size = size;
228 p->mh_file = file;
229 p->mh_line = line;
230 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000231 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000232#ifdef MEM_LIST
233 debugmem_list_add(p);
234#endif
235
236 TEST_POINT
237
238 return(HDR_2_CLIENT(p));
239
240error:
241 return(NULL);
242}
243
244/**
245 * xmlRealloc:
246 * @ptr: the initial memory block pointer
247 * @size: an int specifying the size in byte to allocate.
248 *
249 * a realloc() equivalent, with logging of the allocation info.
250 *
251 * Returns a pointer to the allocated area or NULL in case of lack of memory.
252 */
253
254void *
255xmlRealloc(void *ptr,int size) {
256 return(xmlReallocLoc(ptr, size, "none", 0));
257}
258
259/**
260 * xmlFree:
261 * @ptr: the memory block pointer
262 *
263 * a free() equivalent, with error checking.
Daniel Veillard6454aec1999-09-02 22:04:43 +0000264 */
Daniel Veillard6454aec1999-09-02 22:04:43 +0000265void
266xmlFree(void *ptr)
267{
268 MEMHDR *p;
269
270 TEST_POINT
271
272 p = CLIENT_2_HDR(ptr);
273 if (p->mh_tag != MEMTAG) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000274 Mem_Tag_Err(p);
275 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000276 }
277 p->mh_tag = ~MEMTAG;
278 debugMemSize -= p->mh_size;
279
280#ifdef MEM_LIST
281 debugmem_list_delete(p);
282#endif
283 free(p);
284
285 TEST_POINT
286
287 return;
288
289error:
290 fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
291 return;
292}
293
294/**
295 * xmlMemStrdupLoc:
296 * @ptr: the initial string pointer
297 * @file: the file name or NULL
298 * @file: the line number
299 *
300 * a strdup() equivalent, with logging of the allocation info.
301 *
302 * Returns a pointer to the new string or NULL if allocation error occured.
303 */
304
305char *
306xmlMemStrdupLoc(const char *str, const char *file, int line)
307{
308 char *s;
309 size_t size = strlen(str) + 1;
310 MEMHDR *p;
311
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000312 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000313 TEST_POINT
314
315 p = (MEMHDR *) malloc(RESERVE_SIZE+size);
316 if (!p) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000317 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000318 }
319 p->mh_tag = MEMTAG;
320 p->mh_number = ++block;
321 p->mh_size = size;
322 p->mh_type = STRDUP_TYPE;
323 p->mh_file = file;
324 p->mh_line = line;
325 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000326 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000327#ifdef MEM_LIST
328 debugmem_list_add(p);
329#endif
330 s = HDR_2_CLIENT(p);
331
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000332 if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
333
Daniel Veillard6454aec1999-09-02 22:04:43 +0000334 if (s != NULL)
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000335 strcpy(s,str);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000336 else
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000337 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000338
339 TEST_POINT
340
341 return(s);
342
343error:
344 return(NULL);
345}
346
347/**
348 * xmlMemStrdup:
349 * @ptr: the initial string pointer
350 *
351 * a strdup() equivalent, with logging of the allocation info.
352 *
353 * Returns a pointer to the new string or NULL if allocation error occured.
354 */
355
356char *
357xmlMemStrdup(const char *str) {
358 return(xmlMemStrdupLoc(str, "none", 0));
359}
360
361/**
362 * xmlMemUsed:
363 *
364 * returns the amount of memory currenly allocated
365 *
366 * Returns an int representing the amount of memory allocated.
367 */
368
369int
370xmlMemUsed(void) {
371 return(debugMemSize);
372}
373
Daniel Veillard5e5c6231999-12-29 12:49:06 +0000374#ifdef MEM_LIST
375/**
376 * xmlMemContentShow:
377 * @fp: a FILE descriptor used as the output file
378 * @p: a memory block header
379 *
380 * tries to show some content from the memory block
381 */
382
383void
384xmlMemContentShow(FILE *fp, MEMHDR *p)
385{
386 int i,j,len = p->mh_size;
387 const char *buf = HDR_2_CLIENT(p);
388
389 for (i = 0;i < len;i++) {
390 if (buf[i] == 0) break;
391 if (!isprint(buf[i])) break;
392 }
393 if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
394 if (len >= 4) {
395 MEMHDR *q;
396 void *cur;
397
398 for (j = 0;j < len -3;j += 4) {
399 cur = *((void **) &buf[j]);
400 q = CLIENT_2_HDR(cur);
401 p = memlist;
402 while (p != NULL) {
403 if (p == q) break;
404 p = p->mh_next;
405 }
406 if (p == q) {
407 fprintf(fp, " pointer to #%lu at index %d",
408 p->mh_number, j);
409 return;
410 }
411 }
412 }
413 } else if ((i == 0) && (buf[i] == 0)) {
414 fprintf(fp," null");
415 } else {
416 if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
417 else {
418 fprintf(fp," [");
419 for (j = 0;j < i;j++)
420 fprintf(fp,"%c", buf[j]);
421 fprintf(fp,"]");
422 }
423 }
424}
425#endif
426
Daniel Veillard6454aec1999-09-02 22:04:43 +0000427/**
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000428 * xmlMemShow:
429 * @fp: a FILE descriptor used as the output file
430 * @nr: number of entries to dump
431 *
432 * show a show display of the memory allocated, and dump
433 * the @nr last allocated areas which were not freed
434 */
435
436void
437xmlMemShow(FILE *fp, int nr)
438{
439#ifdef MEM_LIST
440 MEMHDR *p;
441#endif
442
443 if (fp != NULL)
444 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
445 debugMemSize, debugMaxMemSize);
446#ifdef MEM_LIST
447 if (nr > 0) {
448 fprintf(fp,"NUMBER SIZE TYPE WHERE\n");
449 p = memlist;
450 while ((p) && nr > 0) {
451 fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
452 switch (p->mh_type) {
453 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
454 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
455 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
456 default:fprintf(fp," ??? in ");break;
457 }
458 if (p->mh_file != NULL)
459 fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
460 if (p->mh_tag != MEMTAG)
461 fprintf(fp," INVALID");
Daniel Veillard5e5c6231999-12-29 12:49:06 +0000462 xmlMemContentShow(fp, p);
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000463 fprintf(fp,"\n");
464 nr--;
465 p = p->mh_next;
466 }
467 }
468#endif /* MEM_LIST */
469}
470
471/**
Daniel Veillard6454aec1999-09-02 22:04:43 +0000472 * xmlMemDisplay:
473 * @fp: a FILE descriptor used as the output file, if NULL, the result is
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000474 * written to the file .memorylist
Daniel Veillard6454aec1999-09-02 22:04:43 +0000475 *
476 * show in-extenso the memory blocks allocated
477 */
478
479void
480xmlMemDisplay(FILE *fp)
481{
482#ifdef MEM_LIST
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000483 MEMHDR *p;
484 int idx;
485#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
486 time_t currentTime;
487 char buf[500];
488 struct tm * tstruct;
489
490 currentTime = time(NULL);
491 tstruct = localtime(&currentTime);
492 strftime(buf, sizeof(buf) - 1, "%c", tstruct);
493 fprintf(fp," %s\n\n", buf);
494#endif
495
Daniel Veillard6454aec1999-09-02 22:04:43 +0000496
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000497 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
498 debugMemSize, debugMaxMemSize);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000499 fprintf(fp,"BLOCK NUMBER SIZE TYPE\n");
500 idx = 0;
501 p = memlist;
502 while (p) {
Daniel Veillard6454aec1999-09-02 22:04:43 +0000503 fprintf(fp,"%-5u %6lu %6u ",idx++,p->mh_number,p->mh_size);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000504 switch (p->mh_type) {
505 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
506 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
507 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
508 default:fprintf(fp," ??? in ");break;
509 }
Daniel Veillard6454aec1999-09-02 22:04:43 +0000510 if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000511 if (p->mh_tag != MEMTAG)
Daniel Veillard6454aec1999-09-02 22:04:43 +0000512 fprintf(fp," INVALID");
Daniel Veillard5e5c6231999-12-29 12:49:06 +0000513 xmlMemContentShow(fp, p);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000514 fprintf(fp,"\n");
515 p = p->mh_next;
516 }
Daniel Veillard6454aec1999-09-02 22:04:43 +0000517#else
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000518 fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
Daniel Veillard6454aec1999-09-02 22:04:43 +0000519#endif
520}
521
522#ifdef MEM_LIST
523
524void debugmem_list_add(MEMHDR *p)
525{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000526 p->mh_next = memlist;
527 p->mh_prev = NULL;
528 if (memlist) memlist->mh_prev = p;
529 memlist = p;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000530#ifdef MEM_LIST_DEBUG
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000531 if (stderr)
532 Mem_Display(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000533#endif
534}
535
536void debugmem_list_delete(MEMHDR *p)
537{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000538 if (p->mh_next)
539 p->mh_next->mh_prev = p->mh_prev;
540 if (p->mh_prev)
541 p->mh_prev->mh_next = p->mh_next;
542 else memlist = p->mh_next;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000543#ifdef MEM_LIST_DEBUG
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000544 if (stderr)
545 Mem_Display(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000546#endif
547}
548
549#endif
550
551/*
552 * debugmem_tag_error : internal error function.
553 */
554
555void debugmem_tag_error(void *p)
556{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000557 fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000558#ifdef MEM_LIST
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000559 if (stderr)
560 xmlMemDisplay(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000561#endif
562}
563
564FILE *xmlMemoryDumpFile = NULL;
565
566
567/**
568 * xmlMemoryDump:
569 *
570 * Dump in-extenso the memory blocks allocated to the file .memorylist
571 */
572
573void
574xmlMemoryDump(void)
575{
576 FILE *dump;
577
578 dump = fopen(".memdump", "w");
579 if (dump == NULL) xmlMemoryDumpFile = stdout;
580 else xmlMemoryDumpFile = dump;
581
582 xmlMemDisplay(xmlMemoryDumpFile);
583
584 if (dump != NULL) fclose(dump);
585}
586
587
588/****************************************************************
589 * *
590 * Initialization Routines *
591 * *
592 ****************************************************************/
593
594/**
595 * xmlInitMemory:
596 *
597 * Initialize the memory layer.
Daniel Veillard00fdf371999-10-08 09:40:39 +0000598 *
599 * Returns 0 on success
Daniel Veillard6454aec1999-09-02 22:04:43 +0000600 */
601
602
603int
604xmlInitMemory(void)
605{
606 int ret;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000607
608#ifdef HAVE_STDLIB_H
609 char *breakpoint;
610
611 breakpoint = getenv("XML_MEM_BREAKPOINT");
612 if (breakpoint != NULL) {
613 sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
614 }
615#endif
Daniel Veillard6454aec1999-09-02 22:04:43 +0000616
617#ifdef DEBUG_MEMORY
618 fprintf(stderr, "xmlInitMemory() Ok\n");
619#endif
620 ret = 0;
621 return(ret);
622}
623
624#endif /* ! NO_DEBUG_MEMORY */