- Lots of improvements, too long to list here
[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 #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
29
30 #include "xmlmemory.h"
31
32 #ifndef NO_DEBUG_MEMORY
33 #ifdef xmlMalloc
34 #undef xmlMalloc
35 #endif
36 #ifdef xmlRealloc
37 #undef xmlRealloc
38 #endif
39 #ifdef xmlMemStrdup
40 #undef xmlMemStrdup
41 #endif
42
43 extern 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
55 typedef 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
83 static unsigned long  debugMemSize = 0;
84 static unsigned long  debugMaxMemSize = 0;
85 static int block=0;
86 int xmlMemStopAtBlock = 0;
87 int xmlMemInitialized = 0;
88 #ifdef MEM_LIST
89 static MEMHDR *memlist = NULL;
90 #endif
91
92 void debugmem_tag_error(void *addr);
93 #ifdef MEM_LIST
94 void  debugmem_list_add(MEMHDR *);
95 void 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 /**
104  * 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
111 void
112 xmlMallocBreakpoint(void) {
113     fprintf(stderr, "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
114 }
115
116 /**
117  * xmlMallocLoc:
118  * @size:  an int specifying the size in byte to allocate.
119  * @file:  the file name or NULL
120   @file:  the line number
121  *
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
127 void *
128 xmlMallocLoc(int size, const char * file, int line)
129 {
130     MEMHDR *p;
131     
132     if (!xmlMemInitialized) xmlInitMemory();
133 #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) {
142         fprintf(stderr, "xmlMalloc : Out of free space\n");
143         xmlMemoryDump();
144         return(NULL);
145     }   
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;
153     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
154 #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     
162     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
163
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
178 void *
179 xmlMalloc(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
196 void *
197 xmlReallocLoc(void *ptr,int size, const char * file, int line)
198 {
199     MEMHDR *p;
200     unsigned long number;
201
202     if (!xmlMemInitialized) xmlInitMemory();
203     TEST_POINT
204
205     p = CLIENT_2_HDR(ptr);
206     number = p->mh_number;
207     if (p->mh_tag != MEMTAG) {
208        Mem_Tag_Err(p);
209          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;
228     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
229 #ifdef MEM_LIST
230     debugmem_list_add(p);
231 #endif
232
233     TEST_POINT
234
235     return(HDR_2_CLIENT(p));
236     
237 error:    
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
251 void *
252 xmlRealloc(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.
261  */
262 void
263 xmlFree(void *ptr)
264 {
265     MEMHDR *p;
266
267     TEST_POINT
268
269     p = CLIENT_2_HDR(ptr);
270     if (p->mh_tag != MEMTAG) {
271        Mem_Tag_Err(p);
272        goto error;
273     }
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     
286 error:    
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
302 char *
303 xmlMemStrdupLoc(const char *str, const char *file, int line)
304 {
305     char *s;
306     size_t size = strlen(str) + 1;
307     MEMHDR *p;
308
309     if (!xmlMemInitialized) xmlInitMemory();
310     TEST_POINT
311
312     p = (MEMHDR *) malloc(RESERVE_SIZE+size);
313     if (!p) {
314       goto error;
315     }
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;
323     if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
324 #ifdef MEM_LIST
325     debugmem_list_add(p);
326 #endif
327     s = HDR_2_CLIENT(p);
328     
329     if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
330
331     if (s != NULL)
332       strcpy(s,str);
333     else
334       goto error;
335     
336     TEST_POINT
337
338     return(s);
339
340 error:
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
353 char *
354 xmlMemStrdup(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
366 int
367 xmlMemUsed(void) {
368      return(debugMemSize);
369 }
370
371 /**
372  * 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
380 void
381 xmlMemShow(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 /**
415  * xmlMemDisplay:
416  * @fp:  a FILE descriptor used as the output file, if NULL, the result is
417  *       written to the file .memorylist
418  *
419  * show in-extenso the memory blocks allocated
420  */
421
422 void
423 xmlMemDisplay(FILE *fp)
424 {
425 #ifdef MEM_LIST
426     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
439     
440     fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
441             debugMemSize, debugMaxMemSize);
442     fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
443     idx = 0;
444     p = memlist;
445     while (p) {
446           fprintf(fp,"%-5u  %6lu %6u ",idx++,p->mh_number,p->mh_size);
447         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         }
453           if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
454         if (p->mh_tag != MEMTAG)
455               fprintf(fp,"  INVALID");
456         fprintf(fp,"\n");
457         p = p->mh_next;
458     }
459 #else
460     fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
461 #endif
462 }
463
464 #ifdef MEM_LIST
465
466 void debugmem_list_add(MEMHDR *p)
467 {
468      p->mh_next = memlist;
469      p->mh_prev = NULL;
470      if (memlist) memlist->mh_prev = p;
471      memlist = p;
472 #ifdef MEM_LIST_DEBUG
473      if (stderr)
474      Mem_Display(stderr);
475 #endif
476 }
477
478 void debugmem_list_delete(MEMHDR *p)
479 {
480      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;
485 #ifdef MEM_LIST_DEBUG
486      if (stderr)
487      Mem_Display(stderr);
488 #endif
489 }
490
491 #endif
492
493 /*
494  * debugmem_tag_error : internal error function.
495  */
496  
497 void debugmem_tag_error(void *p)
498 {
499      fprintf(stderr, "Memory tag error occurs :%p \n\t bye\n", p);
500 #ifdef MEM_LIST
501      if (stderr)
502      xmlMemDisplay(stderr);
503 #endif
504 }
505
506 FILE *xmlMemoryDumpFile = NULL;
507
508
509 /**
510  * xmlMemoryDump:
511  *
512  * Dump in-extenso the memory blocks allocated to the file .memorylist
513  */
514
515 void
516 xmlMemoryDump(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.
540  *
541  * Returns 0 on success
542  */
543
544
545 int
546 xmlInitMemory(void)
547 {
548      int ret;
549      
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     
558     
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 */