html_special(): move va_end() call outside the switch and make variables
[elinks.git] / src / util / memdebug.c
blob0262acb8ec408de11f9eedc1b0bd8ac3d73f0dde
1 /* Memory debugging (leaks, overflows & co) */
3 /* Wrappers for libc memory managment providing protection against common
4 * pointers manipulation mistakes - bad realloc()/free() pointers, double
5 * free() problem, using uninitialized/freed memory, underflow/overflow
6 * protection, leaks tracking...
8 * Copyright (C) 1999 - 2002 Mikulas Patocka
9 * Copyright (C) 2001 - 2004 Petr Baudis
10 * Copyright (C) 2002 - 2003 Laurent Monin
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * This file is covered by the General Public Licence v2. */
27 /* This file is very useful even for projects other than ELinks and I like to
28 * refer to it through its cvsweb URL, therefore it is a good thing to include
29 * the full copyright header here, contrary to the usual ELinks customs. */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
44 #include "elinks.h"
46 #include "util/error.h"
47 #include "util/lists.h"
48 #include "util/memdebug.h"
49 #include "util/memory.h"
50 #include "util/string.h"
53 #ifdef DEBUG_MEMLEAK
55 /* Eat less memory, but sacrifice speed?
56 * Default is defined. */
57 #define LESS_MEMORY_SPEED
59 /* Fill memory on alloc() ?
60 * Default is defined. */
61 #define FILL_ON_ALLOC
62 #define FILL_ON_ALLOC_VALUE 'X'
64 /* Fill memory on realloc() ?
65 * Default is defined. */
66 #define FILL_ON_REALLOC
67 #define FILL_ON_REALLOC_VALUE 'Y'
69 /* Fill memory before free() ?
70 * Default is undef. */
71 #undef FILL_ON_FREE
72 #define FILL_ON_FREE_VALUE 'Z'
74 /* Check alloc_header block sanity ?
75 * Default is defined. */
76 #define CHECK_AH_SANITY
77 #define AH_SANITY_MAGIC 0xD3BA110C
79 /* Check for useless reallocation ?
80 * If oldsize is equal to newsize, print a message to stderr.
81 * It may help to find inefficient code.
82 * Default is undefined.
84 #undef CHECK_USELESS_REALLOC
86 /* Check for validity of address passed to free() ?
87 * Note that this is VERY slow, as we iterate through whole memory_list each
88 * time. We can't check magics etc, as it would break double free() check.
89 * Default is undef. */
90 #undef CHECK_INVALID_FREE
92 /* Check for double free ?
93 * Default is defined. */
94 #define CHECK_DOUBLE_FREE
95 #define AH_FREE_MAGIC 0xD3BF110C
97 /* Check for overflows and underflows ?
98 * Default is defined. */
99 #define CHECK_XFLOWS
100 #define XFLOW_MAGIC (char) 0xFA
102 /* Log all (de-)allocations to stderr (huge and slow)
103 * Default is undefined. */
104 /* #define LOG_MEMORY_ALLOC */
106 /* --------- end of debugger configuration section */
109 struct alloc_header {
110 LIST_HEAD(struct alloc_header);
112 #ifdef CHECK_AH_SANITY
113 int magic;
114 #endif
115 int size;
116 int line;
117 unsigned char *file;
118 unsigned char *comment;
120 #ifdef CHECK_XFLOWS
121 /* This is a little magic. We want to keep the main pointer aligned,
122 * that means we want to have the xflow underflow mark in the
123 * alloc_header space, but at the _end_ of the aligned reserved space.
124 * This means we in fact live at [SIZE_AH_ALIGNED - 1], not here. (Of
125 * course this might be equivalent in some cases, but it is very
126 * unlikely in practice.) */
127 unsigned char xflow_underflow_placeholder;
128 #endif
131 /* Size is set to be on boundary of 8 (a multiple of 7) in order to have the
132 * main ptr aligned properly (faster access). We hope that */
133 #ifdef LESS_MEMORY_SPEED
134 #define SIZE_AH_ALIGNED ((sizeof(struct alloc_header) + 7) & ~7)
135 #else
136 /* Especially on 128bit machines, this can be faster, but eats more memory. */
137 #define SIZE_AH_ALIGNED ((sizeof(struct alloc_header) + 15) & ~15)
138 #endif
140 #ifdef CHECK_XFLOWS
141 #define XFLOW_INC 1
142 #else
143 #define XFLOW_INC 0
144 #endif
146 /* These macros are used to convert pointers and sizes to or from real ones
147 * when using alloc_header stuff. */
148 #define PTR_AH2BASE(ah) (void *) ((char *) (ah) + SIZE_AH_ALIGNED)
149 #define PTR_BASE2AH(ptr) (struct alloc_header *) \
150 ((char *) (ptr) - SIZE_AH_ALIGNED)
152 /* The second overflow mark is not embraced in SIZE_AH_ALIGNED. */
153 #define SIZE_BASE2AH(size) ((size) + SIZE_AH_ALIGNED + XFLOW_INC)
154 #define SIZE_AH2BASE(size) ((size) - SIZE_AH_ALIGNED - XFLOW_INC)
156 #ifdef CHECK_XFLOWS
157 #define PTR_OVERFLOW_MAGIC(ah) ((char *) PTR_AH2BASE(ah) + (ah)->size)
158 #define PTR_UNDERFLOW_MAGIC(ah) ((char *) PTR_AH2BASE(ah) - 1)
159 #define SET_OVERFLOW_MAGIC(ah) (*PTR_OVERFLOW_MAGIC(ah) = XFLOW_MAGIC)
160 #define SET_UNDERFLOW_MAGIC(ah) (*PTR_UNDERFLOW_MAGIC(ah) = XFLOW_MAGIC)
161 #define SET_XFLOW_MAGIC(ah) SET_OVERFLOW_MAGIC(ah), SET_UNDERFLOW_MAGIC(ah)
162 #endif
164 struct mem_stats mem_stats;
166 INIT_LIST_HEAD(memory_list);
168 #ifdef LOG_MEMORY_ALLOC
169 static void
170 dump_short_info(struct alloc_header *ah, unsigned char *file, int line,
171 unsigned char *type)
173 fprintf(stderr, "%p", PTR_AH2BASE(ah)), fflush(stderr);
174 fprintf(stderr, ":%d", ah->size), fflush(stderr);
175 if (type && *type) fprintf(stderr, " %s", type), fflush(stderr);
176 fprintf(stderr, " @ %s:%d ", file, line), fflush(stderr);
177 if (strcmp(file, ah->file) || line != ah->line)
178 fprintf(stderr, "< %s:%d", ah->file, ah->line), fflush(stderr);
179 if (ah->comment) fprintf(stderr, " [%s]", ah->comment), fflush(stderr);
180 fprintf(stderr, "\n"), fflush(stderr);
182 #else
183 #define dump_short_info(a, b, c, d)
184 #endif
186 static void
187 dump_info(struct alloc_header *ah, unsigned char *info,
188 unsigned char *file, int line, unsigned char *type)
190 fprintf(stderr, "%p", PTR_AH2BASE(ah)); fflush(stderr);
191 /* In some extreme cases, we may core here, as 'ah' can no longer point
192 * to valid memory area (esp. when used in debug_mem_free()). */
193 fprintf(stderr, ":%d", ah->size);
195 if (type && *type) fprintf(stderr, " \033[1m%s\033[0m", type);
197 if (info && *info) fprintf(stderr, " %s", info);
199 fprintf(stderr, " @ ");
201 /* Following is commented out, as we want to print this out even when
202 * there're lions in ah, pointing us to evil places in memory, leading
203 * to segfaults and stuff like that. --pasky */
204 /* if (file && (strcmp(file, ah->file) || line != ah->line)) */
205 if (file) fprintf(stderr, "%s:%d, ", file, line), fflush(stderr);
207 fprintf(stderr, "alloc'd at %s:%d", ah->file, ah->line);
209 if (ah->comment) fprintf(stderr, " [%s]", ah->comment);
211 fprintf(stderr, "\n");
214 #ifdef CHECK_AH_SANITY
215 static inline int
216 bad_ah_sanity(struct alloc_header *ah, unsigned char *info,
217 unsigned char *file, int line)
219 if (!ah) return 1;
220 if (ah->magic != AH_SANITY_MAGIC) {
221 dump_info(ah, info, file, line, "bad alloc_header block");
222 return 1;
225 return 0;
227 #endif /* CHECK_AH_SANITY */
229 #ifdef CHECK_XFLOWS
230 static inline int
231 bad_xflow_magic(struct alloc_header *ah, unsigned char *info,
232 unsigned char *file, int line)
234 if (!ah) return 1;
236 if (*PTR_OVERFLOW_MAGIC(ah) != XFLOW_MAGIC) {
237 dump_info(ah, info, file, line, "overflow detected");
238 return 1;
241 if (*PTR_UNDERFLOW_MAGIC(ah) != XFLOW_MAGIC) {
242 dump_info(ah, info, file, line, "underflow detected");
243 return 1;
246 return 0;
248 #endif /* CHECK_XFLOWS */
251 void
252 check_memory_leaks(void)
254 struct alloc_header *ah;
256 if (!mem_stats.amount) {
257 /* No leaks - escape now. */
258 if (mem_stats.true_amount) /* debug memory leak ? */
259 fprintf(stderr, "\n\033[1mDebug memory leak by %ld bytes\033[0m\n",
260 mem_stats.true_amount);
262 return;
265 fprintf(stderr, "\n\033[1mMemory leak by %ld bytes\033[0m\n",
266 mem_stats.amount);
268 fprintf(stderr, "List of blocks:\n");
269 foreach (ah, memory_list) {
270 #ifdef CHECK_AH_SANITY
271 if (bad_ah_sanity(ah, "Skipped", NULL, 0)) continue;
272 #endif
273 #ifdef CHECK_XFLOWS
274 if (bad_xflow_magic(ah, NULL, NULL, 0)) continue;
275 #endif
276 dump_info(ah, NULL, NULL, 0, NULL);
279 force_dump();
282 static int alloc_try = 0;
284 static int
285 patience(unsigned char *file, int line, unsigned char *of)
287 errfile = file;
288 errline = line;
290 ++alloc_try;
291 if (alloc_try < ALLOC_MAXTRIES) {
292 elinks_error("Out of memory (%s returned NULL): retry #%d/%d, "
293 "I still exercise my patience and retry tirelessly.",
294 of, alloc_try, ALLOC_MAXTRIES);
295 sleep(ALLOC_DELAY);
296 return alloc_try;
299 #ifdef CRASH_IF_ALLOC_MAXTRIES
300 elinks_internal("Out of memory (%s returned NULL) after %d tries, "
301 "I give up. See ya on the other side.",
302 of, alloc_try);
303 #else
304 elinks_error("Out of memory (%s returned NULL) after %d tries, "
305 "I give up and try to continue. Pray for me, please.",
306 of, alloc_try);
307 #endif
308 alloc_try = 0;
309 return 0;
312 void *
313 debug_mem_alloc(unsigned char *file, int line, size_t size)
315 struct alloc_header *ah;
316 size_t true_size;
318 if (!size) return NULL;
320 true_size = SIZE_BASE2AH(size);
321 do {
322 ah = malloc(true_size);
323 if (ah) break;
324 } while (patience(file, line, "malloc"));
325 if (!ah) return NULL;
327 #ifdef FILL_ON_ALLOC
328 memset(ah, FILL_ON_ALLOC_VALUE, SIZE_BASE2AH(size));
329 #endif
331 mem_stats.true_amount += true_size;
332 mem_stats.amount += size;
334 ah->size = size;
335 #ifdef CHECK_AH_SANITY
336 ah->magic = AH_SANITY_MAGIC;
337 #endif
338 ah->file = file;
339 ah->line = line;
340 ah->comment = NULL;
341 #ifdef CHECK_XFLOWS
342 SET_XFLOW_MAGIC(ah);
343 #endif
345 add_to_list(memory_list, ah);
347 dump_short_info(ah, file, line, "malloc");
349 return PTR_AH2BASE(ah);
352 void *
353 debug_mem_calloc(unsigned char *file, int line, size_t eltcount, size_t eltsize)
355 struct alloc_header *ah;
356 size_t size = eltcount * eltsize;
357 size_t true_size;
359 if (!size) return NULL;
361 /* FIXME: Unfortunately, we can't pass eltsize through to calloc()
362 * itself, because we add bloat like alloc_header to it, which is
363 * difficult to be measured in eltsize. Maybe we should round it up to
364 * next eltsize multiple, but would it be worth the possibly wasted
365 * space? Someone should make some benchmarks. If you still read this
366 * comment, it means YOU should help us and do the benchmarks! :)
367 * Thanks a lot. --pasky */
369 true_size = SIZE_BASE2AH(size);
370 do {
371 ah = calloc(1, SIZE_BASE2AH(size));
372 if (ah) break;
373 } while (patience(file, line, "calloc"));
374 if (!ah) return NULL;
376 /* No, we do NOT want to fill this with FILL_ON_ALLOC_VALUE ;)). */
378 mem_stats.true_amount += true_size;
379 mem_stats.amount += size;
381 ah->size = size;
382 #ifdef CHECK_AH_SANITY
383 ah->magic = AH_SANITY_MAGIC;
384 #endif
385 ah->file = file;
386 ah->line = line;
387 ah->comment = NULL;
388 #ifdef CHECK_XFLOWS
389 SET_XFLOW_MAGIC(ah);
390 #endif
392 add_to_list(memory_list, ah);
394 dump_short_info(ah, file, line, "calloc");
396 return PTR_AH2BASE(ah);
399 void
400 debug_mem_free(unsigned char *file, int line, void *ptr)
402 struct alloc_header *ah;
403 int ok = 1;
405 if (!ptr) {
406 errfile = file;
407 errline = line;
408 elinks_internal("mem_free(NULL)");
409 return;
412 #ifdef CHECK_INVALID_FREE
413 ok = 0;
414 foreach (ah, memory_list) {
415 if (ah == PTR_BASE2AH(ptr)) {
416 ok = 1;
417 break;
420 #endif
422 ah = PTR_BASE2AH(ptr);
424 if (!ok) {
425 /* This will get optimized out when not CHECK_INVALID_FREE. */
426 dump_info(ah, "free()", file, line, "invalid address");
427 return;
430 #ifdef CHECK_DOUBLE_FREE
431 if (ah->magic == AH_FREE_MAGIC) {
432 dump_info(ah, "free()", file, line, "double free");
433 /* If we already survived it chances are we could get over it.
434 * So let's not be overly tragic immediatelly. Just make sure
435 * the developer saw it. */
436 sleep(1);
438 #endif
440 #ifdef CHECK_AH_SANITY
441 if (bad_ah_sanity(ah, "free()", file, line))
442 force_dump();
443 #endif
444 #ifdef CHECK_XFLOWS
445 if (bad_xflow_magic(ah, "free()", file, line))
446 force_dump();
447 #endif
449 dump_short_info(ah, file, line, "free");
451 if (ah->comment) {
452 mem_stats.true_amount -= strlen(ah->comment) + 1;
453 free(ah->comment);
456 del_from_list(ah);
458 mem_stats.true_amount -= SIZE_BASE2AH(ah->size);
459 mem_stats.amount -= ah->size;
461 #ifdef FILL_ON_FREE
462 memset(ah, FILL_ON_FREE_VALUE, SIZE_BASE2AH(ah->size));
463 #endif
464 #ifdef CHECK_DOUBLE_FREE
465 ah->magic = AH_FREE_MAGIC;
466 #endif
468 free(ah);
471 void *
472 debug_mem_realloc(unsigned char *file, int line, void *ptr, size_t size)
474 struct alloc_header *ah, *ah2;
475 size_t true_size;
477 if (!ptr) return debug_mem_alloc(file, line, size);
479 /* Frees memory if size is zero. */
480 if (!size) {
481 debug_mem_free(file, line, ptr);
482 return NULL;
485 ah = PTR_BASE2AH(ptr);
487 #ifdef CHECK_AH_SANITY
488 if (bad_ah_sanity(ah, "realloc()", file, line)) force_dump();
489 #endif
490 #ifdef CHECK_XFLOWS
491 if (bad_xflow_magic(ah, "realloc()", file, line)) force_dump();
492 #endif
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);
499 #endif
500 return (void *) ptr;
503 true_size = SIZE_BASE2AH(size);
504 do {
505 ah2 = realloc(ah, true_size);
506 if (ah2) {
507 ah = ah2;
508 break;
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
517 if (size > ah->size)
518 memset((char *) PTR_AH2BASE(ah) + ah->size,
519 FILL_ON_REALLOC_VALUE, size - ah->size);
520 #endif
522 ah->size = size;
523 #ifdef CHECK_AH_SANITY
524 ah->magic = AH_SANITY_MAGIC;
525 #endif
526 ah->file = file;
527 ah->line = line;
528 #ifdef CHECK_XFLOWS
529 SET_XFLOW_MAGIC(ah);
530 #endif
532 ah->prev->next = ah;
533 ah->next->prev = ah;
535 dump_short_info(ah, file, line, "realloc");
537 return PTR_AH2BASE(ah);
540 void
541 set_mem_comment(void *ptr, unsigned char *str, int len)
543 struct alloc_header *ah;
545 ah = PTR_BASE2AH(ptr);
547 if (ah->comment) {
548 mem_stats.true_amount -= strlen(ah->comment) + 1;
549 free(ah->comment);
552 ah->comment = malloc(len + 1);
553 if (ah->comment) {
554 safe_strncpy(ah->comment, str, len + 1);
555 mem_stats.true_amount += len + 1;
559 #endif /* DEBUG_MEMLEAK */