From: Peter Dinda Date: Mon, 27 May 2013 22:32:22 +0000 (-0500) Subject: Memory allocation checking framework X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=4a63fd4cef38da57f4b44d8ac88737faa0a12846 Memory allocation checking framework --- diff --git a/Kconfig b/Kconfig index 4f4a3d5..cfbabb2 100644 --- a/Kconfig +++ b/Kconfig @@ -437,6 +437,13 @@ config DEBUG_LOCKS help This turns on lock debugging for locks in Palacios and in its host-specifc glue code. This requires host support. +config DEBUG_MEM_ALLOC + bool "Memory allocation debugging (if host supports it)" + default n + depends on DEBUG_ON + help + This turns on memory allocation debugging in Palacios, using the mechanisms provided by the host + endmenu diff --git a/linux_module/Makefile b/linux_module/Makefile index 64ded27..99823e1 100644 --- a/linux_module/Makefile +++ b/linux_module/Makefile @@ -50,6 +50,7 @@ v3vee-$(V3_CONFIG_EXT_GUARD_MODULES) += iface-guard-mods.o v3vee-$(V3_CONFIG_HOST_PCI) += iface-host-pci.o v3vee-$(V3_CONFIG_DEBUG_LOCKS) += lockcheck.o +v3vee-$(V3_CONFIG_DEBUG_MEM_ALLOC) += memcheck.o v3vee-objs := $(v3vee-y) ../libv3vee.a diff --git a/linux_module/main.c b/linux_module/main.c index aaae316..ab6d9f2 100644 --- a/linux_module/main.c +++ b/linux_module/main.c @@ -27,6 +27,7 @@ #include "mm.h" #include "vm.h" #include "allow_devmem.h" +#include "memcheck.h" #include "lockcheck.h" #include "linux-exts.h" @@ -347,6 +348,7 @@ static int __init v3_init(void) { int ret = 0; LOCKCHECK_INIT(); + MEMCHECK_INIT(); palacios_init_mm(); @@ -490,6 +492,7 @@ static void __exit v3_exit(void) { DEBUG("Palacios Module Mallocs = %d, Frees = %d\n", mod_allocs, mod_frees); + MEMCHECK_DEINIT(); LOCKCHECK_DEINIT(); } diff --git a/linux_module/memcheck.c b/linux_module/memcheck.c new file mode 100644 index 0000000..2ab86b2 --- /dev/null +++ b/linux_module/memcheck.c @@ -0,0 +1,278 @@ +#include +#include +#include + +#include "palacios.h" + +#include "memcheck.h" + +// Whether not all allocations and frees will be printed +#define SHOW_ALL_ALLOCS 0 + +// Whether or now allocations outside of a size range will be printed +#define SHOW_THRESHOLD_WARNINGS 1 +#define SMALL_KMALLOC_THRESHOLD 9 +#define BIG_KMALLOC_THRESHOLD ((1024*16)-1) +#define SMALL_VMALLOC_THRESHOLD 9 +#define BIG_VMALLOC_THRESHOLD ((1024*1024)-1) +#define SMALL_PAGE_ALLOC_THRESHOLD (4096-1) +#define BIG_PAGE_ALLOC_THRESHOLD (4096*8-1) + +// How far up the stack to track the caller +// 0 => palacios_... +// 1 => v3_alloc... +// 2 => caller of v3_alloc.. +// ... +#define STEP_BACK_DEPTH_FIRST 1 +#define STEP_BACK_DEPTH_LAST 4 +#define STEP_BACK_DEPTH (STEP_BACK_DEPTH_LAST-STEP_BACK_DEPTH_FIRST+1) + + +typedef struct { + int inuse; // 1=inuse + palacios_memcheck_memtype_t type; + // PALACIOS_KMALLOC,VMALLOC,PAGE_ALLOC + void *addr; // the allocated block's address + unsigned long size; // the allocated block's size + void *allocator[STEP_BACK_DEPTH]; + // who allocated this +} memcheck_state_t; + + +// This lock is currently used only to control +// allocation of entries in the global state +static spinlock_t lock; +static memcheck_state_t state[NUM_ALLOCS]; + +static void printmem(char *prefix, memcheck_state_t *m); + + +static memcheck_state_t *get_mem_entry(void) +{ + int i; + unsigned long f; + memcheck_state_t *m; + + palacios_spinlock_lock_irqsave(&lock,f); + + for (i=0;iinuse)) { + m->inuse=1; + break; + } + } + + palacios_spinlock_unlock_irqrestore(&lock,f); + + if (iinuse && m->addr == addr && m->type==type) { + if (size) { + if (m->size==size) { + return m; + } else { + return 0; + } + } else { + return m; + } + } + } + return 0; +} + + +static void free_mem_entry(memcheck_state_t *m) +{ + m->inuse=0; +} + + +void palacios_memcheck_init() +{ + memset(state,0,sizeof(memcheck_state_t)*NUM_ALLOCS); + palacios_spinlock_init(&lock); + DEBUG("MEMCHECK: MEMORY CHECKING INITED\n"); +} + +// +// This needs to be defined explictly since the intrinsic does not take a var +// +#define backtrace(t) \ + t[0]=__builtin_return_address(STEP_BACK_DEPTH_FIRST); \ + t[1]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+1); \ + t[2]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+2); \ + t[3]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+3); + +// +// For printing a backtrace +// +// +#define backtrace_format "%pS << %pS << %pS << %pS" +#define backtrace_expand(t) ((t)[0]),((t)[1]),((t)[2]),((t)[3]) + + +#if 0 +static void clear_trace(void **trace) +{ + int i; + + for (i=0;iaddr) ) { + DEBUG("MEMCHECK: %s: memory state 0x%p BOGUS\n",prefix,m); + return; + } + if (m->addr) { + DEBUG("MEMCHECK: %s: %s memory at 0x%p for %lu bytes allocator=" + backtrace_format + "\n", + prefix, + TYPE_TO_STR(m->type), + m->addr, + m->size, + backtrace_expand(m->allocator)); + } +} + + +void palacios_memcheck_deinit() +{ + int i; + memcheck_state_t *m; + + for (i=0;iinuse) { + printmem("ALLOCATED MEMORY AT DEINIT",m); + } + } + INFO("MEMCHECK: DEINITED\n"); + + // Note that this function could garbage collect at this + // point if we desired +} + + +void threshold(memcheck_state_t *m) +{ +#if SHOW_THRESHOLD_WARNINGS + switch (m->type) { + case PALACIOS_KMALLOC: + if (m->size < SMALL_KMALLOC_THRESHOLD || + m->size > BIG_KMALLOC_THRESHOLD) { + DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n", + SMALL_KMALLOC_THRESHOLD, BIG_KMALLOC_THRESHOLD); + printmem("ALLOCATION EXCEEDS",m); + } + break; + case PALACIOS_VMALLOC: + if (m->size < SMALL_VMALLOC_THRESHOLD || + m->size > BIG_VMALLOC_THRESHOLD) { + DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n", + SMALL_VMALLOC_THRESHOLD, BIG_VMALLOC_THRESHOLD); + printmem("ALLOCATION EXCEEDS",m); + } + break; + case PALACIOS_PAGE_ALLOC: + if (m->size < SMALL_PAGE_ALLOC_THRESHOLD || + m->size > BIG_PAGE_ALLOC_THRESHOLD) { + DEBUG("MEMCHECK: ALLOCATION EXCEEDS THRESHOLDS of %u and %u\n", + SMALL_PAGE_ALLOC_THRESHOLD, BIG_PAGE_ALLOC_THRESHOLD); + printmem("ALLOCATION EXCEEDS",m); + } + break; + default: + break; + } +#endif +} + +void find_overlaps(memcheck_state_t *m) +{ + int i; + for (i=0;iinuse && s!=m && s->type==m->type) { + if (((m->addr >= s->addr) && (m->addr < (s->addr+s->size))) || + (((m->addr+m->size-1) >= s->addr) && ((m->addr+m->size-1) < (s->addr+s->size))) || + ((m->addr < s->addr) && (m->addr+m->size-1)>=(s->addr+s->size))) { + DEBUG("MEMCHECK: OVERLAP DETECTED\n"); + printmem("OVERLAP (0)",s); + printmem("OVERLAP (1)",s); + } + } + } +} + +void palacios_memcheck_alloc(void *addr, unsigned long size, palacios_memcheck_memtype_t type) +{ + memcheck_state_t *m=get_mem_entry(); + + if (!m) { + DEBUG("MEMCHECK: UNABLE TO ALLOCATE TRACKING DATA FOR %s ALLOC AT 0x%p FOR %lu BYTES\n", + TYPE_TO_STR(type),addr,size); + return; + } + m->type=type; + m->addr=addr; + m->size=size; + backtrace(m->allocator); + +#if SHOW_ALL_ALLOCS + printmem("ALLOCATE", m); +#endif + + threshold(m); + find_overlaps(m); +} + +void palacios_memcheck_free(void *addr,unsigned long size, palacios_memcheck_memtype_t type) +{ + memcheck_state_t *m=find_mem_entry(addr,0,type); // don't care about the size now + + if (!m){ + DEBUG("MEMCHECK: FREEING UNTRACKED %s MEMORY AT 0x%p FOR %lu BYTES\n",TYPE_TO_STR(type),addr,size); + return; + } + + if (m->type==PALACIOS_PAGE_ALLOC) { + // need to verify sizes are identical + if (size!=m->size) { + DEBUG("MEMCHECK: FREEING %s MEMORY AT 0x%p FOR %lu bytes, BUT MATCHING ENTRY HAS %lu BYTES\n",TYPE_TO_STR(type),addr,size,m->size); + printmem("MATCHING ENTRY",m); + } + } + +#if SHOW_ALL_ALLOCS + printmem("FREE",m); +#endif + + free_mem_entry(m); +} + diff --git a/linux_module/memcheck.h b/linux_module/memcheck.h new file mode 100644 index 0000000..a5e0158 --- /dev/null +++ b/linux_module/memcheck.h @@ -0,0 +1,52 @@ +#ifndef _memcheck +#define _memcheck + +typedef enum { + PALACIOS_KMALLOC, + PALACIOS_VMALLOC, + PALACIOS_PAGE_ALLOC +} palacios_memcheck_memtype_t; + +#ifdef V3_CONFIG_DEBUG_MEM_ALLOC + +// Maxmimum number of simultaneous allocations to handle +#define NUM_ALLOCS 8192 + +// +// The following macros are used +// in the stub functions to call back to the memory +// checker - if memory allocation hecking is not enabled, these +// turn into nothing +// +#define MEMCHECK_INIT() palacios_memcheck_init() +#define MEMCHECK_KMALLOC(ptr,size) palacios_memcheck_alloc(ptr,size,PALACIOS_KMALLOC) +#define MEMCHECK_KFREE(ptr) palacios_memcheck_free(ptr,0,PALACIOS_KMALLOC) +#define MEMCHECK_VMALLOC(ptr,size) palacios_memcheck_alloc(ptr,size,PALACIOS_VMALLOC) +#define MEMCHECK_VFREE(ptr) palacios_memcheck_free(ptr,0,PALACIOS_VMALLOC) +#define MEMCHECK_ALLOC_PAGES(ptr,size) palacios_memcheck_alloc(ptr,size,PALACIOS_PAGE_ALLOC) +#define MEMCHECK_FREE_PAGES(ptr,size) palacios_memcheck_free(ptr,size,PALACIOS_PAGE_ALLOC) +#define MEMCHECK_DEINIT() palacios_memcheck_deinit() + +void palacios_memcheck_init(void); +void palacios_memcheck_alloc(void *ptr, unsigned long size, palacios_memcheck_memtype_t type); +void palacios_memcheck_free(void *ptr, unsigned long size, palacios_memcheck_memtype_t type); +void palacios_memcheck_deinit(void); + +#else + +// +// The following is what happens when lock checking is not on +// +#define MEMCHECK_INIT() +#define MEMCHECK_KMALLOC(ptr,size) +#define MEMCHECK_KFREE(ptr) +#define MEMCHECK_VMALLOC(ptr,size) +#define MEMCHECK_VFREE(ptr) +#define MEMCHECK_ALLOC_PAGES(ptr,size) +#define MEMCHECK_FREE_PAGES(ptr,size) +#define MEMCHECK_DEINIT() + +#endif + + +#endif diff --git a/linux_module/palacios-stubs.c b/linux_module/palacios-stubs.c index 7f02fe7..72604d3 100644 --- a/linux_module/palacios-stubs.c +++ b/linux_module/palacios-stubs.c @@ -22,6 +22,7 @@ #include "mm.h" +#include "memcheck.h" #include "lockcheck.h" // The following can be used to track heap bugs @@ -176,6 +177,8 @@ void *palacios_allocate_pages(int num_pages, unsigned int alignment) { pg_allocs += num_pages; + MEMCHECK_ALLOC_PAGES(pg_addr,num_pages*4096); + return pg_addr; } @@ -189,6 +192,8 @@ void *palacios_allocate_pages(int num_pages, unsigned int alignment) { void palacios_free_pages(void * page_paddr, int num_pages) { pg_frees += num_pages; free_palacios_pgs((uintptr_t)page_paddr, num_pages); + MEMCHECK_FREE_PAGES(page_paddr,num_pages*4096); + } @@ -209,6 +214,8 @@ palacios_alloc_extended(unsigned int size, unsigned int flags) { memset(addr,0,size+2*ALLOC_PAD); #endif + MEMCHECK_KMALLOC(addr+ALLOC_PAD,size+2*ALLOC_PAD); + return addr+ALLOC_PAD; } @@ -226,6 +233,8 @@ palacios_valloc(unsigned int size) vmallocs++; + MEMCHECK_VMALLOC(addr,size); + return addr; } @@ -233,6 +242,7 @@ void palacios_vfree(void *p) { vfree(p); vfrees++; + MEMCHECK_VFREE(p); } /** @@ -264,7 +274,7 @@ palacios_free( { frees++; kfree(addr-ALLOC_PAD); - return; + MEMCHECK_KFREE(addr-ALLOC_PAD); } /**