[BACK]Return to kern_malloc_debug.c CVS log [TXT][DIR] Up to [local] / sys / kern

Annotation of sys/kern/kern_malloc_debug.c, Revision 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