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