1 /*	$NetBSD: dbg_malloc.c,v 1.1.1.2 2009/12/02 00:26:09 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of the device-mapper userspace tools.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "dmlib.h"
19 
20 #include <assert.h>
21 #include <stdarg.h>
22 
23 char *dm_strdup_aux(const char *str, const char *file, int line)
24 {
25 	char *ret;
26 
27 	if (!str) {
28 		log_error("Internal error: dm_strdup called with NULL pointer");
29 		return NULL;
30 	}
31 
32 	if ((ret = dm_malloc_aux_debug(strlen(str) + 1, file, line)))
33 		strcpy(ret, str);
34 
35 	return ret;
36 }
37 
38 struct memblock {
39 	struct memblock *prev, *next;	/* All allocated blocks are linked */
40 	size_t length;		/* Size of the requested block */
41 	int id;			/* Index of the block */
42 	const char *file;	/* File that allocated */
43 	int line;		/* Line that allocated */
44 	void *magic;		/* Address of this block */
45 } __attribute__((aligned(8)));
46 
47 static struct {
48 	unsigned block_serialno;/* Non-decreasing serialno of block */
49 	unsigned blocks_allocated; /* Current number of blocks allocated */
50 	unsigned blocks_max;	/* Max no of concurrently-allocated blocks */
51 	unsigned int bytes, mbytes;
52 
53 } _mem_stats = {
54 0, 0, 0, 0, 0};
55 
56 static struct memblock *_head = 0;
57 static struct memblock *_tail = 0;
58 
59 void *dm_malloc_aux_debug(size_t s, const char *file, int line)
60 {
61 	struct memblock *nb;
62 	size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
63 
64 	if (s > 50000000) {
65 		log_error("Huge memory allocation (size %" PRIsize_t
66 			  ") rejected - metadata corruption?", s);
67 		return 0;
68 	}
69 
70 	if (!(nb = malloc(tsize))) {
71 		log_error("couldn't allocate any memory, size = %" PRIsize_t,
72 			  s);
73 		return 0;
74 	}
75 
76 	/* set up the file and line info */
77 	nb->file = file;
78 	nb->line = line;
79 
80 	dm_bounds_check();
81 
82 	/* setup fields */
83 	nb->magic = nb + 1;
84 	nb->length = s;
85 	nb->id = ++_mem_stats.block_serialno;
86 	nb->next = 0;
87 
88 	/* stomp a pretty pattern across the new memory
89 	   and fill in the boundary bytes */
90 	{
91 		char *ptr = (char *) (nb + 1);
92 		size_t i;
93 		for (i = 0; i < s; i++)
94 			*ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
95 
96 		for (i = 0; i < sizeof(unsigned long); i++)
97 			*ptr++ = (char) nb->id;
98 	}
99 
100 	nb->prev = _tail;
101 
102 	/* link to tail of the list */
103 	if (!_head)
104 		_head = _tail = nb;
105 	else {
106 		_tail->next = nb;
107 		_tail = nb;
108 	}
109 
110 	_mem_stats.blocks_allocated++;
111 	if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
112 		_mem_stats.blocks_max = _mem_stats.blocks_allocated;
113 
114 	_mem_stats.bytes += s;
115 	if (_mem_stats.bytes > _mem_stats.mbytes)
116 		_mem_stats.mbytes = _mem_stats.bytes;
117 
118 	/* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
119 		  _mem_stats.bytes); */
120 
121 	return nb + 1;
122 }
123 
124 void dm_free_aux(void *p)
125 {
126 	char *ptr;
127 	size_t i;
128 	struct memblock *mb = ((struct memblock *) p) - 1;
129 	if (!p)
130 		return;
131 
132 	dm_bounds_check();
133 
134 	/* sanity check */
135 	assert(mb->magic == p);
136 
137 	/* check data at the far boundary */
138 	ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
139 	for (i = 0; i < sizeof(unsigned long); i++)
140 		if (*ptr++ != (char) mb->id)
141 			assert(!"Damage at far end of block");
142 
143 	/* have we freed this before ? */
144 	assert(mb->id != 0);
145 
146 	/* unlink */
147 	if (mb->prev)
148 		mb->prev->next = mb->next;
149 	else
150 		_head = mb->next;
151 
152 	if (mb->next)
153 		mb->next->prev = mb->prev;
154 	else
155 		_tail = mb->prev;
156 
157 	mb->id = 0;
158 
159 	/* stomp a different pattern across the memory */
160 	ptr = ((char *) mb) + sizeof(struct memblock);
161 	for (i = 0; i < mb->length; i++)
162 		*ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
163 
164 	assert(_mem_stats.blocks_allocated);
165 	_mem_stats.blocks_allocated--;
166 	_mem_stats.bytes -= mb->length;
167 
168 	/* free the memory */
169 	free(mb);
170 }
171 
172 void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
173 {
174 	void *r;
175 	struct memblock *mb = ((struct memblock *) p) - 1;
176 
177 	r = dm_malloc_aux_debug(s, file, line);
178 
179 	if (p) {
180 		memcpy(r, p, mb->length);
181 		dm_free_aux(p);
182 	}
183 
184 	return r;
185 }
186 
187 int dm_dump_memory_debug(void)
188 {
189 	unsigned long tot = 0;
190 	struct memblock *mb;
191 	char str[32];
192 	size_t c;
193 
194 	if (_head)
195 		log_very_verbose("You have a memory leak:");
196 
197 	for (mb = _head; mb; mb = mb->next) {
198 		for (c = 0; c < sizeof(str) - 1; c++) {
199 			if (c >= mb->length)
200 				str[c] = ' ';
201 			else if (*(char *)(mb->magic + c) == '\0')
202 				str[c] = '\0';
203 			else if (*(char *)(mb->magic + c) < ' ')
204 				str[c] = '?';
205 			else
206 				str[c] = *(char *)(mb->magic + c);
207 		}
208 		str[sizeof(str) - 1] = '\0';
209 
210 		LOG_MESG(_LOG_INFO, mb->file, mb->line, 0,
211 			 "block %d at %p, size %" PRIsize_t "\t [%s]",
212 			 mb->id, mb->magic, mb->length, str);
213 		tot += mb->length;
214 	}
215 
216 	if (_head)
217 		log_very_verbose("%ld bytes leaked in total", tot);
218 
219 	return 1;
220 }
221 
222 void dm_bounds_check_debug(void)
223 {
224 	struct memblock *mb = _head;
225 	while (mb) {
226 		size_t i;
227 		char *ptr = ((char *) (mb + 1)) + mb->length;
228 		for (i = 0; i < sizeof(unsigned long); i++)
229 			if (*ptr++ != (char) mb->id)
230 				assert(!"Memory smash");
231 
232 		mb = mb->next;
233 	}
234 }
235 
236 void *dm_malloc_aux(size_t s, const char *file __attribute((unused)),
237 		    int line __attribute((unused)))
238 {
239 	if (s > 50000000) {
240 		log_error("Huge memory allocation (size %" PRIsize_t
241 			  ") rejected - metadata corruption?", s);
242 		return 0;
243 	}
244 
245 	return malloc(s);
246 }
247