Large commit of changes done while travelling to XML'99
[baserock-morphs:libxml2.git] / xmlmemory.c
1 /*
2  * memory.c:  libxml memory allocator wrapper.
3  *
4  * Daniel.Veillard@w3.org
5  */
6
7 #ifdef WIN32
8 #define HAVE_FCNTL_H
9 #include <io.h>
10 #else
11 #include "config.h"
12 #endif
13
14 #include <stdio.h>
15 #include <string.h>
16
17 #ifdef HAVE_SYS_TYPES_H
18 #include <sys/types.h>
19 #endif
20 #ifdef HAVE_TIME_H
21 #include <time.h>
22 #endif
23 #ifdef HAVE_MALLOC_H
24 #include <malloc.h>
25 #endif
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29
30
31 #include "xmlmemory.h"
32
33 #ifndef NO_DEBUG_MEMORY
34 #ifdef xmlMalloc
35 #undef xmlMalloc
36 #endif
37 #ifdef xmlRealloc
38 #undef xmlRealloc
39 #endif
40 #ifdef xmlMemStrdup
41 #undef xmlMemStrdup
42 #endif
43
44 extern void xmlMemoryDump(void);
45
46 /*
47  * Each of the blocks allocated begin with a header containing informations
48  */
49
50 #define MEMTAG 0x5aa5
51
52 #define MALLOC_TYPE 1
53 #define REALLOC_TYPE 2
54 #define STRDUP_TYPE 3
55
56 typedef struct memnod {
57     unsigned int   mh_tag;
58     unsigned int   mh_type;
59     unsigned long  mh_number;
60     size_t         mh_size;
61 #ifdef MEM_LIST
62    struct memnod *mh_next;
63    struct memnod *mh_prev;
64 #endif
65    const char    *mh_file;
66    unsigned int   mh_line;
67 }  MEMHDR;
68
69
70 #ifdef SUN4
71 #define ALIGN_SIZE  16
72 #else
73 #define ALIGN_SIZE  sizeof(double)
74 #endif
75 #define HDR_SIZE    sizeof(MEMHDR)
76 #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
77                       / ALIGN_SIZE ) * ALIGN_SIZE)
78
79
80 #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
81 #define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))
82
83
84 static unsigned long  debugMemSize = 0;
85 static unsigned long  debugMaxMemSize = 0;
86 static int block=0;
87 int xmlMemStopAtBlock = 0;
88 int xmlMemInitialized = 0;
89 #ifdef MEM_LIST
90 static MEMHDR *memlist = NULL;
91 #endif
92
93 void debugmem_tag_error(void *addr);
94 #ifdef MEM_LIST
95 void  debugmem_list_add(MEMHDR *);
96 void debugmem_list_delete(MEMHDR *);
97 #endif
98 #define Mem_Tag_Err(a) debugmem_tag_error(a);
99
100 #ifndef TEST_POINT
101 #define TEST_POINT
102 #endif
103
104 /**
105  * xmlMallocBreakpoint:
106  *
107  * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
108  * number reaches the specified value this function is called. One need to add a breakpoint
109  * to it to get the context in which the given block is allocated.
110  */
111
112 void
113 xmlMallocBreakpoint(void) {
114     fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
115 }
116
117 /**
118  * xmlMallocLoc:
119  * @size:  an int specifying the size in byte to allocate.
120  * @file:  the file name or NULL
121   @file:  the line number
122  *
123  * a malloc() equivalent, with logging of the allocation info.
124  *
125  * Returns a pointer to the allocated area or NULL in case of lack of memory.
126  */
127
128 void *
129 xmlMallocLoc(int size, const char * file, int line)
130 {
131     MEMHDR *p;
132     
133     if (!xmlMemInitialized) xmlInitMemory();
134 #ifdef DEBUG_MEMORY
135     fprintf(stderr, "Malloc(%d)\n",size);
136 #endif
137
138     TEST_POINT
139     
140     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
141
142     if (!p) {
143         fprintf(stderr, "xmlMalloc : Out of free space\n");
144         xmlMemoryDump();
145         return(NULL);
146     }   
147     p->mh_tag = MEMTAG;
148     p->mh_number = ++block;
149     p->mh_size = size;
150     p->mh_type = MALLOC_TYPE;
151     p->mh_file = file;
152     p->mh_line = line;
153     debugMemSize += size;
154     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
155 #ifdef MEM_LIST
156     debugmem_list_add(p);
157 #endif
158
159 #ifdef DEBUG_MEMORY
160     fprintf(stderr, "Malloc(%d) Ok\n",size);
161 #endif
162     
163     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
164
165     TEST_POINT
166
167     return(HDR_2_CLIENT(p));
168 }
169
170 /**
171  * xmlMalloc:
172  * @size:  an int specifying the size in byte to allocate.
173  *
174  * a malloc() equivalent, with logging of the allocation info.
175  *
176  * Returns a pointer to the allocated area or NULL in case of lack of memory.
177  */
178
179 void *
180 xmlMalloc(int size)
181 {
182     return(xmlMallocLoc(size, "none", 0));
183 }
184
185 /**
186  * xmlReallocLoc:
187  * @ptr:  the initial memory block pointer
188  * @size:  an int specifying the size in byte to allocate.
189  * @file:  the file name or NULL
190  * @file:  the line number
191  *
192  * a realloc() equivalent, with logging of the allocation info.
193  *
194  * Returns a pointer to the allocated area or NULL in case of lack of memory.
195  */
196
197 void *
198 xmlReallocLoc(void *ptr,int size, const char * file, int line)
199 {
200     MEMHDR *p;
201     unsigned long number;
202
203     if (!xmlMemInitialized) xmlInitMemory();
204     TEST_POINT
205
206     p = CLIENT_2_HDR(ptr);
207     number = p->mh_number;
208     if (p->mh_tag != MEMTAG) {
209        Mem_Tag_Err(p);
210          goto error;
211     }
212     p->mh_tag = ~MEMTAG;
213     debugMemSize -= p->mh_size;
214 #ifdef MEM_LIST
215     debugmem_list_delete(p);
216 #endif
217
218     p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
219     if (!p) {
220          goto error;
221     }
222     p->mh_tag = MEMTAG;
223     p->mh_number = number;
224     p->mh_type = REALLOC_TYPE;
225     p->mh_size = size;
226     p->mh_file = file;
227     p->mh_line = line;
228     debugMemSize += size;
229     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
230 #ifdef MEM_LIST
231     debugmem_list_add(p);
232 #endif
233
234     TEST_POINT
235
236     return(HDR_2_CLIENT(p));
237     
238 error:    
239     return(NULL);
240 }
241
242 /**
243  * xmlRealloc:
244  * @ptr:  the initial memory block pointer
245  * @size:  an int specifying the size in byte to allocate.
246  *
247  * a realloc() equivalent, with logging of the allocation info.
248  *
249  * Returns a pointer to the allocated area or NULL in case of lack of memory.
250  */
251
252 void *
253 xmlRealloc(void *ptr,int size) {
254     return(xmlReallocLoc(ptr, size, "none", 0));
255 }
256
257 /**
258  * xmlFree:
259  * @ptr:  the memory block pointer
260  *
261  * a free() equivalent, with error checking.
262  */
263 void
264 xmlFree(void *ptr)
265 {
266     MEMHDR *p;
267
268     TEST_POINT
269
270     p = CLIENT_2_HDR(ptr);
271     if (p->mh_tag != MEMTAG) {
272        Mem_Tag_Err(p);
273        goto error;
274     }
275     p->mh_tag = ~MEMTAG;
276     debugMemSize -= p->mh_size;
277
278 #ifdef MEM_LIST
279     debugmem_list_delete(p);
280 #endif
281     free(p);
282
283     TEST_POINT
284
285     return;
286     
287 error:    
288     fprintf(stderr, "xmlFree(%X) error\n", (unsigned int) ptr);
289     return;
290 }
291
292 /**
293  * xmlMemStrdupLoc:
294  * @ptr:  the initial string pointer
295  * @file:  the file name or NULL
296  * @file:  the line number
297  *
298  * a strdup() equivalent, with logging of the allocation info.
299  *
300  * Returns a pointer to the new string or NULL if allocation error occured.
301  */
302
303 char *
304 xmlMemStrdupLoc(const char *str, const char *file, int line)
305 {
306     char *s;
307     size_t size = strlen(str) + 1;
308     MEMHDR *p;
309
310     if (!xmlMemInitialized) xmlInitMemory();
311     TEST_POINT
312
313     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
314     if (!p) {
315       goto error;
316     }
317     p->mh_tag = MEMTAG;
318     p->mh_number = ++block;
319     p->mh_size = size;
320     p->mh_type = STRDUP_TYPE;
321     p->mh_file = file;
322     p->mh_line = line;
323     debugMemSize += size;
324     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
325 #ifdef MEM_LIST
326     debugmem_list_add(p);
327 #endif
328     s = HDR_2_CLIENT(p);
329     
330     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
331
332     if (s != NULL)
333       strcpy(s,str);
334     else
335       goto error;
336     
337     TEST_POINT
338
339     return(s);
340
341 error:
342     return(NULL);
343 }
344
345 /**
346  * xmlMemStrdup:
347  * @ptr:  the initial string pointer
348  *
349  * a strdup() equivalent, with logging of the allocation info.
350  *
351  * Returns a pointer to the new string or NULL if allocation error occured.
352  */
353
354 char *
355 xmlMemStrdup(const char *str) {
356     return(xmlMemStrdupLoc(str, "none", 0));
357 }
358
359 /**
360  * xmlMemUsed:
361  *
362  * returns the amount of memory currenly allocated
363  *
364  * Returns an int representing the amount of memory allocated.
365  */
366
367 int
368 xmlMemUsed(void) {
369      return(debugMemSize);
370 }
371
372 /**
373  * xmlMemDisplay:
374  * @fp:  a FILE descriptor used as the output file, if NULL, the result is
375  8       written to the file .memorylist
376  *
377  * show in-extenso the memory blocks allocated
378  */
379
380 void
381 xmlMemDisplay(FILE *fp)
382 {
383 #ifdef MEM_LIST
384     MEMHDR *p;
385     int     idx;
386 #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
387     time_t currentTime;
388     char buf[500];
389     struct tm * tstruct;
390
391     currentTime = time(NULL);
392     tstruct = localtime(&currentTime);
393     strftime(buf, sizeof(buf) - 1, "%c", tstruct);
394     fprintf(fp,"      %s\n\n", buf);
395 #endif
396
397     
398     fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
399             debugMemSize, debugMaxMemSize);
400     fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
401     idx = 0;
402     p = memlist;
403     while (p) {
404           fprintf(fp,"%-5u  %6lu %6u ",idx++,p->mh_number,p->mh_size);
405         switch (p->mh_type) {
406            case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
407            case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
408           case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
409                     default:fprintf(fp,"   ???    in ");break;
410         }
411           if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
412         if (p->mh_tag != MEMTAG)
413               fprintf(fp,"  INVALID");
414         fprintf(fp,"\n");
415         p = p->mh_next;
416     }
417 #else
418     fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
419 #endif
420 }
421
422 #ifdef MEM_LIST
423
424 void debugmem_list_add(MEMHDR *p)
425 {
426      p->mh_next = memlist;
427      p->mh_prev = NULL;
428      if (memlist) memlist->mh_prev = p;
429      memlist = p;
430 #ifdef MEM_LIST_DEBUG
431      if (stderr)
432      Mem_Display(stderr);
433 #endif
434 }
435
436 void debugmem_list_delete(MEMHDR *p)
437 {
438      if (p->mh_next)
439      p->mh_next->mh_prev = p->mh_prev;
440      if (p->mh_prev)
441      p->mh_prev->mh_next = p->mh_next;
442      else memlist = p->mh_next;
443 #ifdef MEM_LIST_DEBUG
444      if (stderr)
445      Mem_Display(stderr);
446 #endif
447 }
448
449 #endif
450
451 /*
452  * debugmem_tag_error : internal error function.
453  */
454  
455 void debugmem_tag_error(void *p)
456 {
457      fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
458 #ifdef MEM_LIST
459      if (stderr)
460      xmlMemDisplay(stderr);
461 #endif
462 }
463
464 FILE *xmlMemoryDumpFile = NULL;
465
466
467 /**
468  * xmlMemoryDump:
469  *
470  * Dump in-extenso the memory blocks allocated to the file .memorylist
471  */
472
473 void
474 xmlMemoryDump(void)
475 {
476     FILE *dump;
477
478     dump = fopen(".memdump", "w");
479     if (dump == NULL) xmlMemoryDumpFile = stdout;
480     else xmlMemoryDumpFile = dump;
481
482     xmlMemDisplay(xmlMemoryDumpFile);
483
484     if (dump != NULL) fclose(dump);
485 }
486
487
488 /****************************************************************
489  *                                                              *
490  *              Initialization Routines                         *
491  *                                                              *
492  ****************************************************************/
493
494 /**
495  * xmlInitMemory:
496  *
497  * Initialize the memory layer.
498  *
499  * Returns 0 on success
500  */
501
502
503 int
504 xmlInitMemory(void)
505 {
506      int ret;
507      
508 #ifdef HAVE_STDLIB_H
509      char *breakpoint;
510
511      breakpoint = getenv("XML_MEM_BREAKPOINT");
512      if (breakpoint != NULL) {
513          sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
514      }
515 #endif     
516     
517 #ifdef DEBUG_MEMORY
518      fprintf(stderr, "xmlInitMemory() Ok\n");
519 #endif     
520      ret = 0;
521      return(ret);
522 }
523
524 #endif /* ! NO_DEBUG_MEMORY */