xmlmemory.c 26.5 KB
Newer Older
1
/*
2
 * xmlmemory.c:  libxml memory allocator wrapper.
3
 *
4
 * daniel@veillard.com
5
6
 */

7
#define IN_LIBXML
Bjorn Reese's avatar
Bjorn Reese committed
8
#include "libxml.h"
9
10
11
12
13
14

#include <string.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
15

16
17
18
#ifdef HAVE_TIME_H
#include <time.h>
#endif
19
20
21
22

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#else
23
24
25
26
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#endif
27

28
29
30
31
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif

32
/* #define DEBUG_MEMORY */
33

34
35
36
/**
 * MEM_LIST:
 *
37
 * keep track of all allocated blocks for error reporting
38
39
 * Always build the memory list !
 */
40
#ifdef DEBUG_MEMORY_LOCATION
41
42
43
#ifndef MEM_LIST
#define MEM_LIST /* keep a list of all the allocated memory blocks */
#endif
44
#endif
45

46
#include <libxml/globals.h>	/* must come before xmlmemory.h */
47
48
#include <libxml/xmlmemory.h>
#include <libxml/xmlerror.h>
49
#include <libxml/threads.h>
50

51
static int xmlMemInitialized = 0;
52
static unsigned long  debugMemSize = 0;
53
static unsigned long  debugMemBlocks = 0;
54
static unsigned long  debugMaxMemSize = 0;
55
static xmlMutexPtr xmlMemMutex = NULL;
56

57
58
59
60
void xmlMallocBreakpoint(void);

/************************************************************************
 *									*
Daniel Veillard's avatar
Daniel Veillard committed
61
 *		Macros, variables and associated types			*
62
63
64
 *									*
 ************************************************************************/

65
#if !defined(LIBXML_THREAD_ENABLED) && !defined(LIBXML_THREAD_ALLOC_ENABLED)
66
67
68
69
70
71
72
73
74
#ifdef xmlMalloc
#undef xmlMalloc
#endif
#ifdef xmlRealloc
#undef xmlRealloc
#endif
#ifdef xmlMemStrdup
#undef xmlMemStrdup
#endif
75
#endif
76
77
78
79
80
81
82
83
84
85

/*
 * Each of the blocks allocated begin with a header containing informations
 */

#define MEMTAG 0x5aa5

#define MALLOC_TYPE 1
#define REALLOC_TYPE 2
#define STRDUP_TYPE 3
86
87
#define MALLOC_ATOMIC_TYPE 4
#define REALLOC_ATOMIC_TYPE 5
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

typedef struct memnod {
    unsigned int   mh_tag;
    unsigned int   mh_type;
    unsigned long  mh_number;
    size_t         mh_size;
#ifdef MEM_LIST
   struct memnod *mh_next;
   struct memnod *mh_prev;
#endif
   const char    *mh_file;
   unsigned int   mh_line;
}  MEMHDR;


#ifdef SUN4
#define ALIGN_SIZE  16
#else
#define ALIGN_SIZE  sizeof(double)
#endif
#define HDR_SIZE    sizeof(MEMHDR)
#define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
		      / ALIGN_SIZE ) * ALIGN_SIZE)

112
#define MAX_SIZE_T ((size_t)-1)
113
114
115
116
117

#define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
#define HDR_2_CLIENT(a)    ((void *) (((char *) (a)) + RESERVE_SIZE))


118
119
static unsigned int block=0;
static unsigned int xmlMemStopAtBlock = 0;
120
static void *xmlMemTraceBlockAt = NULL;
121
122
123
124
#ifdef MEM_LIST
static MEMHDR *memlist = NULL;
#endif

125
static void debugmem_tag_error(void *addr);
126
#ifdef MEM_LIST
127
128
static void  debugmem_list_add(MEMHDR *);
static void debugmem_list_delete(MEMHDR *);
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#endif
#define Mem_Tag_Err(a) debugmem_tag_error(a);

#ifndef TEST_POINT
#define TEST_POINT
#endif

/**
 * xmlMallocBreakpoint:
 *
 * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
 * number reaches the specified value this function is called. One need to add a breakpoint
 * to it to get the context in which the given block is allocated.
 */

void
xmlMallocBreakpoint(void) {
    xmlGenericError(xmlGenericErrorContext,
	    "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
}

/**
 * xmlMallocLoc:
 * @size:  an int specifying the size in byte to allocate.
 * @file:  the file name or NULL
 * @line:  the line number
 *
 * a malloc() equivalent, with logging of the allocation info.
 *
 * Returns a pointer to the allocated area or NULL in case of lack of memory.
 */

void *
162
xmlMallocLoc(size_t size, const char * file, int line)
163
164
{
    MEMHDR *p;
165
    void *ret;
166

167
168
169
170
171
172
173
    if (!xmlMemInitialized) xmlInitMemory();
#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Malloc(%d)\n",size);
#endif

    TEST_POINT
174

175
176
177
178
179
180
181
    if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
	xmlGenericError(xmlGenericErrorContext,
		"xmlMallocLoc : Unsigned overflow\n");
	xmlMemoryDump();
	return(NULL);
    }

182
183
184
185
    p = (MEMHDR *) malloc(RESERVE_SIZE+size);

    if (!p) {
	xmlGenericError(xmlGenericErrorContext,
186
		"xmlMallocLoc : Out of free space\n");
187
188
	xmlMemoryDump();
	return(NULL);
189
    }
190
191
192
193
194
    p->mh_tag = MEMTAG;
    p->mh_size = size;
    p->mh_type = MALLOC_TYPE;
    p->mh_file = file;
    p->mh_line = line;
195
196
    xmlMutexLock(xmlMemMutex);
    p->mh_number = ++block;
197
    debugMemSize += size;
198
    debugMemBlocks++;
199
200
201
202
    if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
#ifdef MEM_LIST
    debugmem_list_add(p);
#endif
203
    xmlMutexUnlock(xmlMemMutex);
204

205
206
207
208
#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Malloc(%d) Ok\n",size);
#endif
209

210
    if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
211

212
213
214
215
    ret = HDR_2_CLIENT(p);

    if (xmlMemTraceBlockAt == ret) {
	xmlGenericError(xmlGenericErrorContext,
216
217
			"%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt,
			(long unsigned)size);
218
219
220
	xmlMallocBreakpoint();
    }

221
222
    TEST_POINT

223
    return(ret);
224
225
}

226
227
/**
 * xmlMallocAtomicLoc:
228
 * @size:  an unsigned int specifying the size in byte to allocate.
229
230
231
232
233
234
235
236
237
238
239
240
241
 * @file:  the file name or NULL
 * @line:  the line number
 *
 * a malloc() equivalent, with logging of the allocation info.
 *
 * Returns a pointer to the allocated area or NULL in case of lack of memory.
 */

void *
xmlMallocAtomicLoc(size_t size, const char * file, int line)
{
    MEMHDR *p;
    void *ret;
242

243
244
245
246
247
248
249
    if (!xmlMemInitialized) xmlInitMemory();
#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Malloc(%d)\n",size);
#endif

    TEST_POINT
250

251
252
253
254
255
256
257
    if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
	xmlGenericError(xmlGenericErrorContext,
		"xmlMallocAtomicLoc : Unsigned overflow prevented\n");
	xmlMemoryDump();
	return(NULL);
    }

258
259
260
261
    p = (MEMHDR *) malloc(RESERVE_SIZE+size);

    if (!p) {
	xmlGenericError(xmlGenericErrorContext,
262
		"xmlMallocAtomicLoc : Out of free space\n");
263
264
	xmlMemoryDump();
	return(NULL);
265
    }
266
267
268
269
270
    p->mh_tag = MEMTAG;
    p->mh_size = size;
    p->mh_type = MALLOC_ATOMIC_TYPE;
    p->mh_file = file;
    p->mh_line = line;
271
272
    xmlMutexLock(xmlMemMutex);
    p->mh_number = ++block;
273
    debugMemSize += size;
274
    debugMemBlocks++;
275
276
277
278
    if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
#ifdef MEM_LIST
    debugmem_list_add(p);
#endif
279
    xmlMutexUnlock(xmlMemMutex);
280
281
282
283
284

#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Malloc(%d) Ok\n",size);
#endif
285

286
    if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
287
288
289
290
291

    ret = HDR_2_CLIENT(p);

    if (xmlMemTraceBlockAt == ret) {
	xmlGenericError(xmlGenericErrorContext,
292
293
			"%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt,
			(long unsigned)size);
294
295
296
297
298
299
300
	xmlMallocBreakpoint();
    }

    TEST_POINT

    return(ret);
}
301
302
303
304
305
306
307
308
309
310
/**
 * xmlMemMalloc:
 * @size:  an int specifying the size in byte to allocate.
 *
 * a malloc() equivalent, with logging of the allocation info.
 *
 * Returns a pointer to the allocated area or NULL in case of lack of memory.
 */

void *
311
xmlMemMalloc(size_t size)
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
{
    return(xmlMallocLoc(size, "none", 0));
}

/**
 * xmlReallocLoc:
 * @ptr:  the initial memory block pointer
 * @size:  an int specifying the size in byte to allocate.
 * @file:  the file name or NULL
 * @line:  the line number
 *
 * a realloc() equivalent, with logging of the allocation info.
 *
 * Returns a pointer to the allocated area or NULL in case of lack of memory.
 */

void *
329
xmlReallocLoc(void *ptr,size_t size, const char * file, int line)
330
{
331
    MEMHDR *p, *tmp;
332
    unsigned long number;
333
334
335
#ifdef DEBUG_MEMORY
    size_t oldsize;
#endif
336

337
    if (ptr == NULL)
338
339
340
        return(xmlMallocLoc(size, file, line));

    if (!xmlMemInitialized) xmlInitMemory();
341
342
343
344
    TEST_POINT

    p = CLIENT_2_HDR(ptr);
    number = p->mh_number;
345
    if (xmlMemStopAtBlock == number) xmlMallocBreakpoint();
346
347
348
349
350
    if (p->mh_tag != MEMTAG) {
       Mem_Tag_Err(p);
	 goto error;
    }
    p->mh_tag = ~MEMTAG;
351
    xmlMutexLock(xmlMemMutex);
352
    debugMemSize -= p->mh_size;
353
    debugMemBlocks--;
354
355
356
#ifdef DEBUG_MEMORY
    oldsize = p->mh_size;
#endif
357
358
359
#ifdef MEM_LIST
    debugmem_list_delete(p);
#endif
360
    xmlMutexUnlock(xmlMemMutex);
361

362
363
364
365
366
367
368
    if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
	xmlGenericError(xmlGenericErrorContext,
		"xmlMallocLoc : Unsigned overflow\n");
	xmlMemoryDump();
	return(NULL);
    }

369
370
371
    tmp = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
    if (!tmp) {
	 free(p);
372
373
	 goto error;
    }
374
    p = tmp;
375
376
    if (xmlMemTraceBlockAt == ptr) {
	xmlGenericError(xmlGenericErrorContext,
377
378
379
			"%p : Realloced(%lu -> %lu) Ok\n",
			xmlMemTraceBlockAt, (long unsigned)p->mh_size,
			(long unsigned)size);
380
381
	xmlMallocBreakpoint();
    }
382
383
384
385
386
387
    p->mh_tag = MEMTAG;
    p->mh_number = number;
    p->mh_type = REALLOC_TYPE;
    p->mh_size = size;
    p->mh_file = file;
    p->mh_line = line;
388
    xmlMutexLock(xmlMemMutex);
389
    debugMemSize += size;
390
    debugMemBlocks++;
391
392
393
394
    if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
#ifdef MEM_LIST
    debugmem_list_add(p);
#endif
395
    xmlMutexUnlock(xmlMemMutex);
396
397
398

    TEST_POINT

399
400
401
402
#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Realloced(%d to %d) Ok\n", oldsize, size);
#endif
403
    return(HDR_2_CLIENT(p));
404
405

error:
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    return(NULL);
}

/**
 * xmlMemRealloc:
 * @ptr:  the initial memory block pointer
 * @size:  an int specifying the size in byte to allocate.
 *
 * a realloc() equivalent, with logging of the allocation info.
 *
 * Returns a pointer to the allocated area or NULL in case of lack of memory.
 */

void *
420
xmlMemRealloc(void *ptr,size_t size) {
421
422
423
424
425
426
427
428
429
430
431
432
433
    return(xmlReallocLoc(ptr, size, "none", 0));
}

/**
 * xmlMemFree:
 * @ptr:  the memory block pointer
 *
 * a free() equivalent, with error checking.
 */
void
xmlMemFree(void *ptr)
{
    MEMHDR *p;
434
    char *target;
435
436
437
#ifdef DEBUG_MEMORY
    size_t size;
#endif
438

439
440
441
    if (ptr == NULL)
	return;

442
443
444
445
446
447
448
449
450
451
452
453
    if (ptr == (void *) -1) {
	xmlGenericError(xmlGenericErrorContext,
	    "trying to free pointer from freed area\n");
        goto error;
    }

    if (xmlMemTraceBlockAt == ptr) {
	xmlGenericError(xmlGenericErrorContext,
			"%p : Freed()\n", xmlMemTraceBlockAt);
	xmlMallocBreakpoint();
    }

454
455
    TEST_POINT

456
457
    target = (char *) ptr;

458
459
    p = CLIENT_2_HDR(ptr);
    if (p->mh_tag != MEMTAG) {
460
461
        Mem_Tag_Err(p);
        goto error;
462
    }
463
    if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
464
    p->mh_tag = ~MEMTAG;
465
    memset(target, -1, p->mh_size);
466
467
    xmlMutexLock(xmlMemMutex);
    debugMemSize -= p->mh_size;
468
    debugMemBlocks--;
469
470
471
#ifdef DEBUG_MEMORY
    size = p->mh_size;
#endif
472
473
474
#ifdef MEM_LIST
    debugmem_list_delete(p);
#endif
475
476
    xmlMutexUnlock(xmlMemMutex);

477
478
479
480
    free(p);

    TEST_POINT

481
482
483
484
#ifdef DEBUG_MEMORY
    xmlGenericError(xmlGenericErrorContext,
	    "Freed(%d) Ok\n", size);
#endif
485

486
    return;
487
488

error:
489
    xmlGenericError(xmlGenericErrorContext,
490
	    "xmlMemFree(%lX) error\n", (unsigned long) ptr);
491
    xmlMallocBreakpoint();
492
493
494
495
496
    return;
}

/**
 * xmlMemStrdupLoc:
497
 * @str:  the initial string pointer
498
499
500
501
502
 * @file:  the file name or NULL
 * @line:  the line number
 *
 * a strdup() equivalent, with logging of the allocation info.
 *
503
 * Returns a pointer to the new string or NULL if allocation error occurred.
504
505
506
507
508
509
510
511
512
513
514
515
 */

char *
xmlMemStrdupLoc(const char *str, const char *file, int line)
{
    char *s;
    size_t size = strlen(str) + 1;
    MEMHDR *p;

    if (!xmlMemInitialized) xmlInitMemory();
    TEST_POINT

516
517
518
519
520
521
522
    if (size > (MAX_SIZE_T - RESERVE_SIZE)) {
	xmlGenericError(xmlGenericErrorContext,
		"xmlMallocLoc : Unsigned overflow\n");
	xmlMemoryDump();
	return(NULL);
    }

523
524
525
526
527
528
529
530
531
    p = (MEMHDR *) malloc(RESERVE_SIZE+size);
    if (!p) {
      goto error;
    }
    p->mh_tag = MEMTAG;
    p->mh_size = size;
    p->mh_type = STRDUP_TYPE;
    p->mh_file = file;
    p->mh_line = line;
532
533
    xmlMutexLock(xmlMemMutex);
    p->mh_number = ++block;
534
    debugMemSize += size;
535
    debugMemBlocks++;
536
537
538
539
    if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
#ifdef MEM_LIST
    debugmem_list_add(p);
#endif
540
    xmlMutexUnlock(xmlMemMutex);
541

542
    s = (char *) HDR_2_CLIENT(p);
543

544
    if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint();
545

Gaurav's avatar
Gaurav committed
546
    strcpy(s,str);
547

548
549
    TEST_POINT

550
551
552
553
554
555
    if (xmlMemTraceBlockAt == s) {
	xmlGenericError(xmlGenericErrorContext,
			"%p : Strdup() Ok\n", xmlMemTraceBlockAt);
	xmlMallocBreakpoint();
    }

556
557
558
559
560
561
562
563
    return(s);

error:
    return(NULL);
}

/**
 * xmlMemoryStrdup:
564
 * @str:  the initial string pointer
565
566
567
 *
 * a strdup() equivalent, with logging of the allocation info.
 *
568
 * Returns a pointer to the new string or NULL if allocation error occurred.
569
570
571
572
573
574
575
576
577
578
 */

char *
xmlMemoryStrdup(const char *str) {
    return(xmlMemStrdupLoc(str, "none", 0));
}

/**
 * xmlMemUsed:
 *
579
 * Provides the amount of memory currently allocated
580
581
582
583
584
585
 *
 * Returns an int representing the amount of memory allocated.
 */

int
xmlMemUsed(void) {
586
587
588
589
590
591
    int res;

    xmlMutexLock(xmlMemMutex);
    res = debugMemSize;
    xmlMutexUnlock(xmlMemMutex);
    return(res);
592
593
}

594
595
596
597
598
599
600
601
602
603
/**
 * xmlMemBlocks:
 *
 * Provides the number of memory areas currently allocated
 *
 * Returns an int representing the number of blocks
 */

int
xmlMemBlocks(void) {
604
605
606
607
608
609
    int res;

    xmlMutexLock(xmlMemMutex);
    res = debugMemBlocks;
    xmlMutexUnlock(xmlMemMutex);
    return(res);
610
611
}

612
613
614
615
616
617
618
619
620
#ifdef MEM_LIST
/**
 * xmlMemContentShow:
 * @fp:  a FILE descriptor used as the output file
 * @p:  a memory block header
 *
 * tries to show some content from the memory block
 */

621
static void
622
623
xmlMemContentShow(FILE *fp, MEMHDR *p)
{
624
625
    int i,j,k,len;
    const char *buf;
626
627
628
629
630

    if (p == NULL) {
	fprintf(fp, " NULL");
	return;
    }
631
632
    len = p->mh_size;
    buf = (const char *) HDR_2_CLIENT(p);
633
634
635

    for (i = 0;i < len;i++) {
        if (buf[i] == 0) break;
636
	if (!isprint((unsigned char) buf[i])) break;
637
638
639
640
641
642
    }
    if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
        if (len >= 4) {
	    MEMHDR *q;
	    void *cur;

643
            for (j = 0;(j < len -3) && (j < 40);j += 4) {
644
645
646
		cur = *((void **) &buf[j]);
		q = CLIENT_2_HDR(cur);
		p = memlist;
647
		k = 0;
648
649
650
		while (p != NULL) {
		    if (p == q) break;
		    p = p->mh_next;
651
		    if (k++ > 100) break;
652
653
654
655
656
657
658
659
660
661
662
		}
		if ((p != NULL) && (p == q)) {
		    fprintf(fp, " pointer to #%lu at index %d",
		            p->mh_number, j);
		    return;
		}
	    }
	}
    } else if ((i == 0) && (buf[i] == 0)) {
        fprintf(fp," null");
    } else {
663
        if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
664
665
666
667
668
669
670
671
672
673
	else {
            fprintf(fp," [");
	    for (j = 0;j < i;j++)
                fprintf(fp,"%c", buf[j]);
            fprintf(fp,"]");
	}
    }
}
#endif

674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
/**
 * xmlMemDisplayLast:
 * @fp:  a FILE descriptor used as the output file, if NULL, the result is
 *       written to the file .memorylist
 * @nbBytes: the amount of memory to dump
 *
 * the last nbBytes of memory allocated and not freed, useful for dumping
 * the memory left allocated between two places at runtime.
 */

void
xmlMemDisplayLast(FILE *fp, long nbBytes)
{
#ifdef MEM_LIST
    MEMHDR *p;
    unsigned idx;
    int     nb = 0;
#endif
    FILE *old_fp = fp;

    if (nbBytes <= 0)
        return;

    if (fp == NULL) {
	fp = fopen(".memorylist", "w");
	if (fp == NULL)
	    return;
    }

#ifdef MEM_LIST
    fprintf(fp,"   Last %li MEMORY ALLOCATED : %lu, MAX was %lu\n",
            nbBytes, debugMemSize, debugMaxMemSize);
    fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
    idx = 0;
    xmlMutexLock(xmlMemMutex);
    p = memlist;
    while ((p) && (nbBytes > 0)) {
	  fprintf(fp,"%-5u  %6lu %6lu ",idx++,p->mh_number,
		  (unsigned long)p->mh_size);
        switch (p->mh_type) {
           case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
           case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
           case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
           case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
           case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
           default:
	        fprintf(fp,"Unknown memory block, may be corrupted");
		xmlMutexUnlock(xmlMemMutex);
		if (old_fp == NULL)
		    fclose(fp);
		return;
        }
	if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
        if (p->mh_tag != MEMTAG)
	      fprintf(fp,"  INVALID");
        nb++;
	if (nb < 100)
	    xmlMemContentShow(fp, p);
	else
	    fprintf(fp," skip");

        fprintf(fp,"\n");
	nbBytes -= (unsigned long)p->mh_size;
        p = p->mh_next;
    }
    xmlMutexUnlock(xmlMemMutex);
#else
    fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
#endif
    if (old_fp == NULL)
	fclose(fp);
}

747
748
749
750
751
752
753
754
755
756
757
758
759
/**
 * xmlMemDisplay:
 * @fp:  a FILE descriptor used as the output file, if NULL, the result is
 *       written to the file .memorylist
 *
 * show in-extenso the memory blocks allocated
 */

void
xmlMemDisplay(FILE *fp)
{
#ifdef MEM_LIST
    MEMHDR *p;
760
    unsigned idx;
761
    int     nb = 0;
762
763
764
765
#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
    time_t currentTime;
    char buf[500];
    struct tm * tstruct;
766
767
768
769
770
771
772
773
774
#endif
#endif
    FILE *old_fp = fp;

    if (fp == NULL) {
	fp = fopen(".memorylist", "w");
	if (fp == NULL)
	    return;
    }
775

776
777
#ifdef MEM_LIST
#if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
778
779
    currentTime = time(NULL);
    tstruct = localtime(&currentTime);
780
    strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p", tstruct);
781
782
783
    fprintf(fp,"      %s\n\n", buf);
#endif

784

785
786
787
788
    fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
            debugMemSize, debugMaxMemSize);
    fprintf(fp,"BLOCK  NUMBER   SIZE  TYPE\n");
    idx = 0;
789
    xmlMutexLock(xmlMemMutex);
790
791
    p = memlist;
    while (p) {
792
793
	  fprintf(fp,"%-5u  %6lu %6lu ",idx++,p->mh_number,
		  (unsigned long)p->mh_size);
794
795
796
        switch (p->mh_type) {
           case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
           case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
797
           case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
798
           case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
799
800
           case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
           default:
801
	        fprintf(fp,"Unknown memory block, may be corrupted");
802
		xmlMutexUnlock(xmlMemMutex);
803
804
		if (old_fp == NULL)
		    fclose(fp);
805
		return;
806
        }
807
	if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
808
809
        if (p->mh_tag != MEMTAG)
	      fprintf(fp,"  INVALID");
810
811
812
813
814
815
        nb++;
	if (nb < 100)
	    xmlMemContentShow(fp, p);
	else
	    fprintf(fp," skip");

816
817
818
        fprintf(fp,"\n");
        p = p->mh_next;
    }
819
    xmlMutexUnlock(xmlMemMutex);
820
821
822
#else
    fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
#endif
823
824
    if (old_fp == NULL)
	fclose(fp);
825
826
827
828
}

#ifdef MEM_LIST

829
static void debugmem_list_add(MEMHDR *p)
830
831
832
833
834
835
836
837
838
839
840
{
     p->mh_next = memlist;
     p->mh_prev = NULL;
     if (memlist) memlist->mh_prev = p;
     memlist = p;
#ifdef MEM_LIST_DEBUG
     if (stderr)
     Mem_Display(stderr);
#endif
}

841
static void debugmem_list_delete(MEMHDR *p)
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
{
     if (p->mh_next)
     p->mh_next->mh_prev = p->mh_prev;
     if (p->mh_prev)
     p->mh_prev->mh_next = p->mh_next;
     else memlist = p->mh_next;
#ifdef MEM_LIST_DEBUG
     if (stderr)
     Mem_Display(stderr);
#endif
}

#endif

/*
857
858
859
 * debugmem_tag_error:
 *
 * internal error function.
860
 */
861

862
static void debugmem_tag_error(void *p)
863
864
865
866
867
868
869
870
871
{
     xmlGenericError(xmlGenericErrorContext,
	     "Memory tag error occurs :%p \n\t bye\n", p);
#ifdef MEM_LIST
     if (stderr)
     xmlMemDisplay(stderr);
#endif
}

872
#ifdef MEM_LIST
873
static FILE *xmlMemoryDumpFile = NULL;
874
#endif
875

876
877
878
879
880
881
882
883
884
885
/**
 * xmlMemShow:
 * @fp:  a FILE descriptor used as the output file
 * @nr:  number of entries to dump
 *
 * show a show display of the memory allocated, and dump
 * the @nr last allocated areas which were not freed
 */

void
886
xmlMemShow(FILE *fp, int nr ATTRIBUTE_UNUSED)
887
888
889
890
891
892
893
894
895
{
#ifdef MEM_LIST
    MEMHDR *p;
#endif

    if (fp != NULL)
	fprintf(fp,"      MEMORY ALLOCATED : %lu, MAX was %lu\n",
		debugMemSize, debugMaxMemSize);
#ifdef MEM_LIST
896
    xmlMutexLock(xmlMemMutex);
897
898
899
900
901
902
903
904
905
906
907
908
909
910
    if (nr > 0) {
	fprintf(fp,"NUMBER   SIZE  TYPE   WHERE\n");
	p = memlist;
	while ((p) && nr > 0) {
	      fprintf(fp,"%6lu %6lu ",p->mh_number,(unsigned long)p->mh_size);
	    switch (p->mh_type) {
	       case STRDUP_TYPE:fprintf(fp,"strdup()  in ");break;
	       case MALLOC_TYPE:fprintf(fp,"malloc()  in ");break;
	       case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc()  in ");break;
	      case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
	      case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break;
		default:fprintf(fp,"   ???    in ");break;
	    }
	    if (p->mh_file != NULL)
911
	        fprintf(fp,"%s(%u)", p->mh_file, p->mh_line);
912
913
914
915
916
917
918
919
	    if (p->mh_tag != MEMTAG)
		fprintf(fp,"  INVALID");
	    xmlMemContentShow(fp, p);
	    fprintf(fp,"\n");
	    nr--;
	    p = p->mh_next;
	}
    }
920
    xmlMutexUnlock(xmlMemMutex);
921
#endif /* MEM_LIST */
922
923
}

924
925
926
927
928
929
930
931
932
/**
 * xmlMemoryDump:
 *
 * Dump in-extenso the memory blocks allocated to the file .memorylist
 */

void
xmlMemoryDump(void)
{
933
#ifdef MEM_LIST
934
935
    FILE *dump;

936
937
    if (debugMaxMemSize == 0)
	return;
938
    dump = fopen(".memdump", "w");
939
940
    if (dump == NULL)
	xmlMemoryDumpFile = stderr;
941
942
943
944
945
    else xmlMemoryDumpFile = dump;

    xmlMemDisplay(xmlMemoryDumpFile);

    if (dump != NULL) fclose(dump);
946
#endif /* MEM_LIST */
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
}


/****************************************************************
 *								*
 *		Initialization Routines				*
 *								*
 ****************************************************************/

/**
 * xmlInitMemory:
 *
 * Initialize the memory layer.
 *
 * Returns 0 on success
 */
int
xmlInitMemory(void)
{
966
967
#ifdef HAVE_STDLIB_H
     char *breakpoint;
968
#endif
969
970
971
#ifdef DEBUG_MEMORY
     xmlGenericError(xmlGenericErrorContext,
	     "xmlInitMemory()\n");
972
#endif
973
974
975
976
977
    /*
     This is really not good code (see Bug 130419).  Suggestions for
     improvement will be welcome!
    */
     if (xmlMemInitialized) return(-1);
978
979
     xmlMemInitialized = 1;
     xmlMemMutex = xmlNewMutex();
980
981
982
983

#ifdef HAVE_STDLIB_H
     breakpoint = getenv("XML_MEM_BREAKPOINT");
     if (breakpoint != NULL) {
984
         sscanf(breakpoint, "%ud", &xmlMemStopAtBlock);
985
     }
986
#endif
987
988
989
990
991
#ifdef HAVE_STDLIB_H
     breakpoint = getenv("XML_MEM_TRACE");
     if (breakpoint != NULL) {
         sscanf(breakpoint, "%p", &xmlMemTraceBlockAt);
     }
992
993
#endif

994
995
996
#ifdef DEBUG_MEMORY
     xmlGenericError(xmlGenericErrorContext,
	     "xmlInitMemory() Ok\n");
997
#endif
998
     return(0);
999
1000
}