blob: 2d46f3857c30d562fc57b51ad8e6cf6226ef096c [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
28
Daniel Veillard7f7d1111999-09-22 09:46:25 +000029
Daniel Veillard6454aec1999-09-02 22:04:43 +000030#include "xmlmemory.h"
31
32#ifndef NO_DEBUG_MEMORY
Daniel Veillard6454aec1999-09-02 22:04:43 +000033#ifdef xmlMalloc
34#undef xmlMalloc
35#endif
36#ifdef xmlRealloc
37#undef xmlRealloc
38#endif
39#ifdef xmlMemStrdup
40#undef xmlMemStrdup
41#endif
Daniel Veillard00fdf371999-10-08 09:40:39 +000042
Daniel Veillard6454aec1999-09-02 22:04:43 +000043extern void xmlMemoryDump(void);
44
45/*
46 * Each of the blocks allocated begin with a header containing informations
47 */
48
49#define MEMTAG 0x5aa5
50
51#define MALLOC_TYPE 1
52#define REALLOC_TYPE 2
53#define STRDUP_TYPE 3
54
55typedef struct memnod {
56 unsigned int mh_tag;
57 unsigned int mh_type;
58 unsigned long mh_number;
59 size_t mh_size;
60#ifdef MEM_LIST
61 struct memnod *mh_next;
62 struct memnod *mh_prev;
63#endif
64 const char *mh_file;
65 unsigned int mh_line;
66} MEMHDR;
67
68
69#ifdef SUN4
70#define ALIGN_SIZE 16
71#else
72#define ALIGN_SIZE sizeof(double)
73#endif
74#define HDR_SIZE sizeof(MEMHDR)
75#define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
76 / ALIGN_SIZE ) * ALIGN_SIZE)
77
78
79#define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
80#define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE))
81
82
83static unsigned long debugMemSize = 0;
Daniel Veillard7c1206f1999-10-14 09:10:25 +000084static unsigned long debugMaxMemSize = 0;
Daniel Veillard6454aec1999-09-02 22:04:43 +000085static int block=0;
Daniel Veillard7c1206f1999-10-14 09:10:25 +000086int xmlMemStopAtBlock = 0;
87int xmlMemInitialized = 0;
Daniel Veillard6454aec1999-09-02 22:04:43 +000088#ifdef MEM_LIST
89static MEMHDR *memlist = NULL;
90#endif
91
92void debugmem_tag_error(void *addr);
93#ifdef MEM_LIST
94void debugmem_list_add(MEMHDR *);
95void debugmem_list_delete(MEMHDR *);
96#endif
97#define Mem_Tag_Err(a) debugmem_tag_error(a);
98
99#ifndef TEST_POINT
100#define TEST_POINT
101#endif
102
103/**
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000104 * xmlMallocBreakpoint:
105 *
106 * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
107 * number reaches the specified value this function is called. One need to add a breakpoint
108 * to it to get the context in which the given block is allocated.
109 */
110
111void
112xmlMallocBreakpoint(void) {
113 fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
114}
115
116/**
Daniel Veillard6454aec1999-09-02 22:04:43 +0000117 * xmlMallocLoc:
118 * @size: an int specifying the size in byte to allocate.
119 * @file: the file name or NULL
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000120 @file: the line number
Daniel Veillard6454aec1999-09-02 22:04:43 +0000121 *
122 * a malloc() equivalent, with logging of the allocation info.
123 *
124 * Returns a pointer to the allocated area or NULL in case of lack of memory.
125 */
126
127void *
128xmlMallocLoc(int size, const char * file, int line)
129{
130 MEMHDR *p;
131
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000132 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000133#ifdef DEBUG_MEMORY
134 fprintf(stderr, "Malloc(%d)\n",size);
135#endif
136
137 TEST_POINT
138
139 p = (MEMHDR *) malloc(RESERVE_SIZE+size);
140
141 if (!p) {
Daniel Veillard10a2c651999-12-12 13:03:50 +0000142 fprintf(stderr, "xmlMalloc : Out of free space\n");
143 xmlMemoryDump();
144 return(NULL);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000145 }
146 p->mh_tag = MEMTAG;
147 p->mh_number = ++block;
148 p->mh_size = size;
149 p->mh_type = MALLOC_TYPE;
150 p->mh_file = file;
151 p->mh_line = line;
152 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000153 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000154#ifdef MEM_LIST
155 debugmem_list_add(p);
156#endif
157
158#ifdef DEBUG_MEMORY
159 fprintf(stderr, "Malloc(%d) Ok\n",size);
160#endif
161
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000162 if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000163
164 TEST_POINT
165
166 return(HDR_2_CLIENT(p));
167}
168
169/**
170 * xmlMalloc:
171 * @size: an int specifying the size in byte to allocate.
172 *
173 * a malloc() equivalent, with logging of the allocation info.
174 *
175 * Returns a pointer to the allocated area or NULL in case of lack of memory.
176 */
177
178void *
179xmlMalloc(int size)
180{
181 return(xmlMallocLoc(size, "none", 0));
182}
183
184/**
185 * xmlReallocLoc:
186 * @ptr: the initial memory block pointer
187 * @size: an int specifying the size in byte to allocate.
188 * @file: the file name or NULL
189 * @file: the line number
190 *
191 * a realloc() equivalent, with logging of the allocation info.
192 *
193 * Returns a pointer to the allocated area or NULL in case of lack of memory.
194 */
195
196void *
197xmlReallocLoc(void *ptr,int size, const char * file, int line)
198{
199 MEMHDR *p;
200 unsigned long number;
201
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000202 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000203 TEST_POINT
204
205 p = CLIENT_2_HDR(ptr);
206 number = p->mh_number;
207 if (p->mh_tag != MEMTAG) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000208 Mem_Tag_Err(p);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000209 goto error;
210 }
211 p->mh_tag = ~MEMTAG;
212 debugMemSize -= p->mh_size;
213#ifdef MEM_LIST
214 debugmem_list_delete(p);
215#endif
216
217 p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
218 if (!p) {
219 goto error;
220 }
221 p->mh_tag = MEMTAG;
222 p->mh_number = number;
223 p->mh_type = REALLOC_TYPE;
224 p->mh_size = size;
225 p->mh_file = file;
226 p->mh_line = line;
227 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000228 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000229#ifdef MEM_LIST
230 debugmem_list_add(p);
231#endif
232
233 TEST_POINT
234
235 return(HDR_2_CLIENT(p));
236
237error:
238 return(NULL);
239}
240
241/**
242 * xmlRealloc:
243 * @ptr: the initial memory block pointer
244 * @size: an int specifying the size in byte to allocate.
245 *
246 * a realloc() equivalent, with logging of the allocation info.
247 *
248 * Returns a pointer to the allocated area or NULL in case of lack of memory.
249 */
250
251void *
252xmlRealloc(void *ptr,int size) {
253 return(xmlReallocLoc(ptr, size, "none", 0));
254}
255
256/**
257 * xmlFree:
258 * @ptr: the memory block pointer
259 *
260 * a free() equivalent, with error checking.
Daniel Veillard6454aec1999-09-02 22:04:43 +0000261 */
Daniel Veillard6454aec1999-09-02 22:04:43 +0000262void
263xmlFree(void *ptr)
264{
265 MEMHDR *p;
266
267 TEST_POINT
268
269 p = CLIENT_2_HDR(ptr);
270 if (p->mh_tag != MEMTAG) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000271 Mem_Tag_Err(p);
272 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000273 }
274 p->mh_tag = ~MEMTAG;
275 debugMemSize -= p->mh_size;
276
277#ifdef MEM_LIST
278 debugmem_list_delete(p);
279#endif
280 free(p);
281
282 TEST_POINT
283
284 return;
285
286error:
287 fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
288 return;
289}
290
291/**
292 * xmlMemStrdupLoc:
293 * @ptr: the initial string pointer
294 * @file: the file name or NULL
295 * @file: the line number
296 *
297 * a strdup() equivalent, with logging of the allocation info.
298 *
299 * Returns a pointer to the new string or NULL if allocation error occured.
300 */
301
302char *
303xmlMemStrdupLoc(const char *str, const char *file, int line)
304{
305 char *s;
306 size_t size = strlen(str) + 1;
307 MEMHDR *p;
308
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000309 if (!xmlMemInitialized) xmlInitMemory();
Daniel Veillard6454aec1999-09-02 22:04:43 +0000310 TEST_POINT
311
312 p = (MEMHDR *) malloc(RESERVE_SIZE+size);
313 if (!p) {
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000314 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000315 }
316 p->mh_tag = MEMTAG;
317 p->mh_number = ++block;
318 p->mh_size = size;
319 p->mh_type = STRDUP_TYPE;
320 p->mh_file = file;
321 p->mh_line = line;
322 debugMemSize += size;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000323 if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000324#ifdef MEM_LIST
325 debugmem_list_add(p);
326#endif
327 s = HDR_2_CLIENT(p);
328
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000329 if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
330
Daniel Veillard6454aec1999-09-02 22:04:43 +0000331 if (s != NULL)
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000332 strcpy(s,str);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000333 else
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000334 goto error;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000335
336 TEST_POINT
337
338 return(s);
339
340error:
341 return(NULL);
342}
343
344/**
345 * xmlMemStrdup:
346 * @ptr: the initial string pointer
347 *
348 * a strdup() equivalent, with logging of the allocation info.
349 *
350 * Returns a pointer to the new string or NULL if allocation error occured.
351 */
352
353char *
354xmlMemStrdup(const char *str) {
355 return(xmlMemStrdupLoc(str, "none", 0));
356}
357
358/**
359 * xmlMemUsed:
360 *
361 * returns the amount of memory currenly allocated
362 *
363 * Returns an int representing the amount of memory allocated.
364 */
365
366int
367xmlMemUsed(void) {
368 return(debugMemSize);
369}
370
371/**
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000372 * xmlMemShow:
373 * @fp: a FILE descriptor used as the output file
374 * @nr: number of entries to dump
375 *
376 * show a show display of the memory allocated, and dump
377 * the @nr last allocated areas which were not freed
378 */
379
380void
381xmlMemShow(FILE *fp, int nr)
382{
383#ifdef MEM_LIST
384 MEMHDR *p;
385#endif
386
387 if (fp != NULL)
388 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
389 debugMemSize, debugMaxMemSize);
390#ifdef MEM_LIST
391 if (nr > 0) {
392 fprintf(fp,"NUMBER SIZE TYPE WHERE\n");
393 p = memlist;
394 while ((p) && nr > 0) {
395 fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
396 switch (p->mh_type) {
397 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
398 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
399 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
400 default:fprintf(fp," ??? in ");break;
401 }
402 if (p->mh_file != NULL)
403 fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
404 if (p->mh_tag != MEMTAG)
405 fprintf(fp," INVALID");
406 fprintf(fp,"\n");
407 nr--;
408 p = p->mh_next;
409 }
410 }
411#endif /* MEM_LIST */
412}
413
414/**
Daniel Veillard6454aec1999-09-02 22:04:43 +0000415 * xmlMemDisplay:
416 * @fp: a FILE descriptor used as the output file, if NULL, the result is
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000417 * written to the file .memorylist
Daniel Veillard6454aec1999-09-02 22:04:43 +0000418 *
419 * show in-extenso the memory blocks allocated
420 */
421
422void
423xmlMemDisplay(FILE *fp)
424{
425#ifdef MEM_LIST
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000426 MEMHDR *p;
427 int idx;
428#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
429 time_t currentTime;
430 char buf[500];
431 struct tm * tstruct;
432
433 currentTime = time(NULL);
434 tstruct = localtime(&currentTime);
435 strftime(buf, sizeof(buf) - 1, "%c", tstruct);
436 fprintf(fp," %s\n\n", buf);
437#endif
438
Daniel Veillard6454aec1999-09-02 22:04:43 +0000439
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000440 fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
441 debugMemSize, debugMaxMemSize);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000442 fprintf(fp,"BLOCK NUMBER SIZE TYPE\n");
443 idx = 0;
444 p = memlist;
445 while (p) {
Daniel Veillard6454aec1999-09-02 22:04:43 +0000446 fprintf(fp,"%-5u %6lu %6u ",idx++,p->mh_number,p->mh_size);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000447 switch (p->mh_type) {
448 case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
449 case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
450 case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
451 default:fprintf(fp," ??? in ");break;
452 }
Daniel Veillard6454aec1999-09-02 22:04:43 +0000453 if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000454 if (p->mh_tag != MEMTAG)
Daniel Veillard6454aec1999-09-02 22:04:43 +0000455 fprintf(fp," INVALID");
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000456 fprintf(fp,"\n");
457 p = p->mh_next;
458 }
Daniel Veillard6454aec1999-09-02 22:04:43 +0000459#else
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000460 fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
Daniel Veillard6454aec1999-09-02 22:04:43 +0000461#endif
462}
463
464#ifdef MEM_LIST
465
466void debugmem_list_add(MEMHDR *p)
467{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000468 p->mh_next = memlist;
469 p->mh_prev = NULL;
470 if (memlist) memlist->mh_prev = p;
471 memlist = p;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000472#ifdef MEM_LIST_DEBUG
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000473 if (stderr)
474 Mem_Display(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000475#endif
476}
477
478void debugmem_list_delete(MEMHDR *p)
479{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000480 if (p->mh_next)
481 p->mh_next->mh_prev = p->mh_prev;
482 if (p->mh_prev)
483 p->mh_prev->mh_next = p->mh_next;
484 else memlist = p->mh_next;
Daniel Veillard6454aec1999-09-02 22:04:43 +0000485#ifdef MEM_LIST_DEBUG
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000486 if (stderr)
487 Mem_Display(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000488#endif
489}
490
491#endif
492
493/*
494 * debugmem_tag_error : internal error function.
495 */
496
497void debugmem_tag_error(void *p)
498{
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000499 fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000500#ifdef MEM_LIST
Daniel Veillard7f7d1111999-09-22 09:46:25 +0000501 if (stderr)
502 xmlMemDisplay(stderr);
Daniel Veillard6454aec1999-09-02 22:04:43 +0000503#endif
504}
505
506FILE *xmlMemoryDumpFile = NULL;
507
508
509/**
510 * xmlMemoryDump:
511 *
512 * Dump in-extenso the memory blocks allocated to the file .memorylist
513 */
514
515void
516xmlMemoryDump(void)
517{
518 FILE *dump;
519
520 dump = fopen(".memdump", "w");
521 if (dump == NULL) xmlMemoryDumpFile = stdout;
522 else xmlMemoryDumpFile = dump;
523
524 xmlMemDisplay(xmlMemoryDumpFile);
525
526 if (dump != NULL) fclose(dump);
527}
528
529
530/****************************************************************
531 * *
532 * Initialization Routines *
533 * *
534 ****************************************************************/
535
536/**
537 * xmlInitMemory:
538 *
539 * Initialize the memory layer.
Daniel Veillard00fdf371999-10-08 09:40:39 +0000540 *
541 * Returns 0 on success
Daniel Veillard6454aec1999-09-02 22:04:43 +0000542 */
543
544
545int
546xmlInitMemory(void)
547{
548 int ret;
Daniel Veillard7c1206f1999-10-14 09:10:25 +0000549
550#ifdef HAVE_STDLIB_H
551 char *breakpoint;
552
553 breakpoint = getenv("XML_MEM_BREAKPOINT");
554 if (breakpoint != NULL) {
555 sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
556 }
557#endif
Daniel Veillard6454aec1999-09-02 22:04:43 +0000558
559#ifdef DEBUG_MEMORY
560 fprintf(stderr, "xmlInitMemory() Ok\n");
561#endif
562 ret = 0;
563 return(ret);
564}
565
566#endif /* ! NO_DEBUG_MEMORY */