Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Memory allocation checking framework
Peter Dinda [Mon, 27 May 2013 22:32:22 +0000 (17:32 -0500)]
Kconfig
linux_module/Makefile
linux_module/main.c
linux_module/memcheck.c [new file with mode: 0644]
linux_module/memcheck.h [new file with mode: 0644]
linux_module/palacios-stubs.c

diff --git a/Kconfig b/Kconfig
index 4f4a3d5..cfbabb2 100644 (file)
--- 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
 
index 64ded27..99823e1 100644 (file)
@@ -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
index aaae316..ab6d9f2 100644 (file)
@@ -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 (file)
index 0000000..2ab86b2
--- /dev/null
@@ -0,0 +1,278 @@
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/spinlock.h>
+
+#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;i<NUM_ALLOCS;i++) { 
+    m=&(state[i]);
+    if (!(m->inuse)) { 
+      m->inuse=1;
+      break;
+    }
+  }
+
+  palacios_spinlock_unlock_irqrestore(&lock,f);
+  
+  if (i<NUM_ALLOCS) { 
+    return m;
+  } else {
+    return 0;
+  }
+}
+
+
+static memcheck_state_t *find_mem_entry(void *addr, unsigned long size, palacios_memcheck_memtype_t type)
+{
+  int i;
+  memcheck_state_t *m;
+
+  for (i=0;i<NUM_ALLOCS;i++) { 
+    m=&(state[i]);
+    if (m->inuse && 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;i<STEP_BACK_DEPTH;i++) { 
+    trace[i]=0;
+  }
+}
+#endif
+
+#define TYPE_TO_STR(type) ((type)==PALACIOS_KMALLOC ? "kmalloc" : \
+                           (type)==PALACIOS_VMALLOC ? "vmalloc" : \
+                          (type)==PALACIOS_PAGE_ALLOC ? "pagealloc" : "UNKNOWN")
+
+
+static void printmem(char *prefix, memcheck_state_t *m)
+{
+  if (!m || !(m->addr) ) { 
+    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;i<NUM_ALLOCS;i++) { 
+    m=&(state[i]);
+    if (m->inuse) { 
+      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;i<NUM_ALLOCS;i++) {
+    memcheck_state_t *s = &(state[i]);
+    if (s->inuse && 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 (file)
index 0000000..a5e0158
--- /dev/null
@@ -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
index 7f02fe7..72604d3 100644 (file)
@@ -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);
 }
 
 /**