summaryrefslogtreecommitdiff
path: root/memdebug/memdebug.c
blob: 50a46fc32d24e3c4dc42179da569c8cea6527551 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * memdebug - A simple malloc/free debugger
 *
 * This can essentially be useful on embedded systems platforms where tools such
 * as valgrind are not available.
 *
 * Copyright (c) 2010, Tobias Klauser <tklauser@distanz.ch>
 */

#include <stdio.h>
#include <stdlib.h>

/* define/undef to enable tracing of errors */
#define MEMDEBUG_TRACE
/* define/undef to enable tracing of double free errors */
#define MEMDEBUG_TRACE_DOUBLE_FREE

#include "memdebug.h"

static struct memdebug_heap_item *heap_head = NULL;
static struct memdebug_heap_item *heap_tail = NULL;

void *__memdebug_malloc(size_t size, const char *func,
                        const char *file, const unsigned int line)
{
	void *ret;
	struct memdebug_heap_item *hi = malloc(sizeof(struct memdebug_heap_item));

	if (!hi)
		goto out_err;

	ret = malloc(size);
	if (!ret) {
		free(hi);
		goto out_err;
	}

	hi->next = NULL;
	hi->addr = ret;
	hi->size = size;
	hi->func = func;
	hi->file = file;
	hi->line = line;
#ifdef MEMDEBUG_TRACE_DOUBLE_FREE
	hi->freed_func = NULL;
	hi->freed_file = NULL;
	hi->line = 0;
#endif

	if (!heap_head)
		heap_head = hi;
	if (!heap_tail)
		heap_tail = hi;
	else {
		heap_tail->next = hi;
		heap_tail = hi;
	}

	return ret;

out_err:
	memdebug_trace("malloc failed (%s:%d:%s)", file, line, func);
	return NULL;
}

void __memdebug_free(void *ptr, const char *func,
                     const char *file, const unsigned int line)
{
	struct memdebug_heap_item *hi, *prev;

	if (!ptr) {
		memdebug_trace("trying to free NULL pointer (%s:%d:%s)", file, line, func);
		return;
	}

	for (hi = heap_head, prev = NULL; hi; prev = hi, hi = hi->next)
		if (hi->addr ==ptr)
			break;

	if (!hi) {
		memdebug_trace("no memory allocated at %p (%s:%d:%s)", ptr, file, line, func);
		return;
	}

#ifdef MEMDEBUG_TRACE_DOUBLE_FREE
	if (hi->freed_func) {
		memdebug_trace("double free at %p (%s:%d:%s)\nalready freed by %s:%d:%s",
				ptr, file, line, func, hi->freed_file, hi->freed_line, hi->freed_func);
		return;
	}

	hi->freed_func = func;
	hi->freed_file = file;
	hi->freed_line = line;
#else
	/* Delete the item from the linked list */
	if (prev)
		prev->next = hi->next;
	else
		heap_head = hi->next;
	if (heap_tail == hi)
		heap_tail = prev;

	free(hi);
#endif

	free(ptr);
}

void memdebug_report(void)
{
	struct memdebug_heap_item *hi;
	size_t total_size = 0;

	fprintf(stderr, "\n\n*** Reporting allocated memory ***\n");
	for (hi = heap_head; hi; hi = hi->next) {
		fprintf(stderr, "  %zu bytes at %p (%s:%d:%s)\n", hi->size, hi->addr, hi->file, hi->line, hi->func);
		total_size += hi->size;
	}
	fprintf(stderr, "Total size: %zu bytes\n\n", total_size);
	fflush(stderr);
}