1 | /* |
2 | * xmlmemory.c: libxml memory allocator wrapper. |
3 | * |
4 | * daniel@veillard.com |
5 | */ |
6 | |
7 | #define IN_LIBXML |
8 | #include "libxml.h" |
9 | |
10 | #include <string.h> |
11 | |
12 | #ifdef HAVE_SYS_TYPES_H |
13 | #include <sys/types.h> |
14 | #endif |
15 | |
16 | #ifdef HAVE_TIME_H |
17 | #include <time.h> |
18 | #endif |
19 | |
20 | #ifdef HAVE_STDLIB_H |
21 | #include <stdlib.h> |
22 | #else |
23 | #ifdef HAVE_MALLOC_H |
24 | #include <malloc.h> |
25 | #endif |
26 | #endif |
27 | |
28 | #ifdef HAVE_CTYPE_H |
29 | #include <ctype.h> |
30 | #endif |
31 | |
32 | /* #define DEBUG_MEMORY */ |
33 | |
34 | /** |
35 | * MEM_LIST: |
36 | * |
37 | * keep track of all allocated blocks for error reporting |
38 | * Always build the memory list ! |
39 | */ |
40 | #ifdef DEBUG_MEMORY_LOCATION |
41 | #ifndef MEM_LIST |
42 | #define MEM_LIST /* keep a list of all the allocated memory blocks */ |
43 | #endif |
44 | #endif |
45 | |
46 | #include <libxml/globals.h> /* must come before xmlmemory.h */ |
47 | #include <libxml/xmlmemory.h> |
48 | #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 | void xmlMallocBreakpoint(void); |
58 | |
59 | /************************************************************************ |
60 | * * |
61 | * Macros, variables and associated types * |
62 | * * |
63 | ************************************************************************/ |
64 | |
65 | #if !defined(LIBXML_THREAD_ENABLED) && !defined(LIBXML_THREAD_ALLOC_ENABLED) |
66 | #ifdef xmlMalloc |
67 | #undef xmlMalloc |
68 | #endif |
69 | #ifdef xmlRealloc |
70 | #undef xmlRealloc |
71 | #endif |
72 | #ifdef xmlMemStrdup |
73 | #undef xmlMemStrdup |
74 | #endif |
75 | #endif |
76 | |
77 | /* |
78 | * Each of the blocks allocated begin with a header containing informations |
79 | */ |
80 | |
81 | #define MEMTAG 0x5aa5 |
82 | |
83 | #define MALLOC_TYPE 1 |
84 | #define REALLOC_TYPE 2 |
85 | #define STRDUP_TYPE 3 |
86 | #define MALLOC_ATOMIC_TYPE 4 |
87 | #define REALLOC_ATOMIC_TYPE 5 |
88 | |
89 | typedef struct memnod { |
90 | unsigned int mh_tag; |
91 | unsigned int mh_type; |
92 | unsigned long mh_number; |
93 | size_t mh_size; |
94 | #ifdef MEM_LIST |
95 | struct memnod *mh_next; |
96 | struct memnod *mh_prev; |
97 | #endif |
98 | const char *mh_file; |
99 | unsigned int mh_line; |
100 | } MEMHDR; |
101 | |
102 | |
103 | #ifdef SUN4 |
104 | #define ALIGN_SIZE 16 |
105 | #else |
106 | #define ALIGN_SIZE sizeof(double) |
107 | #endif |
108 | #define HDR_SIZE sizeof(MEMHDR) |
109 | #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \ |
110 | / ALIGN_SIZE ) * ALIGN_SIZE) |
111 | |
112 | #define MAX_SIZE_T ((size_t)-1) |
113 | |
114 | #define CLIENT_2_HDR(a) ((void *) (((char *) (a)) - RESERVE_SIZE)) |
115 | #define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE)) |
116 | |
117 | |
118 | static unsigned int block=0; |
119 | static unsigned int xmlMemStopAtBlock = 0; |
120 | static void *xmlMemTraceBlockAt = NULL; |
121 | #ifdef MEM_LIST |
122 | static MEMHDR *memlist = NULL; |
123 | #endif |
124 | |
125 | static void debugmem_tag_error(void *addr); |
126 | #ifdef MEM_LIST |
127 | static void debugmem_list_add(MEMHDR *); |
128 | static void debugmem_list_delete(MEMHDR *); |
129 | #endif |
130 | #define Mem_Tag_Err(a) debugmem_tag_error(a); |
131 | |
132 | #ifndef TEST_POINT |
133 | #define TEST_POINT |
134 | #endif |
135 | |
136 | /** |
137 | * xmlMallocBreakpoint: |
138 | * |
139 | * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block |
140 | * number reaches the specified value this function is called. One need to add a breakpoint |
141 | * to it to get the context in which the given block is allocated. |
142 | */ |
143 | |
144 | void |
145 | xmlMallocBreakpoint(void) { |
146 | xmlGenericError(xmlGenericErrorContext, |
147 | "xmlMallocBreakpoint reached on block %d\n" , xmlMemStopAtBlock); |
148 | } |
149 | |
150 | /** |
151 | * xmlMallocLoc: |
152 | * @size: an int specifying the size in byte to allocate. |
153 | * @file: the file name or NULL |
154 | * @line: the line number |
155 | * |
156 | * a malloc() equivalent, with logging of the allocation info. |
157 | * |
158 | * Returns a pointer to the allocated area or NULL in case of lack of memory. |
159 | */ |
160 | |
161 | void * |
162 | xmlMallocLoc(size_t size, const char * file, int line) |
163 | { |
164 | MEMHDR *p; |
165 | void *ret; |
166 | |
167 | if (!xmlMemInitialized) xmlInitMemory(); |
168 | #ifdef DEBUG_MEMORY |
169 | xmlGenericError(xmlGenericErrorContext, |
170 | "Malloc(%d)\n" ,size); |
171 | #endif |
172 | |
173 | TEST_POINT |
174 | |
175 | if (size > (MAX_SIZE_T - RESERVE_SIZE)) { |
176 | xmlGenericError(xmlGenericErrorContext, |
177 | "xmlMallocLoc : Unsigned overflow\n" ); |
178 | xmlMemoryDump(); |
179 | return(NULL); |
180 | } |
181 | |
182 | p = (MEMHDR *) malloc(RESERVE_SIZE+size); |
183 | |
184 | if (!p) { |
185 | xmlGenericError(xmlGenericErrorContext, |
186 | "xmlMallocLoc : Out of free space\n" ); |
187 | xmlMemoryDump(); |
188 | return(NULL); |
189 | } |
190 | p->mh_tag = MEMTAG; |
191 | p->mh_size = size; |
192 | p->mh_type = MALLOC_TYPE; |
193 | p->mh_file = file; |
194 | p->mh_line = line; |
195 | xmlMutexLock(xmlMemMutex); |
196 | p->mh_number = ++block; |
197 | debugMemSize += size; |
198 | debugMemBlocks++; |
199 | if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; |
200 | #ifdef MEM_LIST |
201 | debugmem_list_add(p); |
202 | #endif |
203 | xmlMutexUnlock(xmlMemMutex); |
204 | |
205 | #ifdef DEBUG_MEMORY |
206 | xmlGenericError(xmlGenericErrorContext, |
207 | "Malloc(%d) Ok\n" ,size); |
208 | #endif |
209 | |
210 | if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); |
211 | |
212 | ret = HDR_2_CLIENT(p); |
213 | |
214 | if (xmlMemTraceBlockAt == ret) { |
215 | xmlGenericError(xmlGenericErrorContext, |
216 | "%p : Malloc(%lu) Ok\n" , xmlMemTraceBlockAt, |
217 | (long unsigned)size); |
218 | xmlMallocBreakpoint(); |
219 | } |
220 | |
221 | TEST_POINT |
222 | |
223 | return(ret); |
224 | } |
225 | |
226 | /** |
227 | * xmlMallocAtomicLoc: |
228 | * @size: an unsigned int specifying the size in byte to allocate. |
229 | * @file: the file name or NULL |
230 | * @line: the line number |
231 | * |
232 | * a malloc() equivalent, with logging of the allocation info. |
233 | * |
234 | * Returns a pointer to the allocated area or NULL in case of lack of memory. |
235 | */ |
236 | |
237 | void * |
238 | xmlMallocAtomicLoc(size_t size, const char * file, int line) |
239 | { |
240 | MEMHDR *p; |
241 | void *ret; |
242 | |
243 | if (!xmlMemInitialized) xmlInitMemory(); |
244 | #ifdef DEBUG_MEMORY |
245 | xmlGenericError(xmlGenericErrorContext, |
246 | "Malloc(%d)\n" ,size); |
247 | #endif |
248 | |
249 | TEST_POINT |
250 | |
251 | if (size > (MAX_SIZE_T - RESERVE_SIZE)) { |
252 | xmlGenericError(xmlGenericErrorContext, |
253 | "xmlMallocAtomicLoc : Unsigned overflow\n" ); |
254 | xmlMemoryDump(); |
255 | return(NULL); |
256 | } |
257 | |
258 | p = (MEMHDR *) malloc(RESERVE_SIZE+size); |
259 | |
260 | if (!p) { |
261 | xmlGenericError(xmlGenericErrorContext, |
262 | "xmlMallocAtomicLoc : Out of free space\n" ); |
263 | xmlMemoryDump(); |
264 | return(NULL); |
265 | } |
266 | p->mh_tag = MEMTAG; |
267 | p->mh_size = size; |
268 | p->mh_type = MALLOC_ATOMIC_TYPE; |
269 | p->mh_file = file; |
270 | p->mh_line = line; |
271 | xmlMutexLock(xmlMemMutex); |
272 | p->mh_number = ++block; |
273 | debugMemSize += size; |
274 | debugMemBlocks++; |
275 | if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; |
276 | #ifdef MEM_LIST |
277 | debugmem_list_add(p); |
278 | #endif |
279 | xmlMutexUnlock(xmlMemMutex); |
280 | |
281 | #ifdef DEBUG_MEMORY |
282 | xmlGenericError(xmlGenericErrorContext, |
283 | "Malloc(%d) Ok\n" ,size); |
284 | #endif |
285 | |
286 | if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); |
287 | |
288 | ret = HDR_2_CLIENT(p); |
289 | |
290 | if (xmlMemTraceBlockAt == ret) { |
291 | xmlGenericError(xmlGenericErrorContext, |
292 | "%p : Malloc(%lu) Ok\n" , xmlMemTraceBlockAt, |
293 | (long unsigned)size); |
294 | xmlMallocBreakpoint(); |
295 | } |
296 | |
297 | TEST_POINT |
298 | |
299 | return(ret); |
300 | } |
301 | /** |
302 | * xmlMemMalloc: |
303 | * @size: an int specifying the size in byte to allocate. |
304 | * |
305 | * a malloc() equivalent, with logging of the allocation info. |
306 | * |
307 | * Returns a pointer to the allocated area or NULL in case of lack of memory. |
308 | */ |
309 | |
310 | void * |
311 | xmlMemMalloc(size_t size) |
312 | { |
313 | return(xmlMallocLoc(size, "none" , 0)); |
314 | } |
315 | |
316 | /** |
317 | * xmlReallocLoc: |
318 | * @ptr: the initial memory block pointer |
319 | * @size: an int specifying the size in byte to allocate. |
320 | * @file: the file name or NULL |
321 | * @line: the line number |
322 | * |
323 | * a realloc() equivalent, with logging of the allocation info. |
324 | * |
325 | * Returns a pointer to the allocated area or NULL in case of lack of memory. |
326 | */ |
327 | |
328 | void * |
329 | xmlReallocLoc(void *ptr,size_t size, const char * file, int line) |
330 | { |
331 | MEMHDR *p, *tmp; |
332 | unsigned long number; |
333 | #ifdef DEBUG_MEMORY |
334 | size_t oldsize; |
335 | #endif |
336 | |
337 | if (ptr == NULL) |
338 | return(xmlMallocLoc(size, file, line)); |
339 | |
340 | if (!xmlMemInitialized) xmlInitMemory(); |
341 | TEST_POINT |
342 | |
343 | p = CLIENT_2_HDR(ptr); |
344 | number = p->mh_number; |
345 | if (xmlMemStopAtBlock == number) xmlMallocBreakpoint(); |
346 | if (p->mh_tag != MEMTAG) { |
347 | Mem_Tag_Err(p); |
348 | goto error; |
349 | } |
350 | p->mh_tag = ~MEMTAG; |
351 | xmlMutexLock(xmlMemMutex); |
352 | debugMemSize -= p->mh_size; |
353 | debugMemBlocks--; |
354 | #ifdef DEBUG_MEMORY |
355 | oldsize = p->mh_size; |
356 | #endif |
357 | #ifdef MEM_LIST |
358 | debugmem_list_delete(p); |
359 | #endif |
360 | xmlMutexUnlock(xmlMemMutex); |
361 | |
362 | if (size > (MAX_SIZE_T - RESERVE_SIZE)) { |
363 | xmlGenericError(xmlGenericErrorContext, |
364 | "xmlReallocLoc : Unsigned overflow\n" ); |
365 | xmlMemoryDump(); |
366 | return(NULL); |
367 | } |
368 | |
369 | tmp = (MEMHDR *) realloc(p,RESERVE_SIZE+size); |
370 | if (!tmp) { |
371 | free(p); |
372 | goto error; |
373 | } |
374 | p = tmp; |
375 | if (xmlMemTraceBlockAt == ptr) { |
376 | xmlGenericError(xmlGenericErrorContext, |
377 | "%p : Realloced(%lu -> %lu) Ok\n" , |
378 | xmlMemTraceBlockAt, (long unsigned)p->mh_size, |
379 | (long unsigned)size); |
380 | xmlMallocBreakpoint(); |
381 | } |
382 | p->mh_tag = MEMTAG; |
383 | p->mh_number = number; |
384 | p->mh_type = REALLOC_TYPE; |
385 | p->mh_size = size; |
386 | p->mh_file = file; |
387 | p->mh_line = line; |
388 | xmlMutexLock(xmlMemMutex); |
389 | debugMemSize += size; |
390 | debugMemBlocks++; |
391 | if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; |
392 | #ifdef MEM_LIST |
393 | debugmem_list_add(p); |
394 | #endif |
395 | xmlMutexUnlock(xmlMemMutex); |
396 | |
397 | TEST_POINT |
398 | |
399 | #ifdef DEBUG_MEMORY |
400 | xmlGenericError(xmlGenericErrorContext, |
401 | "Realloced(%d to %d) Ok\n" , oldsize, size); |
402 | #endif |
403 | return(HDR_2_CLIENT(p)); |
404 | |
405 | error: |
406 | return(NULL); |
407 | } |
408 | |
409 | /** |
410 | * xmlMemRealloc: |
411 | * @ptr: the initial memory block pointer |
412 | * @size: an int specifying the size in byte to allocate. |
413 | * |
414 | * a realloc() equivalent, with logging of the allocation info. |
415 | * |
416 | * Returns a pointer to the allocated area or NULL in case of lack of memory. |
417 | */ |
418 | |
419 | void * |
420 | xmlMemRealloc(void *ptr,size_t size) { |
421 | return(xmlReallocLoc(ptr, size, "none" , 0)); |
422 | } |
423 | |
424 | /** |
425 | * xmlMemFree: |
426 | * @ptr: the memory block pointer |
427 | * |
428 | * a free() equivalent, with error checking. |
429 | */ |
430 | void |
431 | xmlMemFree(void *ptr) |
432 | { |
433 | MEMHDR *p; |
434 | char *target; |
435 | #ifdef DEBUG_MEMORY |
436 | size_t size; |
437 | #endif |
438 | |
439 | if (ptr == NULL) |
440 | return; |
441 | |
442 | if (ptr == (void *) -1) { |
443 | xmlGenericError(xmlGenericErrorContext, |
444 | "trying to free pointer from freed area\n" ); |
445 | goto error; |
446 | } |
447 | |
448 | if (xmlMemTraceBlockAt == ptr) { |
449 | xmlGenericError(xmlGenericErrorContext, |
450 | "%p : Freed()\n" , xmlMemTraceBlockAt); |
451 | xmlMallocBreakpoint(); |
452 | } |
453 | |
454 | TEST_POINT |
455 | |
456 | target = (char *) ptr; |
457 | |
458 | p = CLIENT_2_HDR(ptr); |
459 | if (p->mh_tag != MEMTAG) { |
460 | Mem_Tag_Err(p); |
461 | goto error; |
462 | } |
463 | if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); |
464 | p->mh_tag = ~MEMTAG; |
465 | memset(target, -1, p->mh_size); |
466 | xmlMutexLock(xmlMemMutex); |
467 | debugMemSize -= p->mh_size; |
468 | debugMemBlocks--; |
469 | #ifdef DEBUG_MEMORY |
470 | size = p->mh_size; |
471 | #endif |
472 | #ifdef MEM_LIST |
473 | debugmem_list_delete(p); |
474 | #endif |
475 | xmlMutexUnlock(xmlMemMutex); |
476 | |
477 | free(p); |
478 | |
479 | TEST_POINT |
480 | |
481 | #ifdef DEBUG_MEMORY |
482 | xmlGenericError(xmlGenericErrorContext, |
483 | "Freed(%d) Ok\n" , size); |
484 | #endif |
485 | |
486 | return; |
487 | |
488 | error: |
489 | xmlGenericError(xmlGenericErrorContext, |
490 | "xmlMemFree(%p) error\n" , ptr); |
491 | xmlMallocBreakpoint(); |
492 | return; |
493 | } |
494 | |
495 | /** |
496 | * xmlMemStrdupLoc: |
497 | * @str: the initial string pointer |
498 | * @file: the file name or NULL |
499 | * @line: the line number |
500 | * |
501 | * a strdup() equivalent, with logging of the allocation info. |
502 | * |
503 | * Returns a pointer to the new string or NULL if allocation error occurred. |
504 | */ |
505 | |
506 | char * |
507 | xmlMemStrdupLoc(const char *str, const char *file, int line) |
508 | { |
509 | char *s; |
510 | size_t size = strlen(str) + 1; |
511 | MEMHDR *p; |
512 | |
513 | if (!xmlMemInitialized) xmlInitMemory(); |
514 | TEST_POINT |
515 | |
516 | if (size > (MAX_SIZE_T - RESERVE_SIZE)) { |
517 | xmlGenericError(xmlGenericErrorContext, |
518 | "xmlMemStrdupLoc : Unsigned overflow\n" ); |
519 | xmlMemoryDump(); |
520 | return(NULL); |
521 | } |
522 | |
523 | p = (MEMHDR *) malloc(RESERVE_SIZE+size); |
524 | if (!p) { |
525 | goto error; |
526 | } |
527 | p->mh_tag = MEMTAG; |
528 | p->mh_size = size; |
529 | p->mh_type = STRDUP_TYPE; |
530 | p->mh_file = file; |
531 | p->mh_line = line; |
532 | xmlMutexLock(xmlMemMutex); |
533 | p->mh_number = ++block; |
534 | debugMemSize += size; |
535 | debugMemBlocks++; |
536 | if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; |
537 | #ifdef MEM_LIST |
538 | debugmem_list_add(p); |
539 | #endif |
540 | xmlMutexUnlock(xmlMemMutex); |
541 | |
542 | s = (char *) HDR_2_CLIENT(p); |
543 | |
544 | if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); |
545 | |
546 | strcpy(s,str); |
547 | |
548 | TEST_POINT |
549 | |
550 | if (xmlMemTraceBlockAt == s) { |
551 | xmlGenericError(xmlGenericErrorContext, |
552 | "%p : Strdup() Ok\n" , xmlMemTraceBlockAt); |
553 | xmlMallocBreakpoint(); |
554 | } |
555 | |
556 | return(s); |
557 | |
558 | error: |
559 | return(NULL); |
560 | } |
561 | |
562 | /** |
563 | * xmlMemoryStrdup: |
564 | * @str: the initial string pointer |
565 | * |
566 | * a strdup() equivalent, with logging of the allocation info. |
567 | * |
568 | * Returns a pointer to the new string or NULL if allocation error occurred. |
569 | */ |
570 | |
571 | char * |
572 | xmlMemoryStrdup(const char *str) { |
573 | return(xmlMemStrdupLoc(str, "none" , 0)); |
574 | } |
575 | |
576 | /** |
577 | * xmlMemUsed: |
578 | * |
579 | * Provides the amount of memory currently allocated |
580 | * |
581 | * Returns an int representing the amount of memory allocated. |
582 | */ |
583 | |
584 | int |
585 | xmlMemUsed(void) { |
586 | int res; |
587 | |
588 | xmlMutexLock(xmlMemMutex); |
589 | res = debugMemSize; |
590 | xmlMutexUnlock(xmlMemMutex); |
591 | return(res); |
592 | } |
593 | |
594 | /** |
595 | * xmlMemBlocks: |
596 | * |
597 | * Provides the number of memory areas currently allocated |
598 | * |
599 | * Returns an int representing the number of blocks |
600 | */ |
601 | |
602 | int |
603 | xmlMemBlocks(void) { |
604 | int res; |
605 | |
606 | xmlMutexLock(xmlMemMutex); |
607 | res = debugMemBlocks; |
608 | xmlMutexUnlock(xmlMemMutex); |
609 | return(res); |
610 | } |
611 | |
612 | #ifdef MEM_LIST |
613 | /** |
614 | * xmlMemContentShow: |
615 | * @fp: a FILE descriptor used as the output file |
616 | * @p: a memory block header |
617 | * |
618 | * tries to show some content from the memory block |
619 | */ |
620 | |
621 | static void |
622 | xmlMemContentShow(FILE *fp, MEMHDR *p) |
623 | { |
624 | int i,j,k,len; |
625 | const char *buf; |
626 | |
627 | if (p == NULL) { |
628 | fprintf(fp, " NULL" ); |
629 | return; |
630 | } |
631 | len = p->mh_size; |
632 | buf = (const char *) HDR_2_CLIENT(p); |
633 | |
634 | for (i = 0;i < len;i++) { |
635 | if (buf[i] == 0) break; |
636 | if (!isprint((unsigned char) buf[i])) break; |
637 | } |
638 | if ((i < 4) && ((buf[i] != 0) || (i == 0))) { |
639 | if (len >= 4) { |
640 | MEMHDR *q; |
641 | void *cur; |
642 | |
643 | for (j = 0;(j < len -3) && (j < 40);j += 4) { |
644 | cur = *((void **) &buf[j]); |
645 | q = CLIENT_2_HDR(cur); |
646 | p = memlist; |
647 | k = 0; |
648 | while (p != NULL) { |
649 | if (p == q) break; |
650 | p = p->mh_next; |
651 | if (k++ > 100) break; |
652 | } |
653 | if ((p != NULL) && (p == q)) { |
654 | fprintf(fp, " pointer to #%lu at index %d" , |
655 | p->mh_number, j); |
656 | return; |
657 | } |
658 | } |
659 | } |
660 | } else if ((i == 0) && (buf[i] == 0)) { |
661 | fprintf(fp," null" ); |
662 | } else { |
663 | if (buf[i] == 0) fprintf(fp," \"%.25s\"" , buf); |
664 | else { |
665 | fprintf(fp," [" ); |
666 | for (j = 0;j < i;j++) |
667 | fprintf(fp,"%c" , buf[j]); |
668 | fprintf(fp,"]" ); |
669 | } |
670 | } |
671 | } |
672 | #endif |
673 | |
674 | /** |
675 | * xmlMemDisplayLast: |
676 | * @fp: a FILE descriptor used as the output file, if NULL, the result is |
677 | * written to the file .memorylist |
678 | * @nbBytes: the amount of memory to dump |
679 | * |
680 | * the last nbBytes of memory allocated and not freed, useful for dumping |
681 | * the memory left allocated between two places at runtime. |
682 | */ |
683 | |
684 | void |
685 | xmlMemDisplayLast(FILE *fp, long nbBytes) |
686 | { |
687 | #ifdef MEM_LIST |
688 | MEMHDR *p; |
689 | unsigned idx; |
690 | int nb = 0; |
691 | #endif |
692 | FILE *old_fp = fp; |
693 | |
694 | if (nbBytes <= 0) |
695 | return; |
696 | |
697 | if (fp == NULL) { |
698 | fp = fopen(".memorylist" , "w" ); |
699 | if (fp == NULL) |
700 | return; |
701 | } |
702 | |
703 | #ifdef MEM_LIST |
704 | fprintf(fp," Last %li MEMORY ALLOCATED : %lu, MAX was %lu\n" , |
705 | nbBytes, debugMemSize, debugMaxMemSize); |
706 | fprintf(fp,"BLOCK NUMBER SIZE TYPE\n" ); |
707 | idx = 0; |
708 | xmlMutexLock(xmlMemMutex); |
709 | p = memlist; |
710 | while ((p) && (nbBytes > 0)) { |
711 | fprintf(fp,"%-5u %6lu %6lu " ,idx++,p->mh_number, |
712 | (unsigned long)p->mh_size); |
713 | switch (p->mh_type) { |
714 | case STRDUP_TYPE:fprintf(fp,"strdup() in " );break; |
715 | case MALLOC_TYPE:fprintf(fp,"malloc() in " );break; |
716 | case REALLOC_TYPE:fprintf(fp,"realloc() in " );break; |
717 | case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc() in " );break; |
718 | case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in " );break; |
719 | default: |
720 | fprintf(fp,"Unknown memory block, may be corrupted" ); |
721 | xmlMutexUnlock(xmlMemMutex); |
722 | if (old_fp == NULL) |
723 | fclose(fp); |
724 | return; |
725 | } |
726 | if (p->mh_file != NULL) fprintf(fp,"%s(%u)" , p->mh_file, p->mh_line); |
727 | if (p->mh_tag != MEMTAG) |
728 | fprintf(fp," INVALID" ); |
729 | nb++; |
730 | if (nb < 100) |
731 | xmlMemContentShow(fp, p); |
732 | else |
733 | fprintf(fp," skip" ); |
734 | |
735 | fprintf(fp,"\n" ); |
736 | nbBytes -= (unsigned long)p->mh_size; |
737 | p = p->mh_next; |
738 | } |
739 | xmlMutexUnlock(xmlMemMutex); |
740 | #else |
741 | fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n" ); |
742 | #endif |
743 | if (old_fp == NULL) |
744 | fclose(fp); |
745 | } |
746 | |
747 | /** |
748 | * xmlMemDisplay: |
749 | * @fp: a FILE descriptor used as the output file, if NULL, the result is |
750 | * written to the file .memorylist |
751 | * |
752 | * show in-extenso the memory blocks allocated |
753 | */ |
754 | |
755 | void |
756 | xmlMemDisplay(FILE *fp) |
757 | { |
758 | #ifdef MEM_LIST |
759 | MEMHDR *p; |
760 | unsigned idx; |
761 | int nb = 0; |
762 | #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME) |
763 | time_t currentTime; |
764 | char buf[500]; |
765 | struct tm * tstruct; |
766 | #endif |
767 | #endif |
768 | FILE *old_fp = fp; |
769 | |
770 | if (fp == NULL) { |
771 | fp = fopen(".memorylist" , "w" ); |
772 | if (fp == NULL) |
773 | return; |
774 | } |
775 | |
776 | #ifdef MEM_LIST |
777 | #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME) |
778 | currentTime = time(NULL); |
779 | tstruct = localtime(¤tTime); |
780 | strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p" , tstruct); |
781 | fprintf(fp," %s\n\n" , buf); |
782 | #endif |
783 | |
784 | |
785 | fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n" , |
786 | debugMemSize, debugMaxMemSize); |
787 | fprintf(fp,"BLOCK NUMBER SIZE TYPE\n" ); |
788 | idx = 0; |
789 | xmlMutexLock(xmlMemMutex); |
790 | p = memlist; |
791 | while (p) { |
792 | fprintf(fp,"%-5u %6lu %6lu " ,idx++,p->mh_number, |
793 | (unsigned long)p->mh_size); |
794 | switch (p->mh_type) { |
795 | case STRDUP_TYPE:fprintf(fp,"strdup() in " );break; |
796 | 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 | case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in " );break; |
800 | default: |
801 | fprintf(fp,"Unknown memory block, may be corrupted" ); |
802 | xmlMutexUnlock(xmlMemMutex); |
803 | if (old_fp == NULL) |
804 | fclose(fp); |
805 | return; |
806 | } |
807 | if (p->mh_file != NULL) fprintf(fp,"%s(%u)" , p->mh_file, p->mh_line); |
808 | if (p->mh_tag != MEMTAG) |
809 | fprintf(fp," INVALID" ); |
810 | nb++; |
811 | if (nb < 100) |
812 | xmlMemContentShow(fp, p); |
813 | else |
814 | fprintf(fp," skip" ); |
815 | |
816 | fprintf(fp,"\n" ); |
817 | p = p->mh_next; |
818 | } |
819 | xmlMutexUnlock(xmlMemMutex); |
820 | #else |
821 | fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n" ); |
822 | #endif |
823 | if (old_fp == NULL) |
824 | fclose(fp); |
825 | } |
826 | |
827 | #ifdef MEM_LIST |
828 | |
829 | static void debugmem_list_add(MEMHDR *p) |
830 | { |
831 | p->mh_next = memlist; |
832 | p->mh_prev = NULL; |
833 | if (memlist) memlist->mh_prev = p; |
834 | memlist = p; |
835 | #ifdef MEM_LIST_DEBUG |
836 | if (stderr) |
837 | Mem_Display(stderr); |
838 | #endif |
839 | } |
840 | |
841 | static void debugmem_list_delete(MEMHDR *p) |
842 | { |
843 | if (p->mh_next) |
844 | p->mh_next->mh_prev = p->mh_prev; |
845 | if (p->mh_prev) |
846 | p->mh_prev->mh_next = p->mh_next; |
847 | else memlist = p->mh_next; |
848 | #ifdef MEM_LIST_DEBUG |
849 | if (stderr) |
850 | Mem_Display(stderr); |
851 | #endif |
852 | } |
853 | |
854 | #endif |
855 | |
856 | /* |
857 | * debugmem_tag_error: |
858 | * |
859 | * internal error function. |
860 | */ |
861 | |
862 | static void debugmem_tag_error(void *p) |
863 | { |
864 | xmlGenericError(xmlGenericErrorContext, |
865 | "Memory tag error occurs :%p \n\t bye\n" , p); |
866 | #ifdef MEM_LIST |
867 | if (stderr) |
868 | xmlMemDisplay(stderr); |
869 | #endif |
870 | } |
871 | |
872 | #ifdef MEM_LIST |
873 | static FILE *xmlMemoryDumpFile = NULL; |
874 | #endif |
875 | |
876 | /** |
877 | * xmlMemShow: |
878 | * @fp: a FILE descriptor used as the output file |
879 | * @nr: number of entries to dump |
880 | * |
881 | * show a show display of the memory allocated, and dump |
882 | * the @nr last allocated areas which were not freed |
883 | */ |
884 | |
885 | void |
886 | xmlMemShow(FILE *fp, int nr ATTRIBUTE_UNUSED) |
887 | { |
888 | #ifdef MEM_LIST |
889 | MEMHDR *p; |
890 | #endif |
891 | |
892 | if (fp != NULL) |
893 | fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n" , |
894 | debugMemSize, debugMaxMemSize); |
895 | #ifdef MEM_LIST |
896 | xmlMutexLock(xmlMemMutex); |
897 | if (nr > 0) { |
898 | fprintf(fp,"NUMBER SIZE TYPE WHERE\n" ); |
899 | p = memlist; |
900 | while ((p) && nr > 0) { |
901 | fprintf(fp,"%6lu %6lu " ,p->mh_number,(unsigned long)p->mh_size); |
902 | switch (p->mh_type) { |
903 | case STRDUP_TYPE:fprintf(fp,"strdup() in " );break; |
904 | case MALLOC_TYPE:fprintf(fp,"malloc() in " );break; |
905 | case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc() in " );break; |
906 | case REALLOC_TYPE:fprintf(fp,"realloc() in " );break; |
907 | case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in " );break; |
908 | default:fprintf(fp," ??? in " );break; |
909 | } |
910 | if (p->mh_file != NULL) |
911 | fprintf(fp,"%s(%u)" , p->mh_file, p->mh_line); |
912 | if (p->mh_tag != MEMTAG) |
913 | fprintf(fp," INVALID" ); |
914 | xmlMemContentShow(fp, p); |
915 | fprintf(fp,"\n" ); |
916 | nr--; |
917 | p = p->mh_next; |
918 | } |
919 | } |
920 | xmlMutexUnlock(xmlMemMutex); |
921 | #endif /* MEM_LIST */ |
922 | } |
923 | |
924 | /** |
925 | * xmlMemoryDump: |
926 | * |
927 | * Dump in-extenso the memory blocks allocated to the file .memorylist |
928 | */ |
929 | |
930 | void |
931 | xmlMemoryDump(void) |
932 | { |
933 | #ifdef MEM_LIST |
934 | FILE *dump; |
935 | |
936 | if (debugMaxMemSize == 0) |
937 | return; |
938 | dump = fopen(".memdump" , "w" ); |
939 | if (dump == NULL) |
940 | xmlMemoryDumpFile = stderr; |
941 | else xmlMemoryDumpFile = dump; |
942 | |
943 | xmlMemDisplay(xmlMemoryDumpFile); |
944 | |
945 | if (dump != NULL) fclose(dump); |
946 | #endif /* MEM_LIST */ |
947 | } |
948 | |
949 | |
950 | /**************************************************************** |
951 | * * |
952 | * Initialization Routines * |
953 | * * |
954 | ****************************************************************/ |
955 | |
956 | /** |
957 | * xmlInitMemory: |
958 | * |
959 | * Initialize the memory layer. |
960 | * |
961 | * Returns 0 on success |
962 | */ |
963 | int |
964 | xmlInitMemory(void) |
965 | { |
966 | #ifdef HAVE_STDLIB_H |
967 | char *breakpoint; |
968 | #endif |
969 | #ifdef DEBUG_MEMORY |
970 | xmlGenericError(xmlGenericErrorContext, |
971 | "xmlInitMemory()\n" ); |
972 | #endif |
973 | /* |
974 | This is really not good code (see Bug 130419). Suggestions for |
975 | improvement will be welcome! |
976 | */ |
977 | if (xmlMemInitialized) return(-1); |
978 | xmlMemInitialized = 1; |
979 | xmlMemMutex = xmlNewMutex(); |
980 | |
981 | #ifdef HAVE_STDLIB_H |
982 | breakpoint = getenv("XML_MEM_BREAKPOINT" ); |
983 | if (breakpoint != NULL) { |
984 | sscanf(breakpoint, "%ud" , &xmlMemStopAtBlock); |
985 | } |
986 | #endif |
987 | #ifdef HAVE_STDLIB_H |
988 | breakpoint = getenv("XML_MEM_TRACE" ); |
989 | if (breakpoint != NULL) { |
990 | sscanf(breakpoint, "%p" , &xmlMemTraceBlockAt); |
991 | } |
992 | #endif |
993 | |
994 | #ifdef DEBUG_MEMORY |
995 | xmlGenericError(xmlGenericErrorContext, |
996 | "xmlInitMemory() Ok\n" ); |
997 | #endif |
998 | return(0); |
999 | } |
1000 | |
1001 | /** |
1002 | * xmlCleanupMemory: |
1003 | * |
1004 | * Free up all the memory allocated by the library for its own |
1005 | * use. This should not be called by user level code. |
1006 | */ |
1007 | void |
1008 | xmlCleanupMemory(void) { |
1009 | #ifdef DEBUG_MEMORY |
1010 | xmlGenericError(xmlGenericErrorContext, |
1011 | "xmlCleanupMemory()\n" ); |
1012 | #endif |
1013 | if (xmlMemInitialized == 0) |
1014 | return; |
1015 | |
1016 | xmlFreeMutex(xmlMemMutex); |
1017 | xmlMemMutex = NULL; |
1018 | xmlMemInitialized = 0; |
1019 | #ifdef DEBUG_MEMORY |
1020 | xmlGenericError(xmlGenericErrorContext, |
1021 | "xmlCleanupMemory() Ok\n" ); |
1022 | #endif |
1023 | } |
1024 | |
1025 | /** |
1026 | * xmlMemSetup: |
1027 | * @freeFunc: the free() function to use |
1028 | * @mallocFunc: the malloc() function to use |
1029 | * @reallocFunc: the realloc() function to use |
1030 | * @strdupFunc: the strdup() function to use |
1031 | * |
1032 | * Override the default memory access functions with a new set |
1033 | * This has to be called before any other libxml routines ! |
1034 | * |
1035 | * Should this be blocked if there was already some allocations |
1036 | * done ? |
1037 | * |
1038 | * Returns 0 on success |
1039 | */ |
1040 | int |
1041 | xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc, |
1042 | xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) { |
1043 | #ifdef DEBUG_MEMORY |
1044 | xmlGenericError(xmlGenericErrorContext, |
1045 | "xmlMemSetup()\n" ); |
1046 | #endif |
1047 | if (freeFunc == NULL) |
1048 | return(-1); |
1049 | if (mallocFunc == NULL) |
1050 | return(-1); |
1051 | if (reallocFunc == NULL) |
1052 | return(-1); |
1053 | if (strdupFunc == NULL) |
1054 | return(-1); |
1055 | xmlFree = freeFunc; |
1056 | xmlMalloc = mallocFunc; |
1057 | xmlMallocAtomic = mallocFunc; |
1058 | xmlRealloc = reallocFunc; |
1059 | xmlMemStrdup = strdupFunc; |
1060 | #ifdef DEBUG_MEMORY |
1061 | xmlGenericError(xmlGenericErrorContext, |
1062 | "xmlMemSetup() Ok\n" ); |
1063 | #endif |
1064 | return(0); |
1065 | } |
1066 | |
1067 | /** |
1068 | * xmlMemGet: |
1069 | * @freeFunc: place to save the free() function in use |
1070 | * @mallocFunc: place to save the malloc() function in use |
1071 | * @reallocFunc: place to save the realloc() function in use |
1072 | * @strdupFunc: place to save the strdup() function in use |
1073 | * |
1074 | * Provides the memory access functions set currently in use |
1075 | * |
1076 | * Returns 0 on success |
1077 | */ |
1078 | int |
1079 | xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc, |
1080 | xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) { |
1081 | if (freeFunc != NULL) *freeFunc = xmlFree; |
1082 | if (mallocFunc != NULL) *mallocFunc = xmlMalloc; |
1083 | if (reallocFunc != NULL) *reallocFunc = xmlRealloc; |
1084 | if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup; |
1085 | return(0); |
1086 | } |
1087 | |
1088 | /** |
1089 | * xmlGcMemSetup: |
1090 | * @freeFunc: the free() function to use |
1091 | * @mallocFunc: the malloc() function to use |
1092 | * @mallocAtomicFunc: the malloc() function to use for atomic allocations |
1093 | * @reallocFunc: the realloc() function to use |
1094 | * @strdupFunc: the strdup() function to use |
1095 | * |
1096 | * Override the default memory access functions with a new set |
1097 | * This has to be called before any other libxml routines ! |
1098 | * The mallocAtomicFunc is specialized for atomic block |
1099 | * allocations (i.e. of areas useful for garbage collected memory allocators |
1100 | * |
1101 | * Should this be blocked if there was already some allocations |
1102 | * done ? |
1103 | * |
1104 | * Returns 0 on success |
1105 | */ |
1106 | int |
1107 | xmlGcMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc, |
1108 | xmlMallocFunc mallocAtomicFunc, xmlReallocFunc reallocFunc, |
1109 | xmlStrdupFunc strdupFunc) { |
1110 | #ifdef DEBUG_MEMORY |
1111 | xmlGenericError(xmlGenericErrorContext, |
1112 | "xmlGcMemSetup()\n" ); |
1113 | #endif |
1114 | if (freeFunc == NULL) |
1115 | return(-1); |
1116 | if (mallocFunc == NULL) |
1117 | return(-1); |
1118 | if (mallocAtomicFunc == NULL) |
1119 | return(-1); |
1120 | if (reallocFunc == NULL) |
1121 | return(-1); |
1122 | if (strdupFunc == NULL) |
1123 | return(-1); |
1124 | xmlFree = freeFunc; |
1125 | xmlMalloc = mallocFunc; |
1126 | xmlMallocAtomic = mallocAtomicFunc; |
1127 | xmlRealloc = reallocFunc; |
1128 | xmlMemStrdup = strdupFunc; |
1129 | #ifdef DEBUG_MEMORY |
1130 | xmlGenericError(xmlGenericErrorContext, |
1131 | "xmlGcMemSetup() Ok\n" ); |
1132 | #endif |
1133 | return(0); |
1134 | } |
1135 | |
1136 | /** |
1137 | * xmlGcMemGet: |
1138 | * @freeFunc: place to save the free() function in use |
1139 | * @mallocFunc: place to save the malloc() function in use |
1140 | * @mallocAtomicFunc: place to save the atomic malloc() function in use |
1141 | * @reallocFunc: place to save the realloc() function in use |
1142 | * @strdupFunc: place to save the strdup() function in use |
1143 | * |
1144 | * Provides the memory access functions set currently in use |
1145 | * The mallocAtomicFunc is specialized for atomic block |
1146 | * allocations (i.e. of areas useful for garbage collected memory allocators |
1147 | * |
1148 | * Returns 0 on success |
1149 | */ |
1150 | int |
1151 | xmlGcMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc, |
1152 | xmlMallocFunc *mallocAtomicFunc, xmlReallocFunc *reallocFunc, |
1153 | xmlStrdupFunc *strdupFunc) { |
1154 | if (freeFunc != NULL) *freeFunc = xmlFree; |
1155 | if (mallocFunc != NULL) *mallocFunc = xmlMalloc; |
1156 | if (mallocAtomicFunc != NULL) *mallocAtomicFunc = xmlMallocAtomic; |
1157 | if (reallocFunc != NULL) *reallocFunc = xmlRealloc; |
1158 | if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup; |
1159 | return(0); |
1160 | } |
1161 | |
1162 | #define bottom_xmlmemory |
1163 | #include "elfgcchack.h" |
1164 | |