1 /** Memory debugging (leaks, overflows & co)
4 * Wrappers for libc memory managment providing protection against common
5 * pointers manipulation mistakes - bad realloc()/free() pointers, double
6 * free() problem, using uninitialized/freed memory, underflow/overflow
7 * protection, leaks tracking...
9 * Copyright (C) 1999 - 2002 Mikulas Patocka
10 * Copyright (C) 2001 - 2004 Petr Baudis
11 * Copyright (C) 2002 - 2003 Laurent Monin
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This file is covered by the General Public Licence v2. */
28 /* This file is very useful even for projects other than ELinks and I like to
29 * refer to it through its cvsweb URL, therefore it is a good thing to include
30 * the full copyright header here, contrary to the usual ELinks customs. */
47 #include "util/error.h"
48 #include "util/lists.h"
49 #include "util/memdebug.h"
50 #include "util/memory.h"
51 #include "util/string.h"
56 /** Eat less memory, but sacrifice speed?
57 * Default is defined. */
58 #define LESS_MEMORY_SPEED
60 /** Fill memory on alloc() ?
61 * Default is defined. */
63 #define FILL_ON_ALLOC_VALUE 'X'
65 /** Fill memory on realloc() ?
66 * Default is defined. */
67 #define FILL_ON_REALLOC
68 #define FILL_ON_REALLOC_VALUE 'Y'
70 /** Fill memory before free() ?
71 * Default is undef. */
73 #define FILL_ON_FREE_VALUE 'Z'
75 /** Check alloc_header block sanity ?
76 * Default is defined. */
77 #define CHECK_AH_SANITY
78 #define AH_SANITY_MAGIC 0xD3BA110C
80 /** Check for useless reallocation ?
81 * If oldsize is equal to newsize, print a message to stderr.
82 * It may help to find inefficient code.
83 * Default is undefined.
85 #undef CHECK_USELESS_REALLOC
87 /** Check for validity of address passed to free() ?
88 * Note that this is VERY slow, as we iterate through whole memory_list each
89 * time. We can't check magics etc, as it would break double free() check.
90 * Default is undef. */
91 #undef CHECK_INVALID_FREE
93 /** Check for double free ?
94 * Default is defined. */
95 #define CHECK_DOUBLE_FREE
96 #define AH_FREE_MAGIC 0xD3BF110C
98 /** Check for overflows and underflows ?
99 * Default is defined. */
101 #define XFLOW_MAGIC (char) 0xFA
103 /* Log all (de-)allocations to stderr (huge and slow)
104 * Default is undefined. */
105 /* #define LOG_MEMORY_ALLOC */
107 /* --------- end of debugger configuration section */
110 struct alloc_header
{
111 LIST_HEAD(struct alloc_header
);
113 #ifdef CHECK_AH_SANITY
118 const unsigned char *file
;
119 unsigned char *comment
;
122 /** This is a little magic. We want to keep the main pointer aligned,
123 * that means we want to have the xflow underflow mark in the
124 * alloc_header space, but at the _end_ of the aligned reserved space.
125 * This means we in fact live at [SIZE_AH_ALIGNED - 1], not here. (Of
126 * course this might be equivalent in some cases, but it is very
127 * unlikely in practice.) */
128 unsigned char xflow_underflow_placeholder
;
132 /* Size is set to be on boundary of 8 (a multiple of 7) in order to have the
133 * main ptr aligned properly (faster access). We hope that */
134 #ifdef LESS_MEMORY_SPEED
135 #define SIZE_AH_ALIGNED ((sizeof(struct alloc_header) + 7) & ~7)
137 /* Especially on 128bit machines, this can be faster, but eats more memory. */
138 #define SIZE_AH_ALIGNED ((sizeof(struct alloc_header) + 15) & ~15)
147 /* These macros are used to convert pointers and sizes to or from real ones
148 * when using alloc_header stuff. */
149 #define PTR_AH2BASE(ah) (void *) ((char *) (ah) + SIZE_AH_ALIGNED)
150 #define PTR_BASE2AH(ptr) (struct alloc_header *) \
151 ((char *) (ptr) - SIZE_AH_ALIGNED)
153 /* The second overflow mark is not embraced in SIZE_AH_ALIGNED. */
154 #define SIZE_BASE2AH(size) ((size) + SIZE_AH_ALIGNED + XFLOW_INC)
155 #define SIZE_AH2BASE(size) ((size) - SIZE_AH_ALIGNED - XFLOW_INC)
158 #define PTR_OVERFLOW_MAGIC(ah) ((char *) PTR_AH2BASE(ah) + (ah)->size)
159 #define PTR_UNDERFLOW_MAGIC(ah) ((char *) PTR_AH2BASE(ah) - 1)
160 #define SET_OVERFLOW_MAGIC(ah) (*PTR_OVERFLOW_MAGIC(ah) = XFLOW_MAGIC)
161 #define SET_UNDERFLOW_MAGIC(ah) (*PTR_UNDERFLOW_MAGIC(ah) = XFLOW_MAGIC)
162 #define SET_XFLOW_MAGIC(ah) SET_OVERFLOW_MAGIC(ah), SET_UNDERFLOW_MAGIC(ah)
165 struct mem_stats mem_stats
;
167 INIT_LIST_OF(struct alloc_header
, memory_list
);
169 #ifdef LOG_MEMORY_ALLOC
171 dump_short_info(struct alloc_header
*ah
, const unsigned char *file
, int line
,
172 const unsigned char *type
)
174 fprintf(stderr
, "%p", PTR_AH2BASE(ah
)), fflush(stderr
);
175 fprintf(stderr
, ":%d", ah
->size
), fflush(stderr
);
176 if (type
&& *type
) fprintf(stderr
, " %s", type
), fflush(stderr
);
177 fprintf(stderr
, " @ %s:%d ", file
, line
), fflush(stderr
);
178 if (strcmp(file
, ah
->file
) || line
!= ah
->line
)
179 fprintf(stderr
, "< %s:%d", ah
->file
, ah
->line
), fflush(stderr
);
180 if (ah
->comment
) fprintf(stderr
, " [%s]", ah
->comment
), fflush(stderr
);
181 fprintf(stderr
, "\n"), fflush(stderr
);
184 #define dump_short_info(a, b, c, d)
188 dump_info(struct alloc_header
*ah
, const unsigned char *info
,
189 const unsigned char *file
, int line
, const unsigned char *type
)
191 fprintf(stderr
, "%p", PTR_AH2BASE(ah
)); fflush(stderr
);
192 /* In some extreme cases, we may core here, as 'ah' can no longer point
193 * to valid memory area (esp. when used in debug_mem_free()). */
194 fprintf(stderr
, ":%d", ah
->size
);
196 if (type
&& *type
) fprintf(stderr
, " \033[1m%s\033[0m", type
);
198 if (info
&& *info
) fprintf(stderr
, " %s", info
);
200 fprintf(stderr
, " @ ");
202 /* Following is commented out, as we want to print this out even when
203 * there're lions in ah, pointing us to evil places in memory, leading
204 * to segfaults and stuff like that. --pasky */
205 /* if (file && (strcmp(file, ah->file) || line != ah->line)) */
206 if (file
) fprintf(stderr
, "%s:%d, ", file
, line
), fflush(stderr
);
208 fprintf(stderr
, "alloc'd at %s:%d", ah
->file
, ah
->line
);
210 if (ah
->comment
) fprintf(stderr
, " [%s]", ah
->comment
);
212 fprintf(stderr
, "\n");
215 #ifdef CHECK_AH_SANITY
217 bad_ah_sanity(struct alloc_header
*ah
, const unsigned char *info
,
218 const unsigned char *file
, int line
)
221 if (ah
->magic
!= AH_SANITY_MAGIC
) {
222 dump_info(ah
, info
, file
, line
, "bad alloc_header block");
228 #endif /* CHECK_AH_SANITY */
232 bad_xflow_magic(struct alloc_header
*ah
, const unsigned char *info
,
233 const unsigned char *file
, int line
)
237 if (*PTR_OVERFLOW_MAGIC(ah
) != XFLOW_MAGIC
) {
238 dump_info(ah
, info
, file
, line
, "overflow detected");
242 if (*PTR_UNDERFLOW_MAGIC(ah
) != XFLOW_MAGIC
) {
243 dump_info(ah
, info
, file
, line
, "underflow detected");
249 #endif /* CHECK_XFLOWS */
253 check_memory_leaks(void)
255 struct alloc_header
*ah
;
257 if (!mem_stats
.amount
) {
258 /* No leaks - escape now. */
259 if (mem_stats
.true_amount
) /* debug memory leak ? */
260 fprintf(stderr
, "\n\033[1mDebug memory leak by %ld bytes\033[0m\n",
261 mem_stats
.true_amount
);
266 fprintf(stderr
, "\n\033[1mMemory leak by %ld bytes\033[0m\n",
269 fprintf(stderr
, "List of blocks:\n");
270 foreach (ah
, memory_list
) {
271 #ifdef CHECK_AH_SANITY
272 if (bad_ah_sanity(ah
, "Skipped", NULL
, 0)) continue;
275 if (bad_xflow_magic(ah
, NULL
, NULL
, 0)) continue;
277 dump_info(ah
, NULL
, NULL
, 0, NULL
);
283 static int alloc_try
= 0;
286 patience(const unsigned char *file
, int line
, const unsigned char *of
)
292 if (alloc_try
< ALLOC_MAXTRIES
) {
293 elinks_error("Out of memory (%s returned NULL): retry #%d/%d, "
294 "I still exercise my patience and retry tirelessly.",
295 of
, alloc_try
, ALLOC_MAXTRIES
);
300 #ifdef CRASH_IF_ALLOC_MAXTRIES
301 elinks_internal("Out of memory (%s returned NULL) after %d tries, "
302 "I give up. See ya on the other side.",
305 elinks_error("Out of memory (%s returned NULL) after %d tries, "
306 "I give up and try to continue. Pray for me, please.",
314 debug_mem_alloc(const unsigned char *file
, int line
, size_t size
)
316 struct alloc_header
*ah
;
319 if (!size
) return NULL
;
321 true_size
= SIZE_BASE2AH(size
);
323 ah
= malloc(true_size
);
325 } while (patience(file
, line
, "malloc"));
326 if (!ah
) return NULL
;
329 memset(ah
, FILL_ON_ALLOC_VALUE
, SIZE_BASE2AH(size
));
332 mem_stats
.true_amount
+= true_size
;
333 mem_stats
.amount
+= size
;
336 #ifdef CHECK_AH_SANITY
337 ah
->magic
= AH_SANITY_MAGIC
;
346 add_to_list(memory_list
, ah
);
348 dump_short_info(ah
, file
, line
, "malloc");
350 return PTR_AH2BASE(ah
);
354 debug_mem_calloc(const unsigned char *file
, int line
, size_t eltcount
, size_t eltsize
)
356 struct alloc_header
*ah
;
357 size_t size
= eltcount
* eltsize
;
360 if (!size
) return NULL
;
362 /* FIXME: Unfortunately, we can't pass eltsize through to calloc()
363 * itself, because we add bloat like alloc_header to it, which is
364 * difficult to be measured in eltsize. Maybe we should round it up to
365 * next eltsize multiple, but would it be worth the possibly wasted
366 * space? Someone should make some benchmarks. If you still read this
367 * comment, it means YOU should help us and do the benchmarks! :)
368 * Thanks a lot. --pasky */
370 true_size
= SIZE_BASE2AH(size
);
372 ah
= calloc(1, true_size
);
374 } while (patience(file
, line
, "calloc"));
375 if (!ah
) return NULL
;
377 /* No, we do NOT want to fill this with FILL_ON_ALLOC_VALUE ;)). */
379 mem_stats
.true_amount
+= true_size
;
380 mem_stats
.amount
+= size
;
383 #ifdef CHECK_AH_SANITY
384 ah
->magic
= AH_SANITY_MAGIC
;
393 add_to_list(memory_list
, ah
);
395 dump_short_info(ah
, file
, line
, "calloc");
397 return PTR_AH2BASE(ah
);
401 debug_mem_free(const unsigned char *file
, int line
, void *ptr
)
403 struct alloc_header
*ah
;
409 elinks_internal("mem_free(NULL)");
413 #ifdef CHECK_INVALID_FREE
415 foreach (ah
, memory_list
) {
416 if (ah
== PTR_BASE2AH(ptr
)) {
423 ah
= PTR_BASE2AH(ptr
);
426 /* This will get optimized out when not CHECK_INVALID_FREE. */
427 dump_info(ah
, "free()", file
, line
, "invalid address");
431 #ifdef CHECK_DOUBLE_FREE
432 if (ah
->magic
== AH_FREE_MAGIC
) {
433 dump_info(ah
, "free()", file
, line
, "double free");
434 /* If we already survived it chances are we could get over it.
435 * So let's not be overly tragic immediatelly. Just make sure
436 * the developer saw it. */
441 #ifdef CHECK_AH_SANITY
442 if (bad_ah_sanity(ah
, "free()", file
, line
))
446 if (bad_xflow_magic(ah
, "free()", file
, line
))
450 dump_short_info(ah
, file
, line
, "free");
453 mem_stats
.true_amount
-= strlen(ah
->comment
) + 1;
459 mem_stats
.true_amount
-= SIZE_BASE2AH(ah
->size
);
460 mem_stats
.amount
-= ah
->size
;
463 memset(ah
, FILL_ON_FREE_VALUE
, SIZE_BASE2AH(ah
->size
));
465 #ifdef CHECK_DOUBLE_FREE
466 ah
->magic
= AH_FREE_MAGIC
;
472 debug_mem_realloc(const unsigned char *file
, int line
, void *ptr
, size_t size
)
474 struct alloc_header
*ah
, *ah2
;
477 if (!ptr
) return debug_mem_alloc(file
, line
, size
);
479 /* Frees memory if size is zero. */
481 debug_mem_free(file
, line
, ptr
);
485 ah
= PTR_BASE2AH(ptr
);
487 #ifdef CHECK_AH_SANITY
488 if (bad_ah_sanity(ah
, "realloc()", file
, line
)) force_dump();
491 if (bad_xflow_magic(ah
, "realloc()", file
, line
)) force_dump();
494 /* We compare oldsize to new size, and if equal we just return ptr
495 * and change nothing, this conforms to usual realloc() behavior. */
496 if (ah
->size
== size
) {
497 #ifdef CHECK_USELESS_REALLOC
498 fprintf(stderr
, "[%s:%d] mem_realloc() oldsize = newsize = %zu\n", file
, line
, size
);
503 true_size
= SIZE_BASE2AH(size
);
505 ah2
= realloc(ah
, true_size
);
510 } while (patience(file
, line
, "realloc"));
511 if (!ah2
) return NULL
;
513 mem_stats
.true_amount
+= true_size
- SIZE_BASE2AH(ah
->size
);
514 mem_stats
.amount
+= size
- ah
->size
;
516 #ifdef FILL_ON_REALLOC
518 memset((char *) PTR_AH2BASE(ah
) + ah
->size
,
519 FILL_ON_REALLOC_VALUE
, size
- ah
->size
);
523 #ifdef CHECK_AH_SANITY
524 ah
->magic
= AH_SANITY_MAGIC
;
535 dump_short_info(ah
, file
, line
, "realloc");
537 return PTR_AH2BASE(ah
);
541 set_mem_comment(void *ptr
, const unsigned char *str
, int len
)
543 struct alloc_header
*ah
;
545 ah
= PTR_BASE2AH(ptr
);
548 mem_stats
.true_amount
-= strlen(ah
->comment
) + 1;
551 ah
->comment
= malloc(len
+ 1);
553 memcpy(ah
->comment
, str
, len
);
554 ah
->comment
[len
] = 0;
555 mem_stats
.true_amount
+= len
+ 1;
559 #endif /* DEBUG_MEMLEAK */