Annotation of sys/kern/kern_malloc_debug.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: kern_malloc_debug.c,v 1.26 2007/04/13 18:57:49 art Exp $ */
2:
3: /*
4: * Copyright (c) 1999, 2000 Artur Grabowski <art@openbsd.org>
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: *
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. The name of the author may not be used to endorse or promote products
14: * derived from this software without specific prior written permission.
15: *
16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26: */
27:
28: /*
29: * This really belongs in kern/kern_malloc.c, but it was too much pollution.
30: */
31:
32: /*
33: * It's only possible to debug one type/size at a time. The question is
34: * if this is a limitation or a feature. We never want to run this as the
35: * default malloc because we'll run out of memory really fast. Adding
36: * more types will also add to the complexity of the code.
37: *
38: * This is really simple. Every malloc() allocates two virtual pages,
39: * the second page is left unmapped, and the value returned is aligned
40: * so that it ends at (or very close to) the page boundary to catch overflows.
41: * Every free() changes the protection of the first page to VM_PROT_NONE so
42: * that we can catch any dangling writes to it.
43: * To minimize the risk of writes to recycled chunks we keep an LRU of latest
44: * freed chunks. The length of it is controlled by MALLOC_DEBUG_CHUNKS.
45: *
46: * Don't expect any performance.
47: *
48: * TODO:
49: * - support for size >= PAGE_SIZE
50: * - add support to the fault handler to give better diagnostics if we fail.
51: */
52:
53: #include <sys/param.h>
54: #include <sys/proc.h>
55: #include <sys/kernel.h>
56: #include <sys/malloc.h>
57: #include <sys/systm.h>
58: #include <sys/pool.h>
59:
60: #include <uvm/uvm.h>
61:
62: /*
63: * debug_malloc_type and debug_malloc_size define the type and size of
64: * memory to be debugged. Use 0 for a wildcard. debug_malloc_size_lo
65: * is the lower limit and debug_malloc_size_hi the upper limit of sizes
66: * being debugged; 0 will not work as a wildcard for the upper limit.
67: * For any debugging to take place, type must be != -1, size must be >= 0,
68: * and if the limits are being used, size must be set to 0.
69: * See /usr/src/sys/sys/malloc.h and malloc(9) for a list of types.
70: *
71: * Although those are variables, it's a really bad idea to change the type
72: * if any memory chunks of this type are used. It's ok to change the size
73: * in runtime.
74: */
75: int debug_malloc_type = -1;
76: int debug_malloc_size = -1;
77: int debug_malloc_size_lo = -1;
78: int debug_malloc_size_hi = -1;
79:
80: /*
81: * MALLOC_DEBUG_CHUNKS is the number of memory chunks we require on the
82: * freelist before we reuse them.
83: */
84: #define MALLOC_DEBUG_CHUNKS 16
85:
86: void debug_malloc_allocate_free(int);
87:
88: struct debug_malloc_entry {
89: TAILQ_ENTRY(debug_malloc_entry) md_list;
90: vaddr_t md_va;
91: paddr_t md_pa;
92: size_t md_size;
93: int md_type;
94: };
95:
96: TAILQ_HEAD(,debug_malloc_entry) debug_malloc_freelist;
97: TAILQ_HEAD(,debug_malloc_entry) debug_malloc_usedlist;
98:
99: int debug_malloc_allocs;
100: int debug_malloc_frees;
101: int debug_malloc_pages;
102: int debug_malloc_chunks_on_freelist;
103:
104: int debug_malloc_initialized;
105:
106: struct pool debug_malloc_pool;
107:
108: int
109: debug_malloc(unsigned long size, int type, int flags, void **addr)
110: {
111: struct debug_malloc_entry *md = NULL;
112: int s, wait = (flags & M_NOWAIT) == 0;
113:
114: /* Careful not to compare unsigned long to int -1 */
115: if (((type != debug_malloc_type && debug_malloc_type != 0) ||
116: (size != debug_malloc_size && debug_malloc_size != 0) ||
117: (debug_malloc_size_lo != -1 && size < debug_malloc_size_lo) ||
118: (debug_malloc_size_hi != -1 && size > debug_malloc_size_hi) ||
119: !debug_malloc_initialized) && type != M_DEBUG)
120: return (0);
121:
122: /* XXX - fix later */
123: if (size > PAGE_SIZE)
124: return (0);
125:
126: s = splvm();
127: if (debug_malloc_chunks_on_freelist < MALLOC_DEBUG_CHUNKS)
128: debug_malloc_allocate_free(wait);
129:
130: md = TAILQ_FIRST(&debug_malloc_freelist);
131: if (md == NULL) {
132: splx(s);
133: return (0);
134: }
135: TAILQ_REMOVE(&debug_malloc_freelist, md, md_list);
136: debug_malloc_chunks_on_freelist--;
137:
138: TAILQ_INSERT_HEAD(&debug_malloc_usedlist, md, md_list);
139: debug_malloc_allocs++;
140: splx(s);
141:
142: pmap_kenter_pa(md->md_va, md->md_pa, VM_PROT_READ|VM_PROT_WRITE);
143: pmap_update(pmap_kernel());
144:
145: md->md_size = size;
146: md->md_type = type;
147:
148: /*
149: * Align the returned addr so that it ends where the first page
150: * ends. roundup to get decent alignment.
151: */
152: *addr = (void *)(md->md_va + PAGE_SIZE - roundup(size, sizeof(long)));
153: return (1);
154: }
155:
156: int
157: debug_free(void *addr, int type)
158: {
159: struct debug_malloc_entry *md;
160: vaddr_t va;
161: int s;
162:
163: if (type != debug_malloc_type && debug_malloc_type != 0 &&
164: type != M_DEBUG)
165: return (0);
166:
167: /*
168: * trunc_page to get the address of the page.
169: */
170: va = trunc_page((vaddr_t)addr);
171:
172: s = splvm();
173: TAILQ_FOREACH(md, &debug_malloc_usedlist, md_list)
174: if (md->md_va == va)
175: break;
176:
177: /*
178: * If we are not responsible for this entry, let the normal free
179: * handle it
180: */
181: if (md == NULL) {
182: /*
183: * sanity check. Check for multiple frees.
184: */
185: TAILQ_FOREACH(md, &debug_malloc_freelist, md_list)
186: if (md->md_va == va)
187: panic("debug_free: already free");
188: splx(s);
189: return (0);
190: }
191:
192: debug_malloc_frees++;
193: TAILQ_REMOVE(&debug_malloc_usedlist, md, md_list);
194:
195: TAILQ_INSERT_TAIL(&debug_malloc_freelist, md, md_list);
196: debug_malloc_chunks_on_freelist++;
197: /*
198: * unmap the page.
199: */
200: pmap_kremove(md->md_va, PAGE_SIZE);
201: pmap_update(pmap_kernel());
202: splx(s);
203:
204: return (1);
205: }
206:
207: void
208: debug_malloc_init(void)
209: {
210:
211: TAILQ_INIT(&debug_malloc_freelist);
212: TAILQ_INIT(&debug_malloc_usedlist);
213:
214: debug_malloc_allocs = 0;
215: debug_malloc_frees = 0;
216: debug_malloc_pages = 0;
217: debug_malloc_chunks_on_freelist = 0;
218:
219: pool_init(&debug_malloc_pool, sizeof(struct debug_malloc_entry),
220: 0, 0, 0, "mdbepl", NULL);
221:
222: debug_malloc_initialized = 1;
223: }
224:
225: /*
226: * Add one chunk to the freelist.
227: *
228: * called at splvm.
229: */
230: void
231: debug_malloc_allocate_free(int wait)
232: {
233: vaddr_t va, offset;
234: struct vm_page *pg;
235: struct debug_malloc_entry *md;
236:
237: splassert(IPL_VM);
238:
239: md = pool_get(&debug_malloc_pool, wait ? PR_WAITOK : PR_NOWAIT);
240: if (md == NULL)
241: return;
242:
243: va = uvm_km_kmemalloc(kmem_map, NULL, PAGE_SIZE * 2,
244: UVM_KMF_VALLOC | (wait ? 0: UVM_KMF_NOWAIT));
245: if (va == 0) {
246: pool_put(&debug_malloc_pool, md);
247: return;
248: }
249:
250: offset = va - vm_map_min(kernel_map);
251: for (;;) {
252: pg = uvm_pagealloc(NULL, 0, NULL, 0);
253: if (pg) {
254: atomic_clearbits_int(&pg->pg_flags, PG_BUSY);
255: UVM_PAGE_OWN(pg, NULL);
256: }
257:
258: if (pg)
259: break;
260:
261: if (wait == 0) {
262: uvm_unmap(kmem_map, va, va + PAGE_SIZE * 2);
263: pool_put(&debug_malloc_pool, md);
264: return;
265: }
266: uvm_wait("debug_malloc");
267: }
268:
269: md->md_va = va;
270: md->md_pa = VM_PAGE_TO_PHYS(pg);
271:
272: debug_malloc_pages++;
273: TAILQ_INSERT_HEAD(&debug_malloc_freelist, md, md_list);
274: debug_malloc_chunks_on_freelist++;
275: }
276:
277: void
278: debug_malloc_print(void)
279: {
280:
281: debug_malloc_printit(printf, NULL);
282: }
283:
284: void
285: debug_malloc_assert_allocated(void *addr, const char *func)
286: {
287: struct debug_malloc_entry *md;
288: vaddr_t va = (vaddr_t)addr;
289:
290: TAILQ_FOREACH(md, &debug_malloc_freelist, md_list) {
291: if (va >= md->md_va &&
292: va < md->md_va + 2 * PAGE_SIZE)
293: panic("debug_malloc: (%s): %p - freed", func, addr);
294: }
295: TAILQ_FOREACH(md, &debug_malloc_usedlist, md_list) {
296: if (va >= md->md_va + PAGE_SIZE &&
297: va < md->md_va + 2 * PAGE_SIZE)
298: panic("debug_malloc: (%s): %p - overflow", func, addr);
299: }
300: }
301:
302: void
303: debug_malloc_printit(int (*pr)(const char *, ...), vaddr_t addr)
304: {
305: struct debug_malloc_entry *md;
306:
307: if (addr) {
308: TAILQ_FOREACH(md, &debug_malloc_freelist, md_list) {
309: if (addr >= md->md_va &&
310: addr < md->md_va + 2 * PAGE_SIZE) {
311: (*pr)("Memory at address 0x%x is in a freed "
312: "area. type %d, size: %d\n ",
313: addr, md->md_type, md->md_size);
314: return;
315: }
316: }
317: TAILQ_FOREACH(md, &debug_malloc_usedlist, md_list) {
318: if (addr >= md->md_va + PAGE_SIZE &&
319: addr < md->md_va + 2 * PAGE_SIZE) {
320: (*pr)("Memory at address 0x%x is just outside "
321: "an allocated area. type %d, size: %d\n",
322: addr, md->md_type, md->md_size);
323: return;
324: }
325: }
326: (*pr)("Memory at address 0x%x is outside debugged malloc.\n");
327: return;
328: }
329:
330: (*pr)("allocs: %d\n", debug_malloc_allocs);
331: (*pr)("frees: %d\n", debug_malloc_frees);
332: (*pr)("pages used: %d\n", debug_malloc_pages);
333: (*pr)("chunks on freelist: %d\n", debug_malloc_chunks_on_freelist);
334:
335: (*pr)("\taddr:\tsize:\n");
336: (*pr)("free chunks:\n");
337: TAILQ_FOREACH(md, &debug_malloc_freelist, md_list)
338: (*pr)("\t0x%x\t0x%x\t%d\n", md->md_va, md->md_size,
339: md->md_type);
340: (*pr)("used chunks:\n");
341: TAILQ_FOREACH(md, &debug_malloc_usedlist, md_list)
342: (*pr)("\t0x%x\t0x%x\t%d\n", md->md_va, md->md_size,
343: md->md_type);
344: }
CVSweb